From 0d27c9ea8b7e8d58f7fed2fffeaea0380c9ed782 Mon Sep 17 00:00:00 2001
From: moodler Insert_ID error Affected_Rows error Error=".$this->ErrorNo().' ';
- $first = true;
- foreach($fieldArray as $k => $v) {
- if ($has_autoinc && in_array($k,$keyCol)) continue; // skip autoinc col
-
- if ($first) {
- $first = false;
- $iCols = "$k";
- $iVals = "$v";
- } else {
- $iCols .= ",$k";
- $iVals .= ",$v";
- }
- }
- $insert = "INSERT INTO $table ($iCols) VALUES ($iVals)";
- $rs = $this->Execute($insert);
- return ($rs) ? 2 : 0;
- }
-
-
- /**
- * Will select, getting rows from $offset (1-based), for $nrows.
- * This simulates the MySQL "select * from table limit $offset,$nrows" , and
- * the PostgreSQL "select * from table limit $nrows offset $offset". Note that
- * MySQL and PostgreSQL parameter ordering is the opposite of the other.
- * eg.
- * CacheSelectLimit(15,'select * from table',3); will return rows 1 to 3 (1-based)
- * CacheSelectLimit(15,'select * from table',3,2); will return rows 3 to 5 (1-based)
- *
- * BUG: Currently CacheSelectLimit fails with $sql with LIMIT or TOP clause already set
- *
- * @param [secs2cache] seconds to cache data, set to 0 to force query. This is optional
- * @param sql
- * @param [offset] is the row to start calculations from (1-based)
- * @param [nrows] is the number of rows to get
- * @param [inputarr] array of bind variables
- * @param [arg3] is a private parameter only used by jlim
- * @return the recordset ($rs->databaseType == 'array')
- */
- function &CacheSelectLimit($secs2cache,$sql,$nrows=-1,$offset=-1,$inputarr=false, $arg3=false)
- {
- if (!is_numeric($secs2cache)) {
- if ($sql === false) $sql = -1;
- if ($offset == -1) $offset = false;
- // sql, nrows, offset,inputarr,arg3
- return $this->SelectLimit($secs2cache,$sql,$nrows,$offset,$inputarr,$this->cacheSecs);
- } else {
- if ($sql === false) ADOConnection::outp( "Warning: \$sql missing from CacheSelectLimit()");
- return $this->SelectLimit($sql,$nrows,$offset,$inputarr,$arg3,$secs2cache);
- }
- }
-
- /**
- * Flush cached recordsets that match a particular $sql statement.
- * If $sql == false, then we purge all files in the cache.
- */
- function CacheFlush($sql=false,$inputarr=false)
- {
- global $ADODB_CACHE_DIR;
-
- if (strlen($ADODB_CACHE_DIR) > 1 && !$sql) {
- if (strpos(strtoupper(PHP_OS),'WIN') !== false) {
- $cmd = 'del /s '.str_replace('/','\\',$ADODB_CACHE_DIR).'\adodb_*.cache';
- } else {
- $cmd = 'rm -rf '.$ADODB_CACHE_DIR.'/??/adodb_*.cache';
- // old version 'rm -f `find '.$ADODB_CACHE_DIR.' -name adodb_*.cache`';
- }
- if ($this->debug) {
- ADOConnection::outp( "CacheFlush: $cmd Insert_ID error Affected_Rows error Error=".$this->ErrorNo().' '; */
+ $first = true;
+ foreach($fieldArray as $k => $v) {
+ if ($has_autoinc && in_array($k,$keyCol)) continue; /* skip autoinc col */
+
+ if ($first) {
+ $first = false;
+ $iCols = "$k";
+ $iVals = "$v";
+ } else {
+ $iCols .= ",$k";
+ $iVals .= ",$v";
+ }
+ }
+ $insert = "INSERT INTO $table ($iCols) VALUES ($iVals)";
+ $rs = $this->Execute($insert);
+ return ($rs) ? 2 : 0;
+ }
+
+
+ /**
+ * Will select, getting rows from $offset (1-based), for $nrows.
+ * This simulates the MySQL "select * from table limit $offset,$nrows" , and
+ * the PostgreSQL "select * from table limit $nrows offset $offset". Note that
+ * MySQL and PostgreSQL parameter ordering is the opposite of the other.
+ * eg.
+ * CacheSelectLimit(15,'select * from table',3); will return rows 1 to 3 (1-based)
+ * CacheSelectLimit(15,'select * from table',3,2); will return rows 3 to 5 (1-based)
+ *
+ * BUG: Currently CacheSelectLimit fails with $sql with LIMIT or TOP clause already set
+ *
+ * @param [secs2cache] seconds to cache data, set to 0 to force query. This is optional
+ * @param sql
+ * @param [offset] is the row to start calculations from (1-based)
+ * @param [nrows] is the number of rows to get
+ * @param [inputarr] array of bind variables
+ * @param [arg3] is a private parameter only used by jlim
+ * @return the recordset ($rs->databaseType == 'array')
+ */
+ function &CacheSelectLimit($secs2cache,$sql,$nrows=-1,$offset=-1,$inputarr=false, $arg3=false)
+ {
+ if (!is_numeric($secs2cache)) {
+ if ($sql === false) $sql = -1;
+ if ($offset == -1) $offset = false;
+ /* sql, nrows, offset,inputarr,arg3 */
+ return $this->SelectLimit($secs2cache,$sql,$nrows,$offset,$inputarr,$this->cacheSecs);
+ } else {
+ if ($sql === false) ADOConnection::outp( "Warning: \$sql missing from CacheSelectLimit()");
+ return $this->SelectLimit($sql,$nrows,$offset,$inputarr,$arg3,$secs2cache);
+ }
+ }
+
+ /**
+ * Flush cached recordsets that match a particular $sql statement.
+ * If $sql == false, then we purge all files in the cache.
+ */
+ function CacheFlush($sql=false,$inputarr=false)
+ {
+ global $ADODB_CACHE_DIR;
+
+ if (strlen($ADODB_CACHE_DIR) > 1 && !$sql) {
+ if (strpos(strtoupper(PHP_OS),'WIN') !== false) {
+ $cmd = 'del /s '.str_replace('/','\\',$ADODB_CACHE_DIR).'\adodb_*.cache';
+ } else {
+ $cmd = 'rm -rf '.$ADODB_CACHE_DIR.'/??/adodb_*.cache';
+ /* old version 'rm -f `find '.$ADODB_CACHE_DIR.' -name adodb_*.cache`'; */
+ }
+ if ($this->debug) {
+ ADOConnection::outp( "CacheFlush: $cmd \$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']} Session Replace: '.$ADODB_SESS_CONN->ErrorMsg().' \$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']} \$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']} Session Replace: '.$ADODB_SESS_CONN->ErrorMsg().' \$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}
\n";
-
- if (isset($HTTP_SERVER_VARS['HTTP_USER_AGENT'])) echo $msg;
- else echo strip_tags($msg);
- flush();
- }
-
- /**
- * Connect to database
- *
- * @param [argHostname] Host to connect to
- * @param [argUsername] Userid to login
- * @param [argPassword] Associated password
- * @param [argDatabaseName] database
- * @param [forceNew] force new connection
- *
- * @return true or false
- */
- function Connect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "", $forceNew = false)
- {
- if ($argHostname != "") $this->host = $argHostname;
- if ($argUsername != "") $this->user = $argUsername;
- if ($argPassword != "") $this->password = $argPassword; // not stored for security reasons
- if ($argDatabaseName != "") $this->database = $argDatabaseName;
-
- $this->_isPersistentConnection = false;
- if ($fn = $this->raiseErrorFn) {
- if ($forceNew) {
- if ($this->_nconnect($this->host, $this->user, $this->password, $this->database)) return true;
- } else {
- if ($this->_connect($this->host, $this->user, $this->password, $this->database)) return true;
- }
- $err = $this->ErrorMsg();
- if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'";
- $fn($this->databaseType,'CONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this);
- } else {
- if ($forceNew) {
- if ($this->_nconnect($this->host, $this->user, $this->password, $this->database)) return true;
- } else {
- if ($this->_connect($this->host, $this->user, $this->password, $this->database)) return true;
- }
- }
- if ($this->debug) ADOConnection::outp( $this->host.': '.$this->ErrorMsg());
-
- return false;
- }
-
- function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName)
- {
- return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName);
- }
-
-
- /**
- * Always force a new connection to database - currently only works with oracle
- *
- * @param [argHostname] Host to connect to
- * @param [argUsername] Userid to login
- * @param [argPassword] Associated password
- * @param [argDatabaseName] database
- *
- * @return true or false
- */
- function NConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "")
- {
- return $this->Connect($argHostname, $argUsername, $argPassword, $argDatabaseName, true);
- }
-
- /**
- * Establish persistent connect to database
- *
- * @param [argHostname] Host to connect to
- * @param [argUsername] Userid to login
- * @param [argPassword] Associated password
- * @param [argDatabaseName] database
- *
- * @return return true or false
- */
- function PConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "")
- {
- if (defined('ADODB_NEVER_PERSIST'))
- return $this->Connect($argHostname,$argUsername,$argPassword,$argDatabaseName);
-
- if ($argHostname != "") $this->host = $argHostname;
- if ($argUsername != "") $this->user = $argUsername;
- if ($argPassword != "") $this->password = $argPassword;
- if ($argDatabaseName != "") $this->database = $argDatabaseName;
-
- $this->_isPersistentConnection = true;
-
- if ($fn = $this->raiseErrorFn) {
- if ($this->_pconnect($this->host, $this->user, $this->password, $this->database)) return true;
- $err = $this->ErrorMsg();
- if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'";
- $fn($this->databaseType,'PCONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this);
- } else
- if ($this->_pconnect($this->host, $this->user, $this->password, $this->database)) return true;
-
- if ($this->debug) ADOConnection::outp( $this->host.': '.$this->ErrorMsg());
-
- return false;
- }
-
- // Format date column in sql string given an input format that understands Y M D
- function SQLDate($fmt, $col=false)
- {
- if (!$col) $col = $this->sysDate;
- return $col; // child class implement
- }
-
- /**
- * Should prepare the sql statement and return the stmt resource.
- * For databases that do not support this, we return the $sql. To ensure
- * compatibility with databases that do not support prepare:
- *
- * $stmt = $db->Prepare("insert into table (id, name) values (?,?)");
- * $db->Execute($stmt,array(1,'Jill')) or die('insert failed');
- * $db->Execute($stmt,array(2,'Joe')) or die('insert failed');
- *
- * @param sql SQL to send to database
- *
- * @return return FALSE, or the prepared statement, or the original sql if
- * if the database does not support prepare.
- *
- */
- function Prepare($sql)
- {
- return $sql;
- }
-
- /**
- * Some databases, eg. mssql require a different function for preparing
- * stored procedures. So we cannot use Prepare().
- *
- * Should prepare the stored procedure and return the stmt resource.
- * For databases that do not support this, we return the $sql. To ensure
- * compatibility with databases that do not support prepare:
- *
- * @param sql SQL to send to database
- *
- * @return return FALSE, or the prepared statement, or the original sql if
- * if the database does not support prepare.
- *
- */
- function PrepareSP($sql)
- {
- return $this->Prepare($sql);
- }
-
- /**
- * PEAR DB Compat
- */
- function Quote($s)
- {
- return $this->qstr($s,false);
- }
-
-
- /**
- * PEAR DB Compat - do not use internally.
- */
- function ErrorNative()
- {
- return $this->ErrorNo();
- }
-
-
- /**
- * PEAR DB Compat - do not use internally.
- */
- function nextId($seq_name)
- {
- return $this->GenID($seq_name);
- }
-
- /**
- * Lock a row, will escalate and lock the table if row locking not supported
- * will normally free the lock at the end of the transaction
- *
- * @param $table name of table to lock
- * @param $where where clause to use, eg: "WHERE row=12". If left empty, will escalate to table lock
- */
- function RowLock($table,$where)
- {
- return false;
- }
-
- function CommitLock($table)
- {
- return $this->CommitTrans();
- }
-
- function RollbackLock($table)
- {
- return $this->RollbackTrans();
- }
-
- /**
- * PEAR DB Compat - do not use internally.
- *
- * The fetch modes for NUMERIC and ASSOC for PEAR DB and ADODB are identical
- * for easy porting :-)
- *
- * @param mode The fetchmode ADODB_FETCH_ASSOC or ADODB_FETCH_NUM
- * @returns The previous fetch mode
- */
- function SetFetchMode($mode)
- {
- $old = $this->fetchMode;
- $this->fetchMode = $mode;
-
- if ($old === false) {
- global $ADODB_FETCH_MODE;
- return $ADODB_FETCH_MODE;
- }
- return $old;
- }
-
-
- /**
- * PEAR DB Compat - do not use internally.
- */
- function &Query($sql, $inputarr=false)
- {
- $rs = &$this->Execute($sql, $inputarr);
- if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error();
- return $rs;
- }
-
-
- /**
- * PEAR DB Compat - do not use internally
- */
- function &LimitQuery($sql, $offset, $count)
- {
- $rs = &$this->SelectLimit($sql, $count, $offset); // swap
- if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error();
- return $rs;
- }
-
-
- /**
- * PEAR DB Compat - do not use internally
- */
- function Disconnect()
- {
- return $this->Close();
- }
-
- /*
- Usage in oracle
- $stmt = $db->Prepare('select * from table where id =:myid and group=:group');
- $db->Parameter($stmt,$id,'myid');
- $db->Parameter($stmt,$group,'group',64);
- $db->Execute();
-
- @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.
-
- */
- function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false)
- {
- return false;
- }
-
- /**
- Improved method of initiating a transaction. Used together with CompleteTrans().
- Advantages include:
-
- a. StartTrans/CompleteTrans is nestable, unlike BeginTrans/CommitTrans/RollbackTrans.
- Only the outermost block is treated as a transaction.
- b. CompleteTrans auto-detects SQL errors, and will rollback on errors, commit otherwise.
- c. All BeginTrans/CommitTrans/RollbackTrans inside a StartTrans/CompleteTrans block
- are disabled, making it backward compatible.
- */
- function StartTrans($errfn = 'ADODB_TransMonitor')
- {
-
- if ($this->transOff > 0) {
- $this->transOff += 1;
- return;
- }
-
- $this->_oldRaiseFn = $this->raiseErrorFn;
- $this->raiseErrorFn = $errfn;
- $this->_transOK = true;
-
- if ($this->debug && $this->transCnt > 0) ADOConnection::outp("Bad Transaction: StartTrans called within BeginTrans");
- $this->BeginTrans();
- $this->transOff = 1;
- }
-
- /**
- Used together with StartTrans() to end a transaction. Monitors connection
- for sql errors, and will commit or rollback as appropriate.
-
- @autoComplete if true, monitor sql errors and commit and rollback as appropriate,
- and if set to false force rollback even if no SQL error detected.
- @returns true on commit, false on rollback.
- */
- function CompleteTrans($autoComplete = true)
- {
- if ($this->transOff > 1) {
- $this->transOff -= 1;
- return true;
- }
- $this->raiseErrorFn = $this->_oldRaiseFn;
-
- $this->transOff = 0;
- if ($this->_transOK && $autoComplete) $this->CommitTrans();
- else $this->RollbackTrans();
-
- return $this->_transOK;
- }
-
- /*
- At the end of a StartTrans/CompleteTrans block, perform a rollback.
- */
- function FailTrans()
- {
- if ($this->debug && $this->transOff == 0) {
- ADOConnection::outp("FailTrans outside StartTrans/CompleteTrans");
- }
- $this->_transOK = false;
- }
- /**
- * 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.
- * @param [arg3] reserved for john lim for future use
- * @return RecordSet or false
- */
- function &Execute($sql,$inputarr=false,$arg3=false)
- {
- if ($this->fnExecute) {
- $fn = $this->fnExecute;
- $fn($this,$sql,$inputarr);
- }
- if (!$this->_bindInputArray && $inputarr) {
- $sqlarr = explode('?',$sql);
- $sql = '';
- $i = 0;
- foreach($inputarr as $v) {
-
- $sql .= $sqlarr[$i];
- // from Ron Baldwin
\n($this->databaseType): ".htmlspecialchars($sqlTxt)." $ss
\n
\n",false);
- else
- ADOConnection::outp( "=----\n($this->databaseType): ".($sqlTxt)." \n-----\n",false);
- flush();
-
- $this->_queryID = $this->_query($sql,$inputarr,$arg3);
-
- /*
- Alexios Fakios notes that ErrorMsg() must be called before ErrorNo() for mssql
- because ErrorNo() calls Execute('SELECT @ERROR'), causing recure
- */
- if ($this->databaseType == 'mssql') {
- // ErrorNo is a slow function call in mssql, and not reliable
- // in PHP 4.0.6
- if($emsg = $this->ErrorMsg()) {
- $err = $this->ErrorNo();
- if ($err) {
- ADOConnection::outp($err.': '.$emsg);
- flush();
- }
- }
- } else
- if (!$this->_queryID) {
- $e = $this->ErrorNo();
- $m = $this->ErrorMsg();
- ADOConnection::outp($e .': '. $m );
- flush();
- }
- } else {
- // non-debug version of query
-
- $this->_queryID =@$this->_query($sql,$inputarr,$arg3);
-
- }
- // error handling if query fails
- if ($this->_queryID === false) {
- $fn = $this->raiseErrorFn;
- if ($fn) {
- $fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,$inputarr,$this);
- }
- return false;
- } else if ($this->_queryID === true) {
- // return simplified empty recordset for inserts/updates/deletes with lower overhead
- $rs = new ADORecordSet_empty();
- return $rs;
- }
-
- // return real recordset from select statement
- $rsclass = "ADORecordSet_".$this->databaseType;
- $rs = new $rsclass($this->_queryID,$this->fetchMode); // &new not supported by older PHP versions
- $rs->connection = &$this; // Pablo suggestion
- $rs->Init();
- if (is_array($sql)) $rs->sql = $sql[0];
- else $rs->sql = $sql;
-
- if ($rs->_numOfRows <= 0) {
- global $ADODB_COUNTRECS;
-
- if ($ADODB_COUNTRECS) {
- if (!$rs->EOF){
- $rs = &$this->_rs2rs($rs,-1,-1,!is_array($sql));
- $rs->_queryID = $this->_queryID;
- } else
- $rs->_numOfRows = 0;
- }
- }
- return $rs;
- }
-
- function CreateSequence($seqname='adodbseq',$startID=1)
- {
- if (empty($this->_genSeqSQL)) return false;
- return $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
- }
-
- function DropSequence($seqname)
- {
- if (empty($this->_dropSeqSQL)) return false;
- return $this->Execute(sprintf($this->_dropSeqSQL,$seqname));
- }
-
- /**
- * Generates a sequence id and stores it in $this->genID;
- * GenID is only available if $this->hasGenID = true;
- *
- * @param seqname name of sequence to use
- * @param startID if sequence does not exist, start at this ID
- * @return 0 if not supported, otherwise a sequence id
- */
-
- function GenID($seqname='adodbseq',$startID=1)
- {
- if (!$this->hasGenID) {
- return 0; // formerly returns false pre 1.60
- }
-
- $getnext = sprintf($this->_genIDSQL,$seqname);
- $rs = @$this->Execute($getnext);
- if (!$rs) {
- $createseq = $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
- $rs = $this->Execute($getnext);
- }
- if ($rs && !$rs->EOF) $this->genID = reset($rs->fields);
- else $this->genID = 0; // false
-
- if ($rs) $rs->Close();
-
- return $this->genID;
- }
-
- /**
- * @return the last inserted ID. Not all databases support this.
- */
- function Insert_ID()
- {
- if ($this->hasInsertID) return $this->_insertid();
- if ($this->debug) ADOConnection::outp( '\n", system($cmd),"
");
- } else {
- exec($cmd);
- }
- return;
- }
- $f = $this->_gencachename($sql.serialize($inputarr),false);
- adodb_write_file($f,''); // is adodb_write_file needed?
- @unlink($f);
- }
-
- /**
- * Private function to generate filename for caching.
- * Filename is generated based on:
- *
- * - sql statement
- * - database type (oci8, ibase, ifx, etc)
- * - database name
- * - userid
- *
- * We create 256 sub-directories in the cache directory ($ADODB_CACHE_DIR).
- * 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)
- {
- global $ADODB_CACHE_DIR;
-
- $m = md5($sql.$this->databaseType.$this->database.$this->user);
- $dir = $ADODB_CACHE_DIR.'/'.substr($m,0,2);
- if ($createdir && !file_exists($dir)) {
- $oldu = umask(0);
- if (!mkdir($dir,0771))
- if ($this->debug) ADOConnection::outp( "Unable to mkdir $dir for $sql");
- umask($oldu);
- }
- return $dir.'/adodb_'.$m.'.cache';
- }
-
-
- /**
- * Execute SQL, caching recordsets.
- *
- * @param [secs2cache] seconds to cache data, set to 0 to force query.
- * This is an optional parameter.
- * @param sql SQL statement to execute
- * @param [inputarr] holds the input data to bind to
- * @param [arg3] reserved for john lim for future use
- * @return RecordSet or false
- */
- function &CacheExecute($secs2cache,$sql=false,$inputarr=false,$arg3=false)
- {
- if (!is_numeric($secs2cache)) {
- $arg3 = $inputarr;
- $inputarr = $sql;
- $sql = $secs2cache;
- $secs2cache = $this->cacheSecs;
- }
- include_once(ADODB_DIR.'/adodb-csvlib.inc.php');
-
- $md5file = $this->_gencachename($sql.serialize($inputarr),true);
- $err = '';
-
- if ($secs2cache > 0){
- $rs = &csv2rs($md5file,$err,$secs2cache);
- $this->numCacheHits += 1;
- } else {
- $err='Timeout 1';
- $rs = false;
- $this->numCacheMisses += 1;
- }
- if (!$rs) {
- // no cached rs found
- if ($this->debug) {
- if (get_magic_quotes_runtime()) {
- ADOConnection::outp("Please disable magic_quotes_runtime - it corrupts cache files :(");
- }
- ADOConnection::outp( " $md5file cache failure: $err (see sql below)");
- }
- $rs = &$this->Execute($sql,$inputarr,$arg3);
- if ($rs) {
- $eof = $rs->EOF;
- $rs = &$this->_rs2rs($rs); // read entire recordset into memory immediately
- $txt = _rs2serialize($rs,false,$sql); // serialize
-
- if (!adodb_write_file($md5file,$txt,$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");
- }
- if ($rs->EOF && !$eof) {
- $rs->MoveFirst();
- //$rs = &csv2rs($md5file,$err);
- $rs->connection = &$this; // Pablo suggestion
- }
-
- } else
- @unlink($md5file);
- } else {
- if ($this->fnCacheExecute) {
- $fn = $this->fnCacheExecute;
- $fn($this, $secs2cache, $sql, $inputarr);
- }
- // ok, set cached object found
- $rs->connection = &$this; // Pablo suggestion
- if ($this->debug){
- global $HTTP_SERVER_VARS;
-
- $inBrowser = isset($HTTP_SERVER_VARS['HTTP_USER_AGENT']);
- $ttl = $rs->timeCreated + $secs2cache - time();
- $s = is_array($sql) ? $sql[0] : $sql;
- if ($inBrowser) $s = ''.htmlspecialchars($s).'';
-
- ADOConnection::outp( " $md5file reloaded, ttl=$ttl [ $s ]");
- }
- }
- return $rs;
- }
-
-
- /**
- * Generates an Update Query based on an existing recordset.
- * $arrFields is an associative array of fields with the value
- * that should be assigned.
- *
- * Note: This function should only be used on a recordset
- * that is run against a single table and sql should only
- * be a simple select stmt with no groupby/orderby/limit
- *
- * "Jonathan Younger"
\n";
+
+ if (isset($HTTP_SERVER_VARS['HTTP_USER_AGENT'])) echo $msg;
+ else echo strip_tags($msg);
+ flush();
+ }
+
+ /**
+ * Connect to database
+ *
+ * @param [argHostname] Host to connect to
+ * @param [argUsername] Userid to login
+ * @param [argPassword] Associated password
+ * @param [argDatabaseName] database
+ * @param [forceNew] force new connection
+ *
+ * @return true or false
+ */
+ function Connect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "", $forceNew = false)
+ {
+ if ($argHostname != "") $this->host = $argHostname;
+ if ($argUsername != "") $this->user = $argUsername;
+ if ($argPassword != "") $this->password = $argPassword; /* not stored for security reasons */
+ if ($argDatabaseName != "") $this->database = $argDatabaseName;
+
+ $this->_isPersistentConnection = false;
+ if ($fn = $this->raiseErrorFn) {
+ if ($forceNew) {
+ if ($this->_nconnect($this->host, $this->user, $this->password, $this->database)) return true;
+ } else {
+ if ($this->_connect($this->host, $this->user, $this->password, $this->database)) return true;
+ }
+ $err = $this->ErrorMsg();
+ if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'";
+ $fn($this->databaseType,'CONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this);
+ } else {
+ if ($forceNew) {
+ if ($this->_nconnect($this->host, $this->user, $this->password, $this->database)) return true;
+ } else {
+ if ($this->_connect($this->host, $this->user, $this->password, $this->database)) return true;
+ }
+ }
+ if ($this->debug) ADOConnection::outp( $this->host.': '.$this->ErrorMsg());
+
+ return false;
+ }
+
+ function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName)
+ {
+ return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName);
+ }
+
+
+ /**
+ * Always force a new connection to database - currently only works with oracle
+ *
+ * @param [argHostname] Host to connect to
+ * @param [argUsername] Userid to login
+ * @param [argPassword] Associated password
+ * @param [argDatabaseName] database
+ *
+ * @return true or false
+ */
+ function NConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "")
+ {
+ return $this->Connect($argHostname, $argUsername, $argPassword, $argDatabaseName, true);
+ }
+
+ /**
+ * Establish persistent connect to database
+ *
+ * @param [argHostname] Host to connect to
+ * @param [argUsername] Userid to login
+ * @param [argPassword] Associated password
+ * @param [argDatabaseName] database
+ *
+ * @return return true or false
+ */
+ function PConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "")
+ {
+ if (defined('ADODB_NEVER_PERSIST'))
+ return $this->Connect($argHostname,$argUsername,$argPassword,$argDatabaseName);
+
+ if ($argHostname != "") $this->host = $argHostname;
+ if ($argUsername != "") $this->user = $argUsername;
+ if ($argPassword != "") $this->password = $argPassword;
+ if ($argDatabaseName != "") $this->database = $argDatabaseName;
+
+ $this->_isPersistentConnection = true;
+
+ if ($fn = $this->raiseErrorFn) {
+ if ($this->_pconnect($this->host, $this->user, $this->password, $this->database)) return true;
+ $err = $this->ErrorMsg();
+ if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'";
+ $fn($this->databaseType,'PCONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this);
+ } else
+ if ($this->_pconnect($this->host, $this->user, $this->password, $this->database)) return true;
+
+ if ($this->debug) ADOConnection::outp( $this->host.': '.$this->ErrorMsg());
+
+ return false;
+ }
+
+ /* Format date column in sql string given an input format that understands Y M D */
+ function SQLDate($fmt, $col=false)
+ {
+ if (!$col) $col = $this->sysDate;
+ return $col; /* child class implement */
+ }
+
+ /**
+ * Should prepare the sql statement and return the stmt resource.
+ * For databases that do not support this, we return the $sql. To ensure
+ * compatibility with databases that do not support prepare:
+ *
+ * $stmt = $db->Prepare("insert into table (id, name) values (?,?)");
+ * $db->Execute($stmt,array(1,'Jill')) or die('insert failed');
+ * $db->Execute($stmt,array(2,'Joe')) or die('insert failed');
+ *
+ * @param sql SQL to send to database
+ *
+ * @return return FALSE, or the prepared statement, or the original sql if
+ * if the database does not support prepare.
+ *
+ */
+ function Prepare($sql)
+ {
+ return $sql;
+ }
+
+ /**
+ * Some databases, eg. mssql require a different function for preparing
+ * stored procedures. So we cannot use Prepare().
+ *
+ * Should prepare the stored procedure and return the stmt resource.
+ * For databases that do not support this, we return the $sql. To ensure
+ * compatibility with databases that do not support prepare:
+ *
+ * @param sql SQL to send to database
+ *
+ * @return return FALSE, or the prepared statement, or the original sql if
+ * if the database does not support prepare.
+ *
+ */
+ function PrepareSP($sql)
+ {
+ return $this->Prepare($sql);
+ }
+
+ /**
+ * PEAR DB Compat
+ */
+ function Quote($s)
+ {
+ return $this->qstr($s,false);
+ }
+
+
+ /**
+ * PEAR DB Compat - do not use internally.
+ */
+ function ErrorNative()
+ {
+ return $this->ErrorNo();
+ }
+
+
+ /**
+ * PEAR DB Compat - do not use internally.
+ */
+ function nextId($seq_name)
+ {
+ return $this->GenID($seq_name);
+ }
+
+ /**
+ * Lock a row, will escalate and lock the table if row locking not supported
+ * will normally free the lock at the end of the transaction
+ *
+ * @param $table name of table to lock
+ * @param $where where clause to use, eg: "WHERE row=12". If left empty, will escalate to table lock
+ */
+ function RowLock($table,$where)
+ {
+ return false;
+ }
+
+ function CommitLock($table)
+ {
+ return $this->CommitTrans();
+ }
+
+ function RollbackLock($table)
+ {
+ return $this->RollbackTrans();
+ }
+
+ /**
+ * PEAR DB Compat - do not use internally.
+ *
+ * The fetch modes for NUMERIC and ASSOC for PEAR DB and ADODB are identical
+ * for easy porting :-)
+ *
+ * @param mode The fetchmode ADODB_FETCH_ASSOC or ADODB_FETCH_NUM
+ * @returns The previous fetch mode
+ */
+ function SetFetchMode($mode)
+ {
+ $old = $this->fetchMode;
+ $this->fetchMode = $mode;
+
+ if ($old === false) {
+ global $ADODB_FETCH_MODE;
+ return $ADODB_FETCH_MODE;
+ }
+ return $old;
+ }
+
+
+ /**
+ * PEAR DB Compat - do not use internally.
+ */
+ function &Query($sql, $inputarr=false)
+ {
+ $rs = &$this->Execute($sql, $inputarr);
+ if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error();
+ return $rs;
+ }
+
+
+ /**
+ * PEAR DB Compat - do not use internally
+ */
+ function &LimitQuery($sql, $offset, $count)
+ {
+ $rs = &$this->SelectLimit($sql, $count, $offset); /* swap */
+ if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error();
+ return $rs;
+ }
+
+
+ /**
+ * PEAR DB Compat - do not use internally
+ */
+ function Disconnect()
+ {
+ return $this->Close();
+ }
+
+ /*
+ Usage in oracle
+ $stmt = $db->Prepare('select * from table where id =:myid and group=:group');
+ $db->Parameter($stmt,$id,'myid');
+ $db->Parameter($stmt,$group,'group',64);
+ $db->Execute();
+
+ @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.
+
+ */
+ function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false)
+ {
+ return false;
+ }
+
+ /**
+ Improved method of initiating a transaction. Used together with CompleteTrans().
+ Advantages include:
+
+ a. StartTrans/CompleteTrans is nestable, unlike BeginTrans/CommitTrans/RollbackTrans.
+ Only the outermost block is treated as a transaction.
+ b. CompleteTrans auto-detects SQL errors, and will rollback on errors, commit otherwise.
+ c. All BeginTrans/CommitTrans/RollbackTrans inside a StartTrans/CompleteTrans block
+ are disabled, making it backward compatible.
+ */
+ function StartTrans($errfn = 'ADODB_TransMonitor')
+ {
+
+ if ($this->transOff > 0) {
+ $this->transOff += 1;
+ return;
+ }
+
+ $this->_oldRaiseFn = $this->raiseErrorFn;
+ $this->raiseErrorFn = $errfn;
+ $this->_transOK = true;
+
+ if ($this->debug && $this->transCnt > 0) ADOConnection::outp("Bad Transaction: StartTrans called within BeginTrans");
+ $this->BeginTrans();
+ $this->transOff = 1;
+ }
+
+ /**
+ Used together with StartTrans() to end a transaction. Monitors connection
+ for sql errors, and will commit or rollback as appropriate.
+
+ @autoComplete if true, monitor sql errors and commit and rollback as appropriate,
+ and if set to false force rollback even if no SQL error detected.
+ @returns true on commit, false on rollback.
+ */
+ function CompleteTrans($autoComplete = true)
+ {
+ if ($this->transOff > 1) {
+ $this->transOff -= 1;
+ return true;
+ }
+ $this->raiseErrorFn = $this->_oldRaiseFn;
+
+ $this->transOff = 0;
+ if ($this->_transOK && $autoComplete) $this->CommitTrans();
+ else $this->RollbackTrans();
+
+ return $this->_transOK;
+ }
+
+ /*
+ At the end of a StartTrans/CompleteTrans block, perform a rollback.
+ */
+ function FailTrans()
+ {
+ if ($this->debug && $this->transOff == 0) {
+ ADOConnection::outp("FailTrans outside StartTrans/CompleteTrans");
+ }
+ $this->_transOK = false;
+ }
+ /**
+ * 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.
+ * @param [arg3] reserved for john lim for future use
+ * @return RecordSet or false
+ */
+ function &Execute($sql,$inputarr=false,$arg3=false)
+ {
+ if ($this->fnExecute) {
+ $fn = $this->fnExecute;
+ $fn($this,$sql,$inputarr);
+ }
+ if (!$this->_bindInputArray && $inputarr) {
+ $sqlarr = explode('?',$sql);
+ $sql = '';
+ $i = 0;
+ foreach($inputarr as $v) {
+
+ $sql .= $sqlarr[$i];
+ /* from Ron Baldwin
\n($this->databaseType): ".htmlspecialchars($sqlTxt)." $ss
\n
\n",false);
+ else
+ ADOConnection::outp( "=----\n($this->databaseType): ".($sqlTxt)." \n-----\n",false);
+ flush();
+
+ $this->_queryID = $this->_query($sql,$inputarr,$arg3);
+
+ /*
+ Alexios Fakios notes that ErrorMsg() must be called before ErrorNo() for mssql
+ because ErrorNo() calls Execute('SELECT @ERROR'), causing recure
+ */
+ if ($this->databaseType == 'mssql') {
+ /* ErrorNo is a slow function call in mssql, and not reliable */
+ /* in PHP 4.0.6 */
+ if($emsg = $this->ErrorMsg()) {
+ $err = $this->ErrorNo();
+ if ($err) {
+ ADOConnection::outp($err.': '.$emsg);
+ flush();
+ }
+ }
+ } else
+ if (!$this->_queryID) {
+ $e = $this->ErrorNo();
+ $m = $this->ErrorMsg();
+ ADOConnection::outp($e .': '. $m );
+ flush();
+ }
+ } else {
+ /* non-debug version of query */
+
+ $this->_queryID =@$this->_query($sql,$inputarr,$arg3);
+
+ }
+ /* error handling if query fails */
+ if ($this->_queryID === false) {
+ $fn = $this->raiseErrorFn;
+ if ($fn) {
+ $fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,$inputarr,$this);
+ }
+ return false;
+ } else if ($this->_queryID === true) {
+ /* return simplified empty recordset for inserts/updates/deletes with lower overhead */
+ $rs = new ADORecordSet_empty();
+ return $rs;
+ }
+
+ /* return real recordset from select statement */
+ $rsclass = "ADORecordSet_".$this->databaseType;
+ $rs = new $rsclass($this->_queryID,$this->fetchMode); /* &new not supported by older PHP versions */
+ $rs->connection = &$this; /* Pablo suggestion */
+ $rs->Init();
+ if (is_array($sql)) $rs->sql = $sql[0];
+ else $rs->sql = $sql;
+
+ if ($rs->_numOfRows <= 0) {
+ global $ADODB_COUNTRECS;
+
+ if ($ADODB_COUNTRECS) {
+ if (!$rs->EOF){
+ $rs = &$this->_rs2rs($rs,-1,-1,!is_array($sql));
+ $rs->_queryID = $this->_queryID;
+ } else
+ $rs->_numOfRows = 0;
+ }
+ }
+ return $rs;
+ }
+
+ function CreateSequence($seqname='adodbseq',$startID=1)
+ {
+ if (empty($this->_genSeqSQL)) return false;
+ return $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
+ }
+
+ function DropSequence($seqname)
+ {
+ if (empty($this->_dropSeqSQL)) return false;
+ return $this->Execute(sprintf($this->_dropSeqSQL,$seqname));
+ }
+
+ /**
+ * Generates a sequence id and stores it in $this->genID;
+ * GenID is only available if $this->hasGenID = true;
+ *
+ * @param seqname name of sequence to use
+ * @param startID if sequence does not exist, start at this ID
+ * @return 0 if not supported, otherwise a sequence id
+ */
+
+ function GenID($seqname='adodbseq',$startID=1)
+ {
+ if (!$this->hasGenID) {
+ return 0; /* formerly returns false pre 1.60 */
+ }
+
+ $getnext = sprintf($this->_genIDSQL,$seqname);
+ $rs = @$this->Execute($getnext);
+ if (!$rs) {
+ $createseq = $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
+ $rs = $this->Execute($getnext);
+ }
+ if ($rs && !$rs->EOF) $this->genID = reset($rs->fields);
+ else $this->genID = 0; /* false */
+
+ if ($rs) $rs->Close();
+
+ return $this->genID;
+ }
+
+ /**
+ * @return the last inserted ID. Not all databases support this.
+ */
+ function Insert_ID()
+ {
+ if ($this->hasInsertID) return $this->_insertid();
+ if ($this->debug) ADOConnection::outp( '\n", system($cmd),"
");
+ } else {
+ exec($cmd);
+ }
+ return;
+ }
+ $f = $this->_gencachename($sql.serialize($inputarr),false);
+ adodb_write_file($f,''); /* is adodb_write_file needed? */
+ @unlink($f);
+ }
+
+ /**
+ * Private function to generate filename for caching.
+ * Filename is generated based on:
+ *
+ * - sql statement
+ * - database type (oci8, ibase, ifx, etc)
+ * - database name
+ * - userid
+ *
+ * We create 256 sub-directories in the cache directory ($ADODB_CACHE_DIR).
+ * 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)
+ {
+ global $ADODB_CACHE_DIR;
+
+ $m = md5($sql.$this->databaseType.$this->database.$this->user);
+ $dir = $ADODB_CACHE_DIR.'/'.substr($m,0,2);
+ if ($createdir && !file_exists($dir)) {
+ $oldu = umask(0);
+ if (!mkdir($dir,0771))
+ if ($this->debug) ADOConnection::outp( "Unable to mkdir $dir for $sql");
+ umask($oldu);
+ }
+ return $dir.'/adodb_'.$m.'.cache';
+ }
+
+
+ /**
+ * Execute SQL, caching recordsets.
+ *
+ * @param [secs2cache] seconds to cache data, set to 0 to force query.
+ * This is an optional parameter.
+ * @param sql SQL statement to execute
+ * @param [inputarr] holds the input data to bind to
+ * @param [arg3] reserved for john lim for future use
+ * @return RecordSet or false
+ */
+ function &CacheExecute($secs2cache,$sql=false,$inputarr=false,$arg3=false)
+ {
+ if (!is_numeric($secs2cache)) {
+ $arg3 = $inputarr;
+ $inputarr = $sql;
+ $sql = $secs2cache;
+ $secs2cache = $this->cacheSecs;
+ }
+ include_once(ADODB_DIR.'/adodb-csvlib.inc.php');
+
+ $md5file = $this->_gencachename($sql.serialize($inputarr),true);
+ $err = '';
+
+ if ($secs2cache > 0){
+ $rs = &csv2rs($md5file,$err,$secs2cache);
+ $this->numCacheHits += 1;
+ } else {
+ $err='Timeout 1';
+ $rs = false;
+ $this->numCacheMisses += 1;
+ }
+ if (!$rs) {
+ /* no cached rs found */
+ if ($this->debug) {
+ if (get_magic_quotes_runtime()) {
+ ADOConnection::outp("Please disable magic_quotes_runtime - it corrupts cache files :(");
+ }
+ ADOConnection::outp( " $md5file cache failure: $err (see sql below)");
+ }
+ $rs = &$this->Execute($sql,$inputarr,$arg3);
+ if ($rs) {
+ $eof = $rs->EOF;
+ $rs = &$this->_rs2rs($rs); /* read entire recordset into memory immediately */
+ $txt = _rs2serialize($rs,false,$sql); /* serialize */
+
+ if (!adodb_write_file($md5file,$txt,$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");
+ }
+ if ($rs->EOF && !$eof) {
+ $rs->MoveFirst();
+ /* $rs = &csv2rs($md5file,$err); */
+ $rs->connection = &$this; /* Pablo suggestion */
+ }
+
+ } else
+ @unlink($md5file);
+ } else {
+ if ($this->fnCacheExecute) {
+ $fn = $this->fnCacheExecute;
+ $fn($this, $secs2cache, $sql, $inputarr);
+ }
+ /* ok, set cached object found */
+ $rs->connection = &$this; /* Pablo suggestion */
+ if ($this->debug){
+ global $HTTP_SERVER_VARS;
+
+ $inBrowser = isset($HTTP_SERVER_VARS['HTTP_USER_AGENT']);
+ $ttl = $rs->timeCreated + $secs2cache - time();
+ $s = is_array($sql) ? $sql[0] : $sql;
+ if ($inBrowser) $s = ''.htmlspecialchars($s).'';
+
+ ADOConnection::outp( " $md5file reloaded, ttl=$ttl [ $s ]");
+ }
+ }
+ return $rs;
+ }
+
+
+ /**
+ * Generates an Update Query based on an existing recordset.
+ * $arrFields is an associative array of fields with the value
+ * that should be assigned.
+ *
+ * Note: This function should only be used on a recordset
+ * that is run against a single table and sql should only
+ * be a simple select stmt with no groupby/orderby/limit
+ *
+ * "Jonathan Younger" Session Error: PHP.INI setting session.gc_maxlifetimenot set: $ADODB_SESS_LIFE
";
- $ADODB_SESS_LIFE=1440;
-}
-
-function adodb_sess_open($save_path, $session_name)
-{
-GLOBAL $ADODB_SESSION_CONNECT,
- $ADODB_SESSION_DRIVER,
- $ADODB_SESSION_USER,
- $ADODB_SESSION_PWD,
- $ADODB_SESSION_DB,
- $ADODB_SESS_CONN,
- $ADODB_SESS_DEBUG;
-
- $ADODB_SESS_INSERT = false;
-
- if (isset($ADODB_SESS_CONN)) return true;
-
- $ADODB_SESS_CONN = ADONewConnection($ADODB_SESSION_DRIVER);
- if (!empty($ADODB_SESS_DEBUG)) {
- $ADODB_SESS_CONN->debug = true;
- print" conn=$ADODB_SESSION_CONNECT user=$ADODB_SESSION_USER pwd=$ADODB_SESSION_PWD db=$ADODB_SESSION_DB ";
- }
- return $ADODB_SESS_CONN->PConnect($ADODB_SESSION_CONNECT,
- $ADODB_SESSION_USER,$ADODB_SESSION_PWD,$ADODB_SESSION_DB);
-
-}
-
-function adodb_sess_close()
-{
-global $ADODB_SESS_CONN;
-
- if ($ADODB_SESS_CONN) $ADODB_SESS_CONN->Close();
- return true;
-}
-
-function adodb_sess_read($key)
-{
-$Crypt = new MD5Crypt;
-global $ADODB_SESS_CONN,$ADODB_SESS_INSERT,$ADODB_SESSION_TBL;
- $rs = $ADODB_SESS_CONN->Execute("SELECT data FROM $ADODB_SESSION_TBL WHERE sesskey = '$key' AND expiry >= " . time());
- if ($rs) {
- if ($rs->EOF) {
- $ADODB_SESS_INSERT = true;
- $v = '';
- } else {
- // Decrypt session data
- $v = rawurldecode($Crypt->Decrypt(reset($rs->fields), ADODB_Session_Key()));
- }
- $rs->Close();
- return $v;
- }
- else $ADODB_SESS_INSERT = true;
-
- return '';
-}
-
-function adodb_sess_write($key, $val)
-{
-$Crypt = new MD5Crypt;
- global $ADODB_SESS_INSERT,$ADODB_SESS_CONN, $ADODB_SESS_LIFE, $ADODB_SESSION_TBL,$ADODB_SESSION_EXPIRE_NOTIFY;
-
- $expiry = time() + $ADODB_SESS_LIFE;
-
- // encrypt session data..
- $val = $Crypt->Encrypt(rawurlencode($val), ADODB_Session_Key());
-
- $arr = array('sesskey' => $key, 'expiry' => $expiry, 'data' => $val);
- if ($ADODB_SESSION_EXPIRE_NOTIFY) {
- $var = reset($ADODB_SESSION_EXPIRE_NOTIFY);
- global $$var;
- $arr['expireref'] = $$var;
- }
- $rs = $ADODB_SESS_CONN->Replace($ADODB_SESSION_TBL,
- $arr,
- 'sesskey',$autoQuote = true);
-
- if (!$rs) {
- ADOConnection::outp( 'Session Error: PHP.INI setting session.gc_maxlifetimenot set: $ADODB_SESS_LIFE
"; */
+ $ADODB_SESS_LIFE=1440;
+}
+
+function adodb_sess_open($save_path, $session_name)
+{
+GLOBAL $ADODB_SESSION_CONNECT,
+ $ADODB_SESSION_DRIVER,
+ $ADODB_SESSION_USER,
+ $ADODB_SESSION_PWD,
+ $ADODB_SESSION_DB,
+ $ADODB_SESS_CONN,
+ $ADODB_SESS_DEBUG;
+
+ $ADODB_SESS_INSERT = false;
+
+ if (isset($ADODB_SESS_CONN)) return true;
+
+ $ADODB_SESS_CONN = ADONewConnection($ADODB_SESSION_DRIVER);
+ if (!empty($ADODB_SESS_DEBUG)) {
+ $ADODB_SESS_CONN->debug = true;
+ print" conn=$ADODB_SESSION_CONNECT user=$ADODB_SESSION_USER pwd=$ADODB_SESSION_PWD db=$ADODB_SESSION_DB ";
+ }
+ return $ADODB_SESS_CONN->PConnect($ADODB_SESSION_CONNECT,
+ $ADODB_SESSION_USER,$ADODB_SESSION_PWD,$ADODB_SESSION_DB);
+
+}
+
+function adodb_sess_close()
+{
+global $ADODB_SESS_CONN;
+
+ if ($ADODB_SESS_CONN) $ADODB_SESS_CONN->Close();
+ return true;
+}
+
+function adodb_sess_read($key)
+{
+$Crypt = new MD5Crypt;
+global $ADODB_SESS_CONN,$ADODB_SESS_INSERT,$ADODB_SESSION_TBL;
+ $rs = $ADODB_SESS_CONN->Execute("SELECT data FROM $ADODB_SESSION_TBL WHERE sesskey = '$key' AND expiry >= " . time());
+ if ($rs) {
+ if ($rs->EOF) {
+ $ADODB_SESS_INSERT = true;
+ $v = '';
+ } else {
+ /* Decrypt session data */
+ $v = rawurldecode($Crypt->Decrypt(reset($rs->fields), ADODB_Session_Key()));
+ }
+ $rs->Close();
+ return $v;
+ }
+ else $ADODB_SESS_INSERT = true;
+
+ return '';
+}
+
+function adodb_sess_write($key, $val)
+{
+$Crypt = new MD5Crypt;
+ global $ADODB_SESS_INSERT,$ADODB_SESS_CONN, $ADODB_SESS_LIFE, $ADODB_SESSION_TBL,$ADODB_SESSION_EXPIRE_NOTIFY;
+
+ $expiry = time() + $ADODB_SESS_LIFE;
+
+ /* encrypt session data.. */
+ $val = $Crypt->Encrypt(rawurlencode($val), ADODB_Session_Key());
+
+ $arr = array('sesskey' => $key, 'expiry' => $expiry, 'data' => $val);
+ if ($ADODB_SESSION_EXPIRE_NOTIFY) {
+ $var = reset($ADODB_SESSION_EXPIRE_NOTIFY);
+ global $$var;
+ $arr['expireref'] = $$var;
+ }
+ $rs = $ADODB_SESS_CONN->Replace($ADODB_SESSION_TBL,
+ $arr,
+ 'sesskey',$autoQuote = true);
+
+ if (!$rs) {
+ ADOConnection::outp( '
";
- //print_r($text);
- //if (strlen($text) == 0) $arr = array();
- //else
- $arr = unserialize($text);
- //print_r($arr);
- if (!is_array($arr)) {
- $err = "Recordset had unexpected EOF (in serialized recordset)";
- if (get_magic_quotes_runtime()) $err .= ". Magic Quotes Runtime should be disabled!";
- return false;
- }
- $rs = new ADORecordSet_array();
- $rs->timeCreated = $ttl;
- $rs->InitArrayFields($arr,$flds);
- return $rs;
- }
-
+FieldCount() : 0;
+
+ if ($sql) $sql = urlencode($sql);
+ /* metadata setup */
+
+ if ($max <= 0 || $rs->dataProvider == 'empty') { /* is insert/update/delete */
+ if (is_object($conn)) {
+ $sql .= ','.$conn->Affected_Rows();
+ $sql .= ','.$conn->Insert_ID();
+ } else
+ $sql .= ',,';
+
+ $text = "====-1,0,$sql\n";
+ return $text;
+ } else {
+ $tt = ($rs->timeCreated) ? $rs->timeCreated : time();
+ $line = "====0,$tt,$sql\n";
+ }
+ /* column definitions */
+ for($i=0; $i < $max; $i++) {
+ $o = $rs->FetchField($i);
+ $line .= urlencode($o->name).':'.$rs->MetaType($o->type,$o->max_length).":$o->max_length,";
+ }
+ $text = substr($line,0,strlen($line)-1)."\n";
+
+
+ /* get data */
+ if ($rs->databaseType == 'array') {
+ $text .= serialize($rs->_array);
+ } else {
+ $rows = array();
+ while (!$rs->EOF) {
+ $rows[] = $rs->fields;
+ $rs->MoveNext();
+ }
+ $text .= serialize($rows);
+ }
+ $rs->MoveFirst();
+ return $text;
+ }
+
+
+/**
+* Open CSV file and convert it into Data.
+*
+* @param url file/ftp/http url
+* @param err returns the error message
+* @param timeout dispose if recordset has been alive for $timeout secs
+*
+* @return recordset, or false if error occured. If no
+* error occurred in sql INSERT/UPDATE/DELETE,
+* empty recordset is returned
+*/
+ function &csv2rs($url,&$err,$timeout=0)
+ {
+ $fp = @fopen($url,'r');
+ $err = false;
+ if (!$fp) {
+ $err = $url.'file/URL not found';
+ return false;
+ }
+ flock($fp, LOCK_SH);
+ $arr = array();
+ $ttl = 0;
+
+ if ($meta = fgetcsv ($fp, 32000, ",")) {
+ /* check if error message */
+ if (substr($meta[0],0,4) === '****') {
+ $err = trim(substr($meta[0],4,1024));
+ fclose($fp);
+ return false;
+ }
+ /* check for meta data */
+ /* $meta[0] is -1 means return an empty recordset */
+ /* $meta[1] contains a time */
+
+ if (substr($meta[0],0,4) === '====') {
+
+ if ($meta[0] == "====-1") {
+ if (sizeof($meta) < 5) {
+ $err = "Corrupt first line for format -1";
+ fclose($fp);
+ return false;
+ }
+ fclose($fp);
+
+ if ($timeout > 0) {
+ $err = " Illegal Timeout $timeout ";
+ return false;
+ }
+ $rs->fields = array();
+ $rs->timeCreated = $meta[1];
+ $rs = new ADORecordSet($val=true);
+ $rs->EOF = true;
+ $rs->_numOfFields=0;
+ $rs->sql = urldecode($meta[2]);
+ $rs->affectedrows = (integer)$meta[3];
+ $rs->insertid = $meta[4];
+ return $rs;
+ }
+ # Under high volume loads, we want only 1 thread/process to _write_file
+ # so that we don't have 50 processes queueing to write the same data.
+ # Would require probabilistic blocking write
+ #
+ # -2 sec before timeout, give processes 1/16 chance of writing to file with blocking io
+ # -1 sec after timeout give processes 1/4 chance of writing with blocking
+ # +0 sec after timeout, give processes 100% chance writing with blocking
+ if (sizeof($meta) > 1) {
+ if($timeout >0){
+ $tdiff = $meta[1]+$timeout - time();
+ if ($tdiff <= 2) {
+ switch($tdiff) {
+ case 2:
+ if ((rand() & 15) == 0) {
+ fclose($fp);
+ $err = "Timeout 2";
+ return false;
+ }
+ break;
+ case 1:
+ if ((rand() & 3) == 0) {
+ fclose($fp);
+ $err = "Timeout 1";
+ return false;
+ }
+ break;
+ default:
+ fclose($fp);
+ $err = "Timeout 0";
+ return false;
+ } /* switch */
+
+ } /* if check flush cache */
+ }/* (timeout>0) */
+ $ttl = $meta[1];
+ }
+ $meta = false;
+ $meta = fgetcsv($fp, 16000, ",");
+ if (!$meta) {
+ fclose($fp);
+ $err = "Unexpected EOF 1";
+ return false;
+ }
+ }
+
+ /* Get Column definitions */
+ $flds = array();
+ foreach($meta as $o) {
+ $o2 = explode(':',$o);
+ if (sizeof($o2)!=3) {
+ $arr[] = $meta;
+ $flds = false;
+ break;
+ }
+ $fld = new ADOFieldObject();
+ $fld->name = urldecode($o2[0]);
+ $fld->type = $o2[1];
+ $fld->max_length = $o2[2];
+ $flds[] = $fld;
+ }
+ } else {
+ fclose($fp);
+ $err = "Recordset had unexpected EOF 2";
+ return false;
+ }
+
+ /* slurp in the data */
+ $MAXSIZE = 128000;
+ $text = fread($fp,$MAXSIZE);
+ $cnt = 1;
+ while (strlen($text) == $MAXSIZE*$cnt) {
+ $text .= fread($fp,$MAXSIZE);
+ $cnt += 1;
+ }
+
+ fclose($fp);
+ $arr = @unserialize($text);
+
+ /* var_dump($arr); */
+ if (!is_array($arr)) {
+ $err = "Recordset had unexpected EOF (in serialized recordset)";
+ if (get_magic_quotes_runtime()) $err .= ". Magic Quotes Runtime should be disabled!";
+ return false;
+ }
+ $rs = new ADORecordSet_array();
+ $rs->timeCreated = $ttl;
+ $rs->InitArrayFields($arr,$flds);
+ return $rs;
+ }
?>
\ No newline at end of file
diff --git a/lib/adodb/adodb-datadict.inc.php b/lib/adodb/adodb-datadict.inc.php
index 2ddf79d307..c5a19df5c6 100644
--- a/lib/adodb/adodb-datadict.inc.php
+++ b/lib/adodb/adodb-datadict.inc.php
@@ -1,332 +1,582 @@
-connection->MetaTables();
- }
-
- function MetaColumns($tab)
- {
- return $this->connection->MetaColumns($tab);
- }
-
- function MetaPrimaryKeys($tab,$owner=false,$intkey=false)
- {
- return $this->connection->MetaPrimaryKeys($tab.$owner,$intkey);
- }
-
- function MetaType($t,$len=-1,$fieldobj=false)
- {
- return ADORecordSet::MetaType($t,$len,$fieldobj);
- }
-
- // Executes the sql array returned by GetTableSQL and GetIndexSQL
- function ExecuteSQLArray($sql, $continueOnError = true)
- {
- $rez = 2;
- $conn = &$this->connection;
- foreach($sql as $line) {
- $ok = $conn->Execute($line);
- if (!$ok) {
- if ($this->debug) ADOConnection::outp($conn->ErrorMsg());
- if (!$continueOnError) return 0;
- $rez = 1;
- }
- }
- return 2;
- }
-
- /*
- Returns the actual type given a character code.
-
- 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
- T: Date-time
- L: Integer field suitable for storing booleans (0 or 1)
- I: Integer
- F: Floating point number
- N: Numeric or decimal number
- */
-
- function ActualType($meta)
- {
- return $meta;
- }
-
- function CreateDatabase($dbname,$options=false)
- {
- $options = $this->_Options($options);
- $s = 'CREATE DATABASE '.$dbname;
- if (isset($options[$this->upperName])) $s .= ' '.$options[$this->upperName];
- $sql[] = $s;
- return $sql;
- }
-
- /*
- Generates the SQL to create index. Returns an array of sql strings.
- */
- function CreateIndexSQL($idxname, $tabname, $flds, $idxoptions = false)
- {
- if ($this->schema) $tabname = $this->schema.'.'.$tabname;
- return $this->_IndexSQL($idxname, $tabname, $flds, $this->_Options($idxoptions));
- }
-
- function SetSchema($schema)
- {
- $this->schema = $schema;
- }
-
- function AddColumnSQL($tabname, $flds)
- {
- if ($this->schema) $tabname = $this->schema.'.'.$tabname;
- $sql = array();
- list($lines,$pkey) = $this->_GenFields($flds);
- foreach($lines as $v) {
- $sql[] = "ALTER TABLE $tabname $this->addCol $v";
- }
- return $sql;
- }
-
- function AlterColumnSQL($tabname, $flds)
- {
- if ($this->schema) $tabname = $this->schema.'.'.$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)
- {
- if ($this->schema) $tabname = $this->schema.'.'.$tabname;
- if (!is_array($flds)) $flds = explode(',',$flds);
- $sql = array();
- foreach($flds as $v) {
- $sql[] = "ALTER TABLE $tabname $this->dropCol $v";
- }
- return $sql;
- }
-
- function DropTableSQL($tabname)
- {
- if ($this->schema) $tabname = $this->schema.'.'.$tabname;
- $sql[] = sprintf($this->dropTable,$tabname);
- return $sql;
- }
-
- /*
- Generate the SQL to create table. Returns an array of sql strings.
- */
- function CreateTableSQL($tabname, $flds, $tableoptions=false)
- {
- if (!$tableoptions) $tableoptions = array();
-
- list($lines,$pkey) = $this->_GenFields($flds);
-
- $taboptions = $this->_Options($tableoptions);
- if ($this->schema) $tabname = $this->schema.'.'.$tabname;
- $sql = $this->_TableSQL($tabname,$lines,$pkey,$taboptions);
-
- $tsql = $this->_Triggers($tabname,$taboptions);
- foreach($tsql as $s) $sql[] = $s;
-
- return $sql;
- }
-
- function _GenFields($flds)
- {
- $lines = array();
- $pkey = array();
- foreach($flds as $fld) {
- $fld = _array_change_key_case($fld);
-
- $fname = false;
- $fdefault = false;
- $fautoinc = false;
- $ftype = false;
- $fsize = false;
- $fprec = false;
- $fprimary = false;
- $fnoquote = false;
- $fdefts = false;
- $fdefdate = false;
- $fconstraint = false;
- $fnotnull = false;
- //-----------------
- // Parse attributes
- foreach($fld as $attr => $v) {
- if ($attr == 2 && is_numeric($v)) $attr = 'SIZE';
- else if (is_numeric($attr) && $attr > 1 && !is_numeric($v)) $attr = strtoupper($v);
-
- switch($attr) {
- case '0':
- case 'NAME': $fname = $v; break;
- case '1':
- case 'TYPE': $ty = $v; $ftype = $this->ActualType(strtoupper($v)); break;
- case 'SIZE': $dotat = strpos($v,'.');
- if ($dotat === false) $fsize = $v;
- else {
- $fsize = substr($v,0,$dotat);
- $fprec = substr($v,$dotat+1);
- }
- break;
- case 'AUTOINCREMENT':
- case 'AUTO': $fautoinc = true; $fnotnull = true; break;
- case 'KEY':
- case 'PRIMARY': $fprimary = $v; $fnotnull = true; break;
- case 'DEFAULT': $fdefault = $v; break;
- case 'NOTNULL': $fnotnull = $v; break;
- case 'NOQUOTE': $fnoquote = $v; break;
- case 'DEFDATE': $fdefdate = $v; break;
- case 'DEFTIMESTAMP': $fdefts = $v; break;
- case 'CONSTRAINT': $fconstraint = $v; break;
- } //switch
- } // foreach $fld
-
- //--------------------
- // VALIDATE FIELD INFO
- if (!strlen($fname)) {
- if ($this->debug) ADOConnection::outp("Undefined NAME");
- return false;
- }
-
- if (!strlen($ftype)) {
- if ($this->debug) ADOConnection::outp("Undefined TYPE for field '$fname'");
- return false;
- } else
- $ftype = strtoupper($ftype);
-
- $ftype = $this->_GetSize($ftype, $ty, $fsize, $fprec);
-
- if ($fprimary) $pkey[] = $fname;
-
- // some databases do not allow blobs to have defaults
- if ($ty == 'X') $fdefault = false;
-
- //--------------------
- // CONSTRUCT FIELD SQL
- if ($fdefts) {
- if (substr($this->connection->databaseType,0,5) == 'mysql') {
- $ftype = 'TIMESTAMP';
- } else {
- $fdefault = $this->connection->sysTimeStamp;
- }
- } else if ($fdefdate) {
- if (substr($this->connection->databaseType,0,5) == 'mysql') {
- $ftype = 'TIMESTAMP';
- } else {
- $fdefault = $this->connection->sysDate;
- }
- } else if (strlen($fdefault) && !$fnoquote)
- if ($ty == 'C' or $ty == 'X' or
- ( substr($fdefault,0,1) != "'" && !is_numeric($fdefault)))
- if (substr($fdefault,0,1) == ' ' && substr($fdefault,strlen($fdefault)-1) == ' ')
- $fdefault = trim($fdefault);
- else
- $fdefault = $this->connection->qstr($fdefault);
- $suffix = $this->_CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint);
-
- $fname = str_pad($fname,16);
- $lines[] = "$fname $ftype$suffix";
-
- } // foreach $flds
-
-
- return array($lines,$pkey);
- }
- /*
- GENERATE THE SIZE PART OF THE DATATYPE
- $ftype is the actual type
- $ty is the type defined originally in the DDL
- */
- function _GetSize($ftype, $ty, $fsize, $fprec)
- {
- if (strlen($fsize) && $ty != 'X' && $ty != 'B') {
- $ftype .= "(".$fsize;
- if ($fprec) $ftype .= ",".$fprec;
- $ftype .= ')';
- }
- return $ftype;
- }
-
-
- function _TableSQL($tabname,$lines,$pkey,$tableoptions)
- {
- $sql = array();
-
- if (isset($tableoptions['REPLACE'])) $sql[] = sprintf($this->dropTable,$tabname);
- $s = "CREATE TABLE $tabname (\n";
- $s .= implode(",\n", $lines);
- if (sizeof($pkey)>0) {
- $s .= ",\n PRIMARY KEY (";
- $s .= implode(", ",$pkey).")";
- }
- if (isset($tableoptions['CONSTRAINTS']))
- $s .= "\n".$tableoptions['CONSTRAINTS'];
-
- if (isset($tableoptions[$this->upperName.'_CONSTRAINTS']))
- $s .= "\n".$tableoptions[$this->upperName.'_CONSTRAINTS'];
-
- $s .= "\n)";
- if (isset($tableoptions[$this->upperName])) $s .= $tableoptions[$this->upperName];
- $sql[] = $s;
-
- return $sql;
- }
-
- /*
- GENERATE TRIGGERS IF NEEDED
- used when table has auto-incrementing field that is emulated using triggers
- */
- function _Triggers($tabname,$taboptions)
- {
- return array();
- }
-
- /*
- Sanitize options, so that array elements with no keys are promoted to keys
- */
- function _Options($opts)
- {
- if (!is_array($opts)) return array();
- $newopts = array();
- foreach($opts as $k => $v) {
- if (is_numeric($k)) $newopts[strtoupper($v)] = $v;
- else $newopts[strtoupper($k)] = $v;
- }
- return $newopts;
- }
-}
+$str
"; +print_r($a); +print ""; +} +/* Lens_ParseTest(); */ + +/** + Parse arguments, treat "text" (text) and 'text' as quotation marks. + To escape, use "" or '' or )) + + @param endstmtchar Character that indicates end of statement + @param tokenchars Include the following characters in tokens apart from A-Z and 0-9 + @returns 2 dimensional array containing parsed tokens. +*/ +function Lens_ParseArgs($args,$endstmtchar=',',$tokenchars='_.-') +{ + $pos = 0; + $intoken = false; + $stmtno = 0; + $endquote = false; + $tokens = array(); + $tokens[$stmtno] = array(); + $max = strlen($args); + $quoted = false; + + while ($pos < $max) { + $ch = substr($args,$pos,1); + switch($ch) { + case ' ': + case "\t": + case "\n": + case "\r": + if (!$quoted) { + if ($intoken) { + $intoken = false; + $tokens[$stmtno][] = implode('',$tokarr); + } + break; + } + + $tokarr[] = $ch; + break; + + case '(': + case ')': + case '"': + case "'": + + if ($intoken) { + if (empty($endquote)) { + $tokens[$stmtno][] = implode('',$tokarr); + if ($ch == '(') $endquote = ')'; + else $endquote = $ch; + $quoted = true; + $intoken = true; + $tokarr = array(); + } else if ($endquote == $ch) { + $ch2 = substr($args,$pos+1,1); + if ($ch2 == $endquote) { + $pos += 1; + $tokarr[] = $ch2; + } else { + $quoted = false; + $intoken = false; + $tokens[$stmtno][] = implode('',$tokarr); + $endquote = ''; + } + } else + $tokarr[] = $ch; + + }else { + + if ($ch == '(') $endquote = ')'; + else $endquote = $ch; + $quoted = true; + $intoken = true; + $tokarr = array(); + } + break; + + default: + + if (!$intoken) { + if ($ch == $endstmtchar) { + $stmtno += 1; + $tokens[$stmtno] = array(); + break; + } + + $intoken = true; + $quoted = false; + $endquote = false; + $tokarr = array(); + + } + + if ($quoted) $tokarr[] = $ch; + else if (ctype_alnum($ch) || strpos($tokenchars,$ch) !== false) $tokarr[] = $ch; + else { + if ($ch == $endstmtchar) { + $tokens[$stmtno][] = implode('',$tokarr); + $stmtno += 1; + $tokens[$stmtno] = array(); + $intoken = false; + $tokarr = array(); + break; + } + $tokens[$stmtno][] = implode('',$tokarr); + $tokens[$stmtno][] = $ch; + $intoken = false; + } + } + $pos += 1; + } + + return $tokens; +} + + +class ADODB_DataDict { + var $connection; + var $debug = false; + var $dropTable = "DROP TABLE %s"; + var $addCol = ' ADD'; + var $alterCol = ' ALTER COLUMN'; + var $dropCol = ' DROP COLUMN'; + var $schema = false; + var $serverInfo = array(); + var $autoIncrement = false; + var $dataProvider; + var $blobSize = 100; /* / any varchar/char field this size or greater is treated as a blob */ + /* / in other words, we use a text area for editting. */ + + function GetCommentSQL($table,$col) + { + return false; + } + + function SetCommentSQL($table,$col,$cmt) + { + return false; + } + + function &MetaTables() + { + return $this->connection->MetaTables(); + } + + function &MetaColumns($tab) + { + return $this->connection->MetaColumns($tab); + } + + function &MetaPrimaryKeys($tab,$owner=false,$intkey=false) + { + return $this->connection->MetaPrimaryKeys($tab.$owner,$intkey); + } + + function MetaType($t,$len=-1,$fieldobj=false) + { + return ADORecordSet::MetaType($t,$len,$fieldobj); + } + + /* Executes the sql array returned by GetTableSQL and GetIndexSQL */ + function ExecuteSQLArray($sql, $continueOnError = true) + { + $rez = 2; + $conn = &$this->connection; + $saved = $conn->debug; + foreach($sql as $line) { + + if ($this->debug) $conn->debug = true; + $ok = $conn->Execute($line); + $conn->debug = $saved; + if (!$ok) { + if ($this->debug) ADOConnection::outp($conn->ErrorMsg()); + if (!$continueOnError) return 0; + $rez = 1; + } + } + return 2; + } + + /* + Returns the actual type given a character code. + + 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 + T: Date-time + L: Integer field suitable for storing booleans (0 or 1) + I: Integer + F: Floating point number + N: Numeric or decimal number + */ + + function ActualType($meta) + { + return $meta; + } + + function CreateDatabase($dbname,$options=false) + { + $options = $this->_Options($options); + $s = 'CREATE DATABASE '.$dbname; + if (isset($options[$this->upperName])) $s .= ' '.$options[$this->upperName]; + $sql[] = $s; + return $sql; + } + + /* + Generates the SQL to create index. Returns an array of sql strings. + */ + function CreateIndexSQL($idxname, $tabname, $flds, $idxoptions = false) + { + if ($this->schema) $tabname = $this->schema.'.'.$tabname; + return $this->_IndexSQL($idxname, $tabname, $flds, $this->_Options($idxoptions)); + } + + function SetSchema($schema) + { + $this->schema = $schema; + } + + function AddColumnSQL($tabname, $flds) + { + if ($this->schema) $tabname = $this->schema.'.'.$tabname; + $sql = array(); + list($lines,$pkey) = $this->_GenFields($flds); + foreach($lines as $v) { + $sql[] = "ALTER TABLE $tabname $this->addCol $v"; + } + return $sql; + } + + function AlterColumnSQL($tabname, $flds) + { + if ($this->schema) $tabname = $this->schema.'.'.$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) + { + if ($this->schema) $tabname = $this->schema.'.'.$tabname; + if (!is_array($flds)) $flds = explode(',',$flds); + $sql = array(); + foreach($flds as $v) { + $sql[] = "ALTER TABLE $tabname $this->dropCol $v"; + } + return $sql; + } + + function DropTableSQL($tabname) + { + if ($this->schema) $tabname = $this->schema.'.'.$tabname; + $sql[] = sprintf($this->dropTable,$tabname); + return $sql; + } + + /* + Generate the SQL to create table. Returns an array of sql strings. + */ + function CreateTableSQL($tabname, $flds, $tableoptions=false) + { + if (!$tableoptions) $tableoptions = array(); + + list($lines,$pkey) = $this->_GenFields($flds); + + $taboptions = $this->_Options($tableoptions); + if ($this->schema) $tabname = $this->schema.'.'.$tabname; + $sql = $this->_TableSQL($tabname,$lines,$pkey,$taboptions); + + $tsql = $this->_Triggers($tabname,$taboptions); + foreach($tsql as $s) $sql[] = $s; + + return $sql; + } + + function _GenFields($flds) + { + if (is_string($flds)) { + $padding = ' '; + $txt = $flds.$padding; + $flds = array(); + $flds0 = Lens_ParseArgs($txt,','); + $hasparam = false; + foreach($flds0 as $f0) { + $f1 = array(); + foreach($f0 as $token) { + switch (strtoupper($token)) { + case 'CONSTRAINT': + case 'DEFAULT': + $hasparam = $token; + break; + default: + if ($hasparam) $f1[$hasparam] = $token; + else $f1[] = $token; + $hasparam = false; + break; + } + } + $flds[] = $f1; + + } + } + $this->autoIncrement = false; + $lines = array(); + $pkey = array(); + foreach($flds as $fld) { + $fld = _array_change_key_case($fld); + + $fname = false; + $fdefault = false; + $fautoinc = false; + $ftype = false; + $fsize = false; + $fprec = false; + $fprimary = false; + $fnoquote = false; + $fdefts = false; + $fdefdate = false; + $fconstraint = false; + $fnotnull = false; + $funsigned = false; + + /* ----------------- */ + /* Parse attributes */ + foreach($fld as $attr => $v) { + if ($attr == 2 && is_numeric($v)) $attr = 'SIZE'; + else if (is_numeric($attr) && $attr > 1 && !is_numeric($v)) $attr = strtoupper($v); + + switch($attr) { + case '0': + case 'NAME': $fname = $v; break; + case '1': + case 'TYPE': $ty = $v; $ftype = $this->ActualType(strtoupper($v)); break; + case 'SIZE': $dotat = strpos($v,'.'); + if ($dotat === false) $fsize = $v; + else { + $fsize = substr($v,0,$dotat); + $fprec = substr($v,$dotat+1); + } + break; + case 'UNSIGNED': $funsigned = true; break; + case 'AUTOINCREMENT': + case 'AUTO': $fautoinc = true; $fnotnull = true; break; + case 'KEY': + case 'PRIMARY': $fprimary = $v; $fnotnull = true; break; + case 'DEF': + case 'DEFAULT': $fdefault = $v; break; + case 'NOTNULL': $fnotnull = $v; break; + case 'NOQUOTE': $fnoquote = $v; break; + case 'DEFDATE': $fdefdate = $v; break; + case 'DEFTIMESTAMP': $fdefts = $v; break; + case 'CONSTRAINT': $fconstraint = $v; break; + } /* switch */ + } /* foreach $fld */ + + /* -------------------- */ + /* VALIDATE FIELD INFO */ + if (!strlen($fname)) { + if ($this->debug) ADOConnection::outp("Undefined NAME"); + return false; + } + + if (!strlen($ftype)) { + if ($this->debug) ADOConnection::outp("Undefined TYPE for field '$fname'"); + return false; + } else { + $ftype = strtoupper($ftype); + } + + $ftype = $this->_GetSize($ftype, $ty, $fsize, $fprec); + + if ($ty == 'X' || $ty == 'X2' || $ty == 'B') $fnotnull = false; /* some blob types do not accept nulls */ + + if ($fprimary) $pkey[] = $fname; + + /* some databases do not allow blobs to have defaults */ + if ($ty == 'X') $fdefault = false; + + /* -------------------- */ + /* CONSTRUCT FIELD SQL */ + if ($fdefts) { + if (substr($this->connection->databaseType,0,5) == 'mysql') { + $ftype = 'TIMESTAMP'; + } else { + $fdefault = $this->connection->sysTimeStamp; + } + } else if ($fdefdate) { + if (substr($this->connection->databaseType,0,5) == 'mysql') { + $ftype = 'TIMESTAMP'; + } else { + $fdefault = $this->connection->sysDate; + } + } else if (strlen($fdefault) && !$fnoquote) + if ($ty == 'C' or $ty == 'X' or + ( substr($fdefault,0,1) != "'" && !is_numeric($fdefault))) + if (strlen($fdefault) != 1 && substr($fdefault,0,1) == ' ' && substr($fdefault,strlen($fdefault)-1) == ' ') + $fdefault = trim($fdefault); + else if (strtolower($fdefault) != 'null') + $fdefault = $this->connection->qstr($fdefault); + $suffix = $this->_CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned); + + $fname = str_pad($fname,16); + $lines[] = "$fname $ftype$suffix"; + + if ($fautoinc) $this->autoIncrement = true; + } /* foreach $flds */ + + + return array($lines,$pkey); + } + /* + GENERATE THE SIZE PART OF THE DATATYPE + $ftype is the actual type + $ty is the type defined originally in the DDL + */ + function _GetSize($ftype, $ty, $fsize, $fprec) + { + if (strlen($fsize) && $ty != 'X' && $ty != 'B' && strpos($ftype,'(') === false) { + $ftype .= "(".$fsize; + if ($fprec) $ftype .= ",".$fprec; + $ftype .= ')'; + } + return $ftype; + } + + + /* return string must begin with space */ + function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint) + { + $suffix = ''; + if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault"; + if ($fnotnull) $suffix .= ' NOT NULL'; + if ($fconstraint) $suffix .= ' '.$fconstraint; + return $suffix; + } + + function _IndexSQL($idxname, $tabname, $flds, $idxoptions) + { + if (isset($idxoptions['REPLACE'])) $sql[] = "DROP INDEX $idxname"; + if (isset($idxoptions['UNIQUE'])) $unique = ' UNIQUE'; + else $unique = ''; + + if (is_array($flds)) $flds = implode(', ',$flds); + $s = "CREATE$unique INDEX $idxname ON $tabname "; + if (isset($idxoptions[$this->upperName])) $s .= $idxoptions[$this->upperName]; + $s .= "($flds)"; + $sql[] = $s; + + return $sql; + } + + function _DropAutoIncrement($tabname) + { + return false; + } + + function _TableSQL($tabname,$lines,$pkey,$tableoptions) + { + $sql = array(); + + if (isset($tableoptions['REPLACE'])) { + $sql[] = sprintf($this->dropTable,$tabname); + if ($this->autoIncrement) { + $sInc = $this->_DropAutoIncrement($tabname); + if ($sInc) $sql[] = $sInc; + } + } + $s = "CREATE TABLE $tabname (\n"; + $s .= implode(",\n", $lines); + if (sizeof($pkey)>0) { + $s .= ",\n PRIMARY KEY ("; + $s .= implode(", ",$pkey).")"; + } + if (isset($tableoptions['CONSTRAINTS'])) + $s .= "\n".$tableoptions['CONSTRAINTS']; + + if (isset($tableoptions[$this->upperName.'_CONSTRAINTS'])) + $s .= "\n".$tableoptions[$this->upperName.'_CONSTRAINTS']; + + $s .= "\n)"; + if (isset($tableoptions[$this->upperName])) $s .= $tableoptions[$this->upperName]; + $sql[] = $s; + + return $sql; + } + + /* + GENERATE TRIGGERS IF NEEDED + used when table has auto-incrementing field that is emulated using triggers + */ + function _Triggers($tabname,$taboptions) + { + return array(); + } + + /* + Sanitize options, so that array elements with no keys are promoted to keys + */ + function _Options($opts) + { + if (!is_array($opts)) return array(); + $newopts = array(); + foreach($opts as $k => $v) { + if (is_numeric($k)) $newopts[strtoupper($v)] = $v; + else $newopts[strtoupper($k)] = $v; + } + return $newopts; + } + + +/* +"Florian Buzin [ easywe ]"
|<
';
- var $prev = '<<
';
- var $next = '>>
';
- var $last = '>|
';
- var $moreLinks = '...';
- var $startLinks = '...';
- var $gridHeader = false;
- var $htmlSpecialChars = true;
- var $page = 'Page';
- var $linkSelectedColor = 'red';
- var $cache = 0; #secs to cache with CachePageExecute()
-
- //----------------------------------------------
- // constructor
- //
- // $db adodb connection object
- // $sql sql statement
- // $id optional id to identify which pager,
- // if you have multiple on 1 page.
- // $id should be only be [a-z0-9]*
- //
- function ADODB_Pager(&$db,$sql,$id = 'adodb', $showPageLinks = false)
- {
- global $HTTP_SERVER_VARS,$PHP_SELF,$HTTP_SESSION_VARS,$HTTP_GET_VARS;
-
- $curr_page = $id.'_curr_page';
- if (empty($PHP_SELF)) $PHP_SELF = $HTTP_SERVER_VARS['PHP_SELF'];
-
- $this->sql = $sql;
- $this->id = $id;
- $this->db = $db;
- $this->showPageLinks = $showPageLinks;
-
- $next_page = $id.'_next_page';
-
- if (isset($HTTP_GET_VARS[$next_page])) {
- $HTTP_SESSION_VARS[$curr_page] = $HTTP_GET_VARS[$next_page];
- }
- if (empty($HTTP_SESSION_VARS[$curr_page])) $HTTP_SESSION_VARS[$curr_page] = 1; ## at first page
-
- $this->curr_page = $HTTP_SESSION_VARS[$curr_page];
-
- }
-
- //---------------------------
- // Display link to first page
- function Render_First($anchor=true)
- {
- global $PHP_SELF;
- if ($anchor) {
- ?>
- first;?>
- first ";
- }
- }
-
- //--------------------------
- // Display link to next page
- function render_next($anchor=true)
- {
- global $PHP_SELF;
-
- if ($anchor) {
- ?>
- next;?>
- next ";
- }
- }
-
- //------------------
- // Link to last page
- //
- // for better performance with large recordsets, you can set
- // $this->db->pageExecuteCountRows = false, which disables
- // last page counting.
- function render_last($anchor=true)
- {
- global $PHP_SELF;
-
- if (!$this->db->pageExecuteCountRows) return;
-
- if ($anchor) {
- ?>
- last;?>
- last ";
- }
- }
-
- //---------------------------------------------------
- // original code by "Pablo Costa" ", - $header, - " |
", - $grid, - " |
", - $footer, - " |
|<
';
+ var $prev = '<<
';
+ var $next = '>>
';
+ var $last = '>|
';
+ var $moreLinks = '...';
+ var $startLinks = '...';
+ var $gridHeader = false;
+ var $htmlSpecialChars = true;
+ var $page = 'Page';
+ var $linkSelectedColor = 'red';
+ var $cache = 0; #secs to cache with CachePageExecute()
+
+ /* ---------------------------------------------- */
+ /* constructor */
+ /* */
+ /* $db adodb connection object */
+ /* $sql sql statement */
+ /* $id optional id to identify which pager, */
+ /* if you have multiple on 1 page. */
+ /* $id should be only be [a-z0-9]* */
+ /* */
+ function ADODB_Pager(&$db,$sql,$id = 'adodb', $showPageLinks = false)
+ {
+ global $HTTP_SERVER_VARS,$PHP_SELF,$HTTP_SESSION_VARS,$HTTP_GET_VARS;
+
+ $curr_page = $id.'_curr_page';
+ if (empty($PHP_SELF)) $PHP_SELF = $HTTP_SERVER_VARS['PHP_SELF'];
+
+ $this->sql = $sql;
+ $this->id = $id;
+ $this->db = $db;
+ $this->showPageLinks = $showPageLinks;
+
+ $next_page = $id.'_next_page';
+
+ if (isset($HTTP_GET_VARS[$next_page])) {
+ $HTTP_SESSION_VARS[$curr_page] = $HTTP_GET_VARS[$next_page];
+ }
+ if (empty($HTTP_SESSION_VARS[$curr_page])) $HTTP_SESSION_VARS[$curr_page] = 1; ## at first page
+
+ $this->curr_page = $HTTP_SESSION_VARS[$curr_page];
+
+ }
+
+ /* --------------------------- */
+ /* Display link to first page */
+ function Render_First($anchor=true)
+ {
+ global $PHP_SELF;
+ if ($anchor) {
+ ?>
+ first;?>
+ first ";
+ }
+ }
+
+ /* -------------------------- */
+ /* Display link to next page */
+ function render_next($anchor=true)
+ {
+ global $PHP_SELF;
+
+ if ($anchor) {
+ ?>
+ next;?>
+ next ";
+ }
+ }
+
+ /* ------------------ */
+ /* Link to last page */
+ /* */
+ /* for better performance with large recordsets, you can set */
+ /* $this->db->pageExecuteCountRows = false, which disables */
+ /* last page counting. */
+ function render_last($anchor=true)
+ {
+ global $PHP_SELF;
+
+ if (!$this->db->pageExecuteCountRows) return;
+
+ if ($anchor) {
+ ?>
+ last;?>
+ last ";
+ }
+ }
+
+ /* --------------------------------------------------- */
+ /* original code by "Pablo Costa" ", + $header, + " |
", + $grid, + " |
", + $footer, + " |
\$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}
"; + + + Installation + ============ + 1. Create this table in your database (syntax might vary depending on your db): + + create table sessions ( + SESSKEY char(32) not null, + EXPIRY int(11) unsigned not null, + EXPIREREF varchar(64), + DATA CLOB, + primary key (sesskey) + ); + + + 2. Then define the following parameters in this file: + $ADODB_SESSION_DRIVER='database driver, eg. mysql or ibase'; + $ADODB_SESSION_CONNECT='server to connect to'; + $ADODB_SESSION_USER ='user'; + $ADODB_SESSION_PWD ='password'; + $ADODB_SESSION_DB ='database'; + $ADODB_SESSION_TBL = 'sessions' + $ADODB_SESSION_USE_LOBS = false; (or, if you wanna use CLOBS (= 'CLOB') or ( = 'BLOB') + + 3. Recommended is PHP 4.0.6 or later. There are documented + session bugs in earlier versions of PHP. + + 4. If you want to receive notifications when a session expires, then + you can tag a session with an EXPIREREF, and before the session + record is deleted, we can call a function that will pass the EXPIREREF + as the first parameter, and the session key as the second parameter. + + To do this, define a notification function, say NotifyFn: + + function NotifyFn($expireref, $sesskey) + { + } + + Then define a global variable, with the first parameter being the + global variable you would like to store in the EXPIREREF field, and + the second is the function name. + + In this example, we want to be notified when a user's session + has expired, so we store the user id in $USERID, and make this + the value stored in the EXPIREREF field: + + $ADODB_SESSION_EXPIRE_NOTIFY = array('USERID','NotifyFn'); +*/ + +if (!defined('_ADODB_LAYER')) { + include (dirname(__FILE__).'/adodb.inc.php'); +} + +if (!defined('ADODB_SESSION')) { + + define('ADODB_SESSION',1); + + /* if database time and system time is difference is greater than this, then give warning */ + define('ADODB_SESSION_SYNCH_SECS',60); + +/****************************************************************************************\ + Global definitions +\****************************************************************************************/ +GLOBAL $ADODB_SESSION_CONNECT, + $ADODB_SESSION_DRIVER, + $ADODB_SESSION_USER, + $ADODB_SESSION_PWD, + $ADODB_SESSION_DB, + $ADODB_SESS_CONN, + $ADODB_SESS_LIFE, + $ADODB_SESS_DEBUG, + $ADODB_SESSION_EXPIRE_NOTIFY, + $ADODB_SESSION_CRC; + $ADODB_SESSION_USE_LOBS; + + + $ADODB_SESS_LIFE = ini_get('session.gc_maxlifetime'); + if ($ADODB_SESS_LIFE <= 1) { + /* bug in PHP 4.0.3 pl 1 -- how about other versions? */ + /* print "Session: connection failed
",false); +} + +/****************************************************************************************\ + Close the connection +\****************************************************************************************/ +function adodb_sess_close() +{ +global $ADODB_SESS_CONN; + + if ($ADODB_SESS_CONN) $ADODB_SESS_CONN->Close(); + return true; +} + +/****************************************************************************************\ + Slurp in the session variables and return the serialized string +\****************************************************************************************/ +function adodb_sess_read($key) +{ +global $ADODB_SESS_CONN,$ADODB_SESSION_TBL,$ADODB_SESSION_CRC; + + $rs = $ADODB_SESS_CONN->Execute("SELECT data FROM $ADODB_SESSION_TBL WHERE sesskey = '$key' AND expiry >= " . time()); + if ($rs) { + if ($rs->EOF) { + $v = ''; + } else + $v = rawurldecode(reset($rs->fields)); + + $rs->Close(); + + /* new optimization adodb 2.1 */ + $ADODB_SESSION_CRC = strlen($v).crc32($v); + + return $v; + } + + return ''; /* thx to Jorma Tuomainen, webmaster#wizactive.com */ +} + +/****************************************************************************************\ + Write the serialized data to a database. + + If the data has not been modified since adodb_sess_read(), we do not write. +\****************************************************************************************/ +function adodb_sess_write($key, $val) +{ + global + $ADODB_SESS_CONN, + $ADODB_SESS_LIFE, + $ADODB_SESSION_TBL, + $ADODB_SESS_DEBUG, + $ADODB_SESSION_CRC, + $ADODB_SESSION_EXPIRE_NOTIFY, + $ADODB_SESSION_DRIVER, /* added */ + $ADODB_SESSION_USE_LOBS; /* added */ + + $expiry = time() + $ADODB_SESS_LIFE; + + /* crc32 optimization since adodb 2.1 */ + /* now we only update expiry date, thx to sebastian thom in adodb 2.32 */ + if ($ADODB_SESSION_CRC !== false && $ADODB_SESSION_CRC == strlen($val).crc32($val)) { + if ($ADODB_SESS_DEBUG) echo "Session: Only updating date - crc32 not changed
"; + $qry = "UPDATE $ADODB_SESSION_TBL SET expiry=$expiry WHERE sesskey='$key' AND expiry >= " . time(); + $rs = $ADODB_SESS_CONN->Execute($qry); + return true; + } + $val = rawurlencode($val); + + $arr = array('sesskey' => $key, 'expiry' => $expiry, 'data' => $val); + if ($ADODB_SESSION_EXPIRE_NOTIFY) { + $var = reset($ADODB_SESSION_EXPIRE_NOTIFY); + global $$var; + $arr['expireref'] = $$var; + } + + + if ($ADODB_SESSION_USE_LOBS === false) { /* no lobs, simply use replace() */ + $rs = $ADODB_SESS_CONN->Replace($ADODB_SESSION_TBL,$arr, 'sesskey',$autoQuote = true); + if (!$rs) { + $err = $ADODB_SESS_CONN->ErrorMsg(); + } + } else { + /* what value shall we insert/update for lob row? */ + switch ($ADODB_SESSION_DRIVER) { + /* empty_clob or empty_lob for oracle dbs */ + case "oracle": + case "oci8": + case "oci8po": + case "oci805": + $lob_value = sprintf("empty_%s()", strtolower($ADODB_SESSION_USE_LOBS)); + break; + + /* null for all other */ + default: + $lob_value = "null"; + break; + } + + /* do we insert or update? => as for sesskey */ + $res = $ADODB_SESS_CONN->Execute("select count(*) as cnt from $ADODB_SESSION_TBL where sesskey = '$key'"); + if ($res && ($res->fields["CNT"] > 0)) { + $qry = sprintf("update %s set expiry = %d, data = %s where sesskey = '%s'", $ADODB_SESSION_TBL, $expiry, $lob_value, $key); + } else { + /* insert */ + $qry = sprintf("insert into %s (sesskey, expiry, data) values ('%s', %d, %s)", $ADODB_SESSION_TBL, $key, $expiry, $lob_value); + } + + $err = ""; + $rs1 = $ADODB_SESS_CONN->Execute($qry); + if (!$rs1) { + $err .= $ADODB_SESS_CONN->ErrorMsg()."\n"; + } + $rs2 = $ADODB_SESS_CONN->UpdateBlob($ADODB_SESSION_TBL, 'data', $val, "sesskey='$key'", strtoupper($ADODB_SESSION_USE_LOBS)); + if (!$rs2) { + $err .= $ADODB_SESS_CONN->ErrorMsg()."\n"; + } + $rs = ($rs1 && $rs2) ? true : false; + } + + if (!$rs) { + ADOConnection::outp( 'Session Replace: '.nl2br($err).'
',false); + } else { + /* bug in access driver (could be odbc?) means that info is not commited */ + /* properly unless select statement executed in Win2000 */ + if ($ADODB_SESS_CONN->databaseType == 'access') + $rs = $ADODB_SESS_CONN->Execute("select sesskey from $ADODB_SESSION_TBL WHERE sesskey='$key'"); + } + return !empty($rs); +} + +function adodb_sess_destroy($key) +{ + global $ADODB_SESS_CONN, $ADODB_SESSION_TBL,$ADODB_SESSION_EXPIRE_NOTIFY; + + if ($ADODB_SESSION_EXPIRE_NOTIFY) { + reset($ADODB_SESSION_EXPIRE_NOTIFY); + $fn = next($ADODB_SESSION_EXPIRE_NOTIFY); + $savem = $ADODB_SESS_CONN->SetFetchMode(ADODB_FETCH_NUM); + $rs = $ADODB_SESS_CONN->Execute("SELECT expireref,sesskey FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); + $ADODB_SESS_CONN->SetFetchMode($savem); + if ($rs) { + $ADODB_SESS_CONN->BeginTrans(); + while (!$rs->EOF) { + $ref = $rs->fields[0]; + $key = $rs->fields[1]; + $fn($ref,$key); + $del = $ADODB_SESS_CONN->Execute("DELETE FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); + $rs->MoveNext(); + } + $ADODB_SESS_CONN->CommitTrans(); + } + } else { + $qry = "DELETE FROM $ADODB_SESSION_TBL WHERE sesskey = '$key'"; + $rs = $ADODB_SESS_CONN->Execute($qry); + } + return $rs ? true : false; +} + +function adodb_sess_gc($maxlifetime) +{ + global $ADODB_SESS_DEBUG, $ADODB_SESS_CONN, $ADODB_SESSION_TBL,$ADODB_SESSION_EXPIRE_NOTIFY; + + if ($ADODB_SESSION_EXPIRE_NOTIFY) { + reset($ADODB_SESSION_EXPIRE_NOTIFY); + $fn = next($ADODB_SESSION_EXPIRE_NOTIFY); + $savem = $ADODB_SESS_CONN->SetFetchMode(ADODB_FETCH_NUM); + $rs = $ADODB_SESS_CONN->Execute("SELECT expireref,sesskey FROM $ADODB_SESSION_TBL WHERE expiry < " . time()); + $ADODB_SESS_CONN->SetFetchMode($savem); + if ($rs) { + $ADODB_SESS_CONN->BeginTrans(); + while (!$rs->EOF) { + $ref = $rs->fields[0]; + $key = $rs->fields[1]; + $fn($ref,$key); + $del = $ADODB_SESS_CONN->Execute("DELETE FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); + $rs->MoveNext(); + } + $ADODB_SESS_CONN->CommitTrans(); + } + } else { + $qry = "DELETE FROM $ADODB_SESSION_TBL WHERE expiry < " . time(); + $ADODB_SESS_CONN->Execute($qry); + + if ($ADODB_SESS_DEBUG) ADOConnection::outp("Garbage Collection: $qry
"); + } + /* suggested by Cameron, "GaM3R"$msg
"); + } + } + + return true; +} + +session_module_name('user'); +session_set_save_handler( + "adodb_sess_open", + "adodb_sess_close", + "adodb_sess_read", + "adodb_sess_write", + "adodb_sess_destroy", + "adodb_sess_gc"); +} + +/* TEST SCRIPT -- UNCOMMENT */ + +if (0) { +GLOBAL $HTTP_SESSION_VARS; + + session_start(); + session_register('AVAR'); + $HTTP_SESSION_VARS['AVAR'] += 1; + ADOConnection::outp( "\$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}
",false); +} + +?> diff --git a/lib/adodb/adodb-session.php b/lib/adodb/adodb-session.php index 67b61e7583..d808b06d7c 100644 --- a/lib/adodb/adodb-session.php +++ b/lib/adodb/adodb-session.php @@ -1,379 +1,379 @@ -\$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}"; - -To force non-persistent connections, call adodb_session_open first before session_start(): - - GLOBAL $HTTP_SESSION_VARS; - include('adodb.inc.php'); - include('adodb-session.php'); - adodb_session_open(false,false,false); - session_start(); - session_register('AVAR'); - $HTTP_SESSION_VARS['AVAR'] += 1; - print "\$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}
"; - - - Installation - ============ - 1. Create this table in your database (syntax might vary depending on your db): - - create table sessions ( - SESSKEY char(32) not null, - EXPIRY int(11) unsigned not null, - EXPIREREF varchar(64), - DATA text not null, - primary key (sesskey) - ); - - - 2. Then define the following parameters in this file: - $ADODB_SESSION_DRIVER='database driver, eg. mysql or ibase'; - $ADODB_SESSION_CONNECT='server to connect to'; - $ADODB_SESSION_USER ='user'; - $ADODB_SESSION_PWD ='password'; - $ADODB_SESSION_DB ='database'; - $ADODB_SESSION_TBL = 'sessions' - - 3. Recommended is PHP 4.0.6 or later. There are documented - session bugs in earlier versions of PHP. - - 4. If you want to receive notifications when a session expires, then - you can tag a session with an EXPIREREF, and before the session - record is deleted, we can call a function that will pass the EXPIREREF - as the first parameter, and the session key as the second parameter. - - To do this, define a notification function, say NotifyFn: - - function NotifyFn($expireref, $sesskey) - { - } - - Then define a global variable, with the first parameter being the - global variable you would like to store in the EXPIREREF field, and - the second is the function name. - - In this example, we want to be notified when a user's session - has expired, so we store the user id in $USERID, and make this - the value stored in the EXPIREREF field: - - $ADODB_SESSION_EXPIRE_NOTIFY = array('USERID','NotifyFn'); -*/ - -if (!defined('_ADODB_LAYER')) { - include (dirname(__FILE__).'/adodb.inc.php'); -} - -if (!defined('ADODB_SESSION')) { - - define('ADODB_SESSION',1); - - /* if database time and system time is difference is greater than this, then give warning */ - define('ADODB_SESSION_SYNCH_SECS',60); - -/****************************************************************************************\ - Global definitions -\****************************************************************************************/ -GLOBAL $ADODB_SESSION_CONNECT, - $ADODB_SESSION_DRIVER, - $ADODB_SESSION_USER, - $ADODB_SESSION_PWD, - $ADODB_SESSION_DB, - $ADODB_SESS_CONN, - $ADODB_SESS_LIFE, - $ADODB_SESS_DEBUG, - $ADODB_SESSION_EXPIRE_NOTIFY, - $ADODB_SESSION_CRC; - - - $ADODB_SESS_LIFE = ini_get('session.gc_maxlifetime'); - if ($ADODB_SESS_LIFE <= 1) { - // bug in PHP 4.0.3 pl 1 -- how about other versions? - //print "Session: connection failed
",false); -} - -/****************************************************************************************\ - Close the connection -\****************************************************************************************/ -function adodb_sess_close() -{ -global $ADODB_SESS_CONN; - - if ($ADODB_SESS_CONN) $ADODB_SESS_CONN->Close(); - return true; -} - -/****************************************************************************************\ - Slurp in the session variables and return the serialized string -\****************************************************************************************/ -function adodb_sess_read($key) -{ -global $ADODB_SESS_CONN,$ADODB_SESSION_TBL,$ADODB_SESSION_CRC; - - $rs = $ADODB_SESS_CONN->Execute("SELECT data FROM $ADODB_SESSION_TBL WHERE sesskey = '$key' AND expiry >= " . time()); - if ($rs) { - if ($rs->EOF) { - $v = ''; - } else - $v = rawurldecode(reset($rs->fields)); - - $rs->Close(); - - // new optimization adodb 2.1 - $ADODB_SESSION_CRC = strlen($v).crc32($v); - - return $v; - } - - return ''; // thx to Jorma Tuomainen, webmaster#wizactive.com -} - -/****************************************************************************************\ - Write the serialized data to a database. - - If the data has not been modified since adodb_sess_read(), we do not write. -\****************************************************************************************/ -function adodb_sess_write($key, $val) -{ - global - $ADODB_SESS_CONN, - $ADODB_SESS_LIFE, - $ADODB_SESSION_TBL, - $ADODB_SESS_DEBUG, - $ADODB_SESSION_CRC, - $ADODB_SESSION_EXPIRE_NOTIFY; - - $expiry = time() + $ADODB_SESS_LIFE; - - // crc32 optimization since adodb 2.1 - // now we only update expiry date, thx to sebastian thom in adodb 2.32 - if ($ADODB_SESSION_CRC !== false && $ADODB_SESSION_CRC == strlen($val).crc32($val)) { - if ($ADODB_SESS_DEBUG) echo "Session: Only updating date - crc32 not changed
"; - $qry = "UPDATE $ADODB_SESSION_TBL SET expiry=$expiry WHERE sesskey='$key' AND expiry >= " . time(); - $rs = $ADODB_SESS_CONN->Execute($qry); - return true; - } - $val = rawurlencode($val); - - $arr = array('sesskey' => $key, 'expiry' => $expiry, 'data' => $val); - if ($ADODB_SESSION_EXPIRE_NOTIFY) { - $var = reset($ADODB_SESSION_EXPIRE_NOTIFY); - global $$var; - $arr['expireref'] = $$var; - } - $rs = $ADODB_SESS_CONN->Replace($ADODB_SESSION_TBL,$arr, - 'sesskey',$autoQuote = true); - - if (!$rs) { - ADOConnection::outp( 'Session Replace: '.$ADODB_SESS_CONN->ErrorMsg().'
',false); - } else { - // bug in access driver (could be odbc?) means that info is not commited - // properly unless select statement executed in Win2000 - if ($ADODB_SESS_CONN->databaseType == 'access') - $rs = $ADODB_SESS_CONN->Execute("select sesskey from $ADODB_SESSION_TBL WHERE sesskey='$key'"); - } - return !empty($rs); -} - -function adodb_sess_destroy($key) -{ - global $ADODB_SESS_CONN, $ADODB_SESSION_TBL,$ADODB_SESSION_EXPIRE_NOTIFY; - - if ($ADODB_SESSION_EXPIRE_NOTIFY) { - reset($ADODB_SESSION_EXPIRE_NOTIFY); - $fn = next($ADODB_SESSION_EXPIRE_NOTIFY); - $savem = $ADODB_SESS_CONN->SetFetchMode(ADODB_FETCH_NUM); - $rs = $ADODB_SESS_CONN->Execute("SELECT expireref,sesskey FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); - $ADODB_SESS_CONN->SetFetchMode($savem); - if ($rs) { - $ADODB_SESS_CONN->BeginTrans(); - while (!$rs->EOF) { - $ref = $rs->fields[0]; - $key = $rs->fields[1]; - $fn($ref,$key); - $del = $ADODB_SESS_CONN->Execute("DELETE FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); - $rs->MoveNext(); - } - $ADODB_SESS_CONN->CommitTrans(); - } - } else { - $qry = "DELETE FROM $ADODB_SESSION_TBL WHERE sesskey = '$key'"; - $rs = $ADODB_SESS_CONN->Execute($qry); - } - return $rs ? true : false; -} - -function adodb_sess_gc($maxlifetime) -{ - global $ADODB_SESS_DEBUG, $ADODB_SESS_CONN, $ADODB_SESSION_TBL,$ADODB_SESSION_EXPIRE_NOTIFY; - - if ($ADODB_SESSION_EXPIRE_NOTIFY) { - reset($ADODB_SESSION_EXPIRE_NOTIFY); - $fn = next($ADODB_SESSION_EXPIRE_NOTIFY); - $savem = $ADODB_SESS_CONN->SetFetchMode(ADODB_FETCH_NUM); - $rs = $ADODB_SESS_CONN->Execute("SELECT expireref,sesskey FROM $ADODB_SESSION_TBL WHERE expiry < " . time()); - $ADODB_SESS_CONN->SetFetchMode($savem); - if ($rs) { - $ADODB_SESS_CONN->BeginTrans(); - while (!$rs->EOF) { - $ref = $rs->fields[0]; - $key = $rs->fields[1]; - $fn($ref,$key); - $del = $ADODB_SESS_CONN->Execute("DELETE FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); - $rs->MoveNext(); - } - $ADODB_SESS_CONN->CommitTrans(); - } - } else { - $qry = "DELETE FROM $ADODB_SESSION_TBL WHERE expiry < " . time(); - $ADODB_SESS_CONN->Execute($qry); - - if ($ADODB_SESS_DEBUG) ADOConnection::outp("Garbage Collection: $qry
"); - } - // suggested by Cameron, "GaM3R"$msg
"); - } - } - - return true; -} - -session_module_name('user'); -session_set_save_handler( - "adodb_sess_open", - "adodb_sess_close", - "adodb_sess_read", - "adodb_sess_write", - "adodb_sess_destroy", - "adodb_sess_gc"); -} - -/* TEST SCRIPT -- UNCOMMENT */ - -if (0) { -GLOBAL $HTTP_SESSION_VARS; - - session_start(); - session_register('AVAR'); - $HTTP_SESSION_VARS['AVAR'] += 1; - ADOConnection::outp( "\$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}
",false); -} - +\$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}"; + +To force non-persistent connections, call adodb_session_open first before session_start(): + + GLOBAL $HTTP_SESSION_VARS; + include('adodb.inc.php'); + include('adodb-session.php'); + adodb_sess_open(false,false,false); + session_start(); + session_register('AVAR'); + $HTTP_SESSION_VARS['AVAR'] += 1; + print "\$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}
"; + + + Installation + ============ + 1. Create this table in your database (syntax might vary depending on your db): + + create table sessions ( + SESSKEY char(32) not null, + EXPIRY int(11) unsigned not null, + EXPIREREF varchar(64), + DATA text not null, + primary key (sesskey) + ); + + + 2. Then define the following parameters in this file: + $ADODB_SESSION_DRIVER='database driver, eg. mysql or ibase'; + $ADODB_SESSION_CONNECT='server to connect to'; + $ADODB_SESSION_USER ='user'; + $ADODB_SESSION_PWD ='password'; + $ADODB_SESSION_DB ='database'; + $ADODB_SESSION_TBL = 'sessions' + + 3. Recommended is PHP 4.0.6 or later. There are documented + session bugs in earlier versions of PHP. + + 4. If you want to receive notifications when a session expires, then + you can tag a session with an EXPIREREF, and before the session + record is deleted, we can call a function that will pass the EXPIREREF + as the first parameter, and the session key as the second parameter. + + To do this, define a notification function, say NotifyFn: + + function NotifyFn($expireref, $sesskey) + { + } + + Then define a global variable, with the first parameter being the + global variable you would like to store in the EXPIREREF field, and + the second is the function name. + + In this example, we want to be notified when a user's session + has expired, so we store the user id in $USERID, and make this + the value stored in the EXPIREREF field: + + $ADODB_SESSION_EXPIRE_NOTIFY = array('USERID','NotifyFn'); +*/ + +if (!defined('_ADODB_LAYER')) { + include (dirname(__FILE__).'/adodb.inc.php'); +} + +if (!defined('ADODB_SESSION')) { + + define('ADODB_SESSION',1); + + /* if database time and system time is difference is greater than this, then give warning */ + define('ADODB_SESSION_SYNCH_SECS',60); + +/****************************************************************************************\ + Global definitions +\****************************************************************************************/ +GLOBAL $ADODB_SESSION_CONNECT, + $ADODB_SESSION_DRIVER, + $ADODB_SESSION_USER, + $ADODB_SESSION_PWD, + $ADODB_SESSION_DB, + $ADODB_SESS_CONN, + $ADODB_SESS_LIFE, + $ADODB_SESS_DEBUG, + $ADODB_SESSION_EXPIRE_NOTIFY, + $ADODB_SESSION_CRC; + + + $ADODB_SESS_LIFE = ini_get('session.gc_maxlifetime'); + if ($ADODB_SESS_LIFE <= 1) { + /* bug in PHP 4.0.3 pl 1 -- how about other versions? */ + /* print "Session: connection failed
",false); +} + +/****************************************************************************************\ + Close the connection +\****************************************************************************************/ +function adodb_sess_close() +{ +global $ADODB_SESS_CONN; + + if ($ADODB_SESS_CONN) $ADODB_SESS_CONN->Close(); + return true; +} + +/****************************************************************************************\ + Slurp in the session variables and return the serialized string +\****************************************************************************************/ +function adodb_sess_read($key) +{ +global $ADODB_SESS_CONN,$ADODB_SESSION_TBL,$ADODB_SESSION_CRC; + + $rs = $ADODB_SESS_CONN->Execute("SELECT data FROM $ADODB_SESSION_TBL WHERE sesskey = '$key' AND expiry >= " . time()); + if ($rs) { + if ($rs->EOF) { + $v = ''; + } else + $v = rawurldecode(reset($rs->fields)); + + $rs->Close(); + + /* new optimization adodb 2.1 */ + $ADODB_SESSION_CRC = strlen($v).crc32($v); + + return $v; + } + + return ''; /* thx to Jorma Tuomainen, webmaster#wizactive.com */ +} + +/****************************************************************************************\ + Write the serialized data to a database. + + If the data has not been modified since adodb_sess_read(), we do not write. +\****************************************************************************************/ +function adodb_sess_write($key, $val) +{ + global + $ADODB_SESS_CONN, + $ADODB_SESS_LIFE, + $ADODB_SESSION_TBL, + $ADODB_SESS_DEBUG, + $ADODB_SESSION_CRC, + $ADODB_SESSION_EXPIRE_NOTIFY; + + $expiry = time() + $ADODB_SESS_LIFE; + + /* crc32 optimization since adodb 2.1 */ + /* now we only update expiry date, thx to sebastian thom in adodb 2.32 */ + if ($ADODB_SESSION_CRC !== false && $ADODB_SESSION_CRC == strlen($val).crc32($val)) { + if ($ADODB_SESS_DEBUG) echo "Session: Only updating date - crc32 not changed
"; + $qry = "UPDATE $ADODB_SESSION_TBL SET expiry=$expiry WHERE sesskey='$key' AND expiry >= " . time(); + $rs = $ADODB_SESS_CONN->Execute($qry); + return true; + } + $val = rawurlencode($val); + + $arr = array('sesskey' => $key, 'expiry' => $expiry, 'data' => $val); + if ($ADODB_SESSION_EXPIRE_NOTIFY) { + $var = reset($ADODB_SESSION_EXPIRE_NOTIFY); + global $$var; + $arr['expireref'] = $$var; + } + $rs = $ADODB_SESS_CONN->Replace($ADODB_SESSION_TBL,$arr, + 'sesskey',$autoQuote = true); + + if (!$rs) { + ADOConnection::outp( 'Session Replace: '.$ADODB_SESS_CONN->ErrorMsg().'
',false); + } else { + /* bug in access driver (could be odbc?) means that info is not commited */ + /* properly unless select statement executed in Win2000 */ + if ($ADODB_SESS_CONN->databaseType == 'access') + $rs = $ADODB_SESS_CONN->Execute("select sesskey from $ADODB_SESSION_TBL WHERE sesskey='$key'"); + } + return !empty($rs); +} + +function adodb_sess_destroy($key) +{ + global $ADODB_SESS_CONN, $ADODB_SESSION_TBL,$ADODB_SESSION_EXPIRE_NOTIFY; + + if ($ADODB_SESSION_EXPIRE_NOTIFY) { + reset($ADODB_SESSION_EXPIRE_NOTIFY); + $fn = next($ADODB_SESSION_EXPIRE_NOTIFY); + $savem = $ADODB_SESS_CONN->SetFetchMode(ADODB_FETCH_NUM); + $rs = $ADODB_SESS_CONN->Execute("SELECT expireref,sesskey FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); + $ADODB_SESS_CONN->SetFetchMode($savem); + if ($rs) { + $ADODB_SESS_CONN->BeginTrans(); + while (!$rs->EOF) { + $ref = $rs->fields[0]; + $key = $rs->fields[1]; + $fn($ref,$key); + $del = $ADODB_SESS_CONN->Execute("DELETE FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); + $rs->MoveNext(); + } + $ADODB_SESS_CONN->CommitTrans(); + } + } else { + $qry = "DELETE FROM $ADODB_SESSION_TBL WHERE sesskey = '$key'"; + $rs = $ADODB_SESS_CONN->Execute($qry); + } + return $rs ? true : false; +} + +function adodb_sess_gc($maxlifetime) +{ + global $ADODB_SESS_DEBUG, $ADODB_SESS_CONN, $ADODB_SESSION_TBL,$ADODB_SESSION_EXPIRE_NOTIFY; + + if ($ADODB_SESSION_EXPIRE_NOTIFY) { + reset($ADODB_SESSION_EXPIRE_NOTIFY); + $fn = next($ADODB_SESSION_EXPIRE_NOTIFY); + $savem = $ADODB_SESS_CONN->SetFetchMode(ADODB_FETCH_NUM); + $rs = $ADODB_SESS_CONN->Execute("SELECT expireref,sesskey FROM $ADODB_SESSION_TBL WHERE expiry < " . time()); + $ADODB_SESS_CONN->SetFetchMode($savem); + if ($rs) { + $ADODB_SESS_CONN->BeginTrans(); + while (!$rs->EOF) { + $ref = $rs->fields[0]; + $key = $rs->fields[1]; + $fn($ref,$key); + $del = $ADODB_SESS_CONN->Execute("DELETE FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); + $rs->MoveNext(); + } + $ADODB_SESS_CONN->CommitTrans(); + } + } else { + $qry = "DELETE FROM $ADODB_SESSION_TBL WHERE expiry < " . time(); + $ADODB_SESS_CONN->Execute($qry); + + if ($ADODB_SESS_DEBUG) ADOConnection::outp("Garbage Collection: $qry
"); + } + /* suggested by Cameron, "GaM3R"$msg
"); + } + } + + return true; +} + +session_module_name('user'); +session_set_save_handler( + "adodb_sess_open", + "adodb_sess_close", + "adodb_sess_read", + "adodb_sess_write", + "adodb_sess_destroy", + "adodb_sess_gc"); +} + +/* TEST SCRIPT -- UNCOMMENT */ + +if (0) { +GLOBAL $HTTP_SESSION_VARS; + + session_start(); + session_register('AVAR'); + $HTTP_SESSION_VARS['AVAR'] += 1; + ADOConnection::outp( "\$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}
",false); +} + ?> \ No newline at end of file diff --git a/lib/adodb/adodb-time.inc.php b/lib/adodb/adodb-time.inc.php index 97faaf801f..5a0f6cac36 100644 --- a/lib/adodb/adodb-time.inc.php +++ b/lib/adodb/adodb-time.inc.php @@ -1,5 +1,4 @@ $y1 errorTesting gregorian <=> julian conversion
";
$t = adodb_mktime(0,0,0,10,11,1492);
- //http://www.holidayorigins.com/html/columbus_day.html - Friday check
+ /* http://www.holidayorigins.com/html/columbus_day.html - Friday check */
if (!(adodb_date('D Y-m-d',$t) == 'Fri 1492-10-11')) print 'Error in Columbus landing
';
$t = adodb_mktime(0,0,0,2,29,1500);
@@ -303,7 +302,7 @@ function adodb_date_test()
if (adodb_year_digit_check(50) != 1950) print "Err 2-digit 1950
";
if (adodb_year_digit_check(90) != 1990) print "Err 2-digit 1990
";
- // Test string formating
+ /* Test string formating */
print "
Testing date formating
"; $fmt = '\d\a\t\e T Y-m-d H:i:s a A d D F g G h H i j l L m M n O \R\F\C822 r s t U w y Y z Z 2003'; $s1 = date($fmt,0); @@ -317,7 +316,7 @@ function adodb_date_test() $ts = 3600.0*((rand()%60000)+(rand()%60000))+(rand()%60000); $s1 = date($fmt,$ts); $s2 = adodb_date($fmt,$ts); - //print "$s1";
+ /* print "$s1
$s2
"; */ $pos = strcmp($s1,$s2); if (($s1) != ($s2)) { @@ -346,7 +345,7 @@ function adodb_date_test() } } - // Test generation of dates outside 1901-2038 + /* Test generation of dates outside 1901-2038 */ print "
Testing random dates between 100 and 4000
"; adodb_date_test_date(100,1); for ($i=100; --$i >= 0;) { @@ -365,8 +364,8 @@ function adodb_date_test() $max = 365*$yrs*86400; $lastyear = 0; - // we generate a timestamp, convert it to a date, and convert it back to a timestamp - // and check if the roundtrip broke the original timestamp value. + /* we generate a timestamp, convert it to a date, and convert it back to a timestamp */ + /* and check if the roundtrip broke the original timestamp value. */ print "Testing $start to ".($start+$yrs).", or $max seconds, offset=$offset: "; for ($max += $i; $i < $max; $i += $offset) { @@ -437,7 +436,7 @@ function _adodb_is_leap_year($year) if ($year % 400 == 0) { return true; - // if gregorian calendar (>1582), century not-divisible by 400 is not leap + /* if gregorian calendar (>1582), century not-divisible by 400 is not leap */ } else if ($year > 1582 && $year % 100 == 0 ) { return false; } @@ -472,8 +471,8 @@ function adodb_year_digit_check($y) $c0 = $century - 1; } $c1 *= 100; - // if 2-digit year is less than 30 years in future, set it to this century - // otherwise if more than 30 years in future, then we set 2-digit year to the prev century. + /* if 2-digit year is less than 30 years in future, set it to this century */ + /* otherwise if more than 30 years in future, then we set 2-digit year to the prev century. */ if (($y + $c1) < $yr+30) $y = $y + $c1; else $y = $y + $c0*100; } @@ -499,8 +498,8 @@ function adodb_getdate($d=false,$fast=false) { if ($d === false) return getdate(); if (!defined('ADODB_TEST_DATES')) { - if ((abs($d) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range - if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0) // if windows, must be +ve integer + if ((abs($d) <= 0x7FFFFFFF)) { /* check if number in 32-bit signed range */ + if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0) /* if windows, must be +ve integer */ return @getdate($d); } } @@ -520,15 +519,15 @@ function _adodb_getdate($origd=false,$fast=false,$is_gmt=false) $_hour_power = 3600; $_min_power = 60; - if ($d < -12219321600) $d -= 86400*10; // if 15 Oct 1582 or earlier, gregorian correction + if ($d < -12219321600) $d -= 86400*10; /* if 15 Oct 1582 or earlier, gregorian correction */ $_month_table_normal = array("",31,28,31,30,31,30,31,31,30,31,30,31); $_month_table_leaf = array("",31,29,31,30,31,30,31,31,30,31,30,31); if ($d < 0) { $origd = $d; - // The valid range of a 32bit signed timestamp is typically from - // Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT + /* The valid range of a 32bit signed timestamp is typically from */ + /* Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT */ for ($a = 1970 ; --$a >= 0;) { $lastd = $d; @@ -642,8 +641,8 @@ function adodb_date($fmt,$d=false,$is_gmt=false) { if ($d === false) return date($fmt); if (!defined('ADODB_TEST_DATES')) { - if ((abs($d) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range - if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0) // if windows, must be +ve integer + if ((abs($d) <= 0x7FFFFFFF)) { /* check if number in 32-bit signed range */ + if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0) /* if windows, must be +ve integer */ return @date($fmt,$d); } } @@ -666,10 +665,10 @@ function adodb_date($fmt,$d=false,$is_gmt=false) */ for ($i=0; $i < $max; $i++) { switch($fmt[$i]) { - case 'T': $dates .= date('T',100000);break; - // YEAR + case 'T': $dates .= date('T');break; + /* YEAR */ case 'L': $dates .= $arr['leap'] ? '1' : '0'; break; - case 'r': // Thu, 21 Dec 2000 16:01:07 +0200 + case 'r': /* Thu, 21 Dec 2000 16:01:07 +0200 */ $dates .= gmdate('D',$_day_power*(3+adodb_dow($year,$month,$day))).', ' . ($day<10?' '.$day:$day) . ' '.date('M',mktime(0,0,0,$month,2,1971)).' '.$year.' '; @@ -685,12 +684,12 @@ function adodb_date($fmt,$d=false,$is_gmt=false) case 'Y': $dates .= $year; break; case 'y': $dates .= substr($year,strlen($year)-2,2); break; - // MONTH + /* MONTH */ case 'm': if ($month<10) $dates .= '0'.$month; else $dates .= $month; break; case 'n': $dates .= $month; break; case 'M': $dates .= date('M',mktime(0,0,0,$month,2,1971)); break; case 'F': $dates .= date('F',mktime(0,0,0,$month,2,1971)); break; - // DAY + /* DAY */ case 't': $dates .= $arr['ndays']; break; case 'z': $dates .= $arr['yday']; break; case 'w': $dates .= adodb_dow($year,$month,$day); break; @@ -706,7 +705,7 @@ function adodb_date($fmt,$d=false,$is_gmt=false) else $dates .= 'th'; break; - // HOUR + /* HOUR */ case 'Z': $dates .= ($is_gmt) ? 0 : -adodb_get_gmt_different(); break; case 'O': @@ -740,13 +739,13 @@ function adodb_date($fmt,$d=false,$is_gmt=false) } $dates .= $hh; break; - // MINUTES + /* MINUTES */ case 'i': if ($min < 10) $dates .= '0'.$min; else $dates .= $min; break; - // SECONDS + /* SECONDS */ case 'U': $dates .= $d; break; case 's': if ($secs < 10) $dates .= '0'.$secs; else $dates .= $secs; break; - // AM/PM - // Note 00:00 to 11:59 is AM, while 12:00 to 23:59 is PM + /* AM/PM */ + /* Note 00:00 to 11:59 is AM, while 12:00 to 23:59 is PM */ case 'a': if ($hour>=12) $dates .= 'pm'; else $dates .= 'am'; @@ -757,7 +756,7 @@ function adodb_date($fmt,$d=false,$is_gmt=false) break; default: $dates .= $fmt[$i]; break; - // ESCAPE + /* ESCAPE */ case "\\": $i++; if ($i < $max) $dates .= $fmt[$i]; @@ -781,9 +780,10 @@ function adodb_gmmktime($hr,$min,$sec,$mon,$day,$year,$is_dst=false) Note that $is_dst is not implemented and is ignored. */ function adodb_mktime($hr,$min,$sec,$mon,$day,$year,$is_dst=false,$is_gmt=false) -{ if (!defined('ADODB_TEST_DATES')) { - // for windows, we don't check 1970 because with timezone differences, - // 1 Jan 1970 could generate negative timestamp, which is illegal +{ + if (!defined('ADODB_TEST_DATES')) { + /* for windows, we don't check 1970 because with timezone differences, */ + /* 1 Jan 1970 could generate negative timestamp, which is illegal */ if (!defined('ADODB_NO_NEGATIVE_TS') || ($year >= 1971)) if (1901 < $year && $year < 2038) return @mktime($hr,$min,$sec,$mon,$day,$year); @@ -858,10 +858,10 @@ function adodb_mktime($hr,$min,$sec,$mon,$day,$year,$is_dst=false,$is_gmt=false) $_day_time = $hr * $_hour_power + $min * $_min_power + $sec; $_day_time = $_day_power - $_day_time; $ret = -( $_total_date * $_day_power + $_day_time - $gmt_different); - if ($ret < -12220185600) $ret += 10*86400; // if earlier than 5 Oct 1582 - gregorian correction - else if ($ret < -12219321600) $ret = -12219321600; // if in limbo, reset to 15 Oct 1582. + if ($ret < -12220185600) $ret += 10*86400; /* if earlier than 5 Oct 1582 - gregorian correction */ + else if ($ret < -12219321600) $ret = -12219321600; /* if in limbo, reset to 15 Oct 1582. */ } - //print " dmy=$day/$mon/$year $hr:$min:$sec => " .$ret; + /* print " dmy=$day/$mon/$year $hr:$min:$sec => " .$ret; */ return $ret; } diff --git a/lib/adodb/adodb-xmlschema.inc.php b/lib/adodb/adodb-xmlschema.inc.php new file mode 100644 index 0000000000..9debc392e9 --- /dev/null +++ b/lib/adodb/adodb-xmlschema.inc.php @@ -0,0 +1,722 @@ +tableName = $name; + } + + /** + * 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 ) { + + /* Set the field index so we know where we are */ + $this->currentField = $name; + + /* Set the field type (required) */ + $this->fieldSpec[$name]['TYPE'] = $type; + + /* Set the field size (optional) */ + if( isset( $size ) ) { + $this->fieldSpec[$name]['SIZE'] = $size; + } + + /* Set the field options */ + if( isset( $opts ) ) $this->fieldSpec[$name]['OPTS'] = $opts; + + /* Return array containing field specifier */ + return $this->fieldSpec; + } + + /** + * 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 ) { + + /* Add the option to the field specifier */ + if( $value === NULL ) { /* No value, so add only the option */ + $this->fieldSpec[$field]['OPTS'][] = $opt; + } else { /* Add the option and value */ + $this->fieldSpec[$field]['OPTS'][] = array( "$opt" => "$value" ); + } + + /* Return array containing field specifier */ + return $this->fieldSpec; + } + + /** + * 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 string Option list + */ + function addTableOpt( $opt ) { + + $optlist = &$this->tableOpts; + $optlist ? $optlist .= ", $opt" : $optlist = $opt; + + /* Return the options list */ + return $optlist; + } + + /** + * Generates the SQL that will create the table in the database + * + * Returns SQL that will create the table represented by the object. + * + * @param object $dict ADOdb data dictionary + * @return array Array containing table creation SQL + */ + function create( $dict ) { + + /* Loop through the field specifier array, building the associative array for the field options */ + $fldarray = array(); + $i = 0; + + foreach( $this->fieldSpec as $field => $finfo ) { + $i++; + + /* 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[$i] = array( $field, $finfo['TYPE'], $finfo['SIZE'] ); + + /* Loop through the options array and add the field options. */ + $opts = $finfo['OPTS']; + if( $opts ) { + foreach( $finfo['OPTS'] as $opt ) { + + if( is_array( $opt ) ) { /* Option has an argument. */ + $key = key( $opt ); + $value = $opt[key( $opt ) ]; + $fldarray[$i][$key] = $value; + } else { /* Option doesn't have arguments */ + array_push( $fldarray[$i], $opt ); + } + } + } + } + + /* Build table array */ + $sqlArray = $dict->CreateTableSQL( $this->tableName, $fldarray, $this->tableOpts ); + + /* Return the array containing the SQL to create the table */ + return $sqlArray; + } + + /** + * Destructor + */ + function destroy() { + unset( $this ); + } +} + +/** +* 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 xmlschema +* @access private +*/ +class dbIndex { + + /** + * @var string Index name + */ + var $indexName; + + /** + * @var string Name of the table this index is attached to + */ + var $tableName; + + /** + * @var array Indexed fields: Table columns included in this index + */ + var $fields; + + /** + * Constructor. Initialize the index and table names. + * + * @param string $name Index name + * @param string $table Name of indexed table + */ + function dbIndex( $name, $table ) { + $this->indexName = $name; + $this->tableName = $table; + } + + /** + * Adds a field to the index + * + * This method adds the specified column to an index. + * + * @param string $name Field name + * @return string Field list + */ + function addField( $name ) { + $fieldlist = &$this->fields; + $fieldlist ? $fieldlist .=" , $name" : $fieldlist = $name; + + /* Return the field list */ + return $fieldlist; + } + + /** + * Generates the SQL that will create the index in the database + * + * Returns SQL that will create the index represented by the object. + * + * @param object $dict ADOdb data dictionary object + * @return array Array containing index creation SQL + */ + function create( $dict ) { + + /* Build table array */ + $sqlArray = $dict->CreateIndexSQL( $this->indexName, $this->tableName, $this->fields ); + + /* Return the array containing the SQL to create the table */ + return $sqlArray; + } + + /** + * Destructor + */ + function destroy() { + unset( $this ); + } +} + +/** +* Creates the SQL to execute a list of provided SQL queries +* +* This class compiles a list of SQL queries specified in the external file. +* +* @package xmlschema +* @access private +*/ +class dbQuerySet { + + /** + * @var array List of SQL queries + */ + var $querySet; + + /** + * @var string String used to build of a query line by line + */ + var $query; + + /** + * Constructor. Initializes the queries array + */ + function dbQuerySet() { + $this->querySet = array(); + $this->query = ''; + } + + /** + * 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 + */ + function buildQuery( $data = NULL ) { + isset( $data ) ? $this->query .= " " . trim( $data ) : $this->query = ''; + } + + /** + * Adds a completed query to the query list + * + * @return string SQL of added query + */ + function addQuery() { + + /* Push the query onto the query set array */ + $finishedQuery = $this->query; + array_push( $this->querySet, $finishedQuery ); + + /* Return the query set array */ + return $finishedQuery; + } + + /** + * Creates and returns the current query set + * + * @return array Query set + */ + function create() { + return $this->querySet; + } + + /** + * Destructor + */ + function destroy() { + unset( $this ); + } +} + + +/** +* 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. +* +* @package xmlschema +*/ +class adoSchema { + + /** + * @var array Array containing SQL queries to generate all objects + */ + var $sqlArray; + + /** + * @var object XML Parser object + */ + var $xmlParser; + + /** + * @var object ADOdb connection object + */ + var $dbconn; + + /** + * @var string Database type (platform) + */ + var $dbType; + + /** + * @var object ADOdb Data Dictionary + */ + var $dict; + + /** + * @var object Temporary dbTable object + * @access private + */ + var $table; + + /** + * @var object Temporary dbIndex object + * @access private + */ + var $index; + + /** + * @var object Temporary dbQuerySet object + * @access private + */ + var $querySet; + + /** + * @var string Current XML element + * @access private + */ + var $currentElement; + + /** + * @var long Original Magic Quotes Runtime value + * @access private + */ + var $mgq; + + /** + * Constructor. Initializes the xmlschema object + * + * @param object $dbconn ADOdb connection object + */ + function adoSchema( $dbconn ) { + + /* Initialize the environment */ + $this->mgq = get_magic_quotes_runtime(); + set_magic_quotes_runtime(0); + + $this->dbconn = &$dbconn; + $this->dbType = $dbconn->databaseType; + $this->sqlArray = array(); + + /* Create an ADOdb dictionary object */ + $this->dict = NewDataDictionary( $dbconn ); + } + + /** + * Loads and parses an XML file + * + * This method accepts a path to an xmlschema-compliant XML file, + * loads it, parses it, and uses it to create the SQL to generate the objects + * described by the XML file. + * + * @param string $file XML file + * @return array Array of SQL queries, ready to execute + */ + function ParseSchema( $file ) { + + /* Create the parser */ + $this->xmlParser = &$xmlParser; + $xmlParser = xml_parser_create(); + xml_set_object( $xmlParser, &$this ); + + /* Initialize the XML callback functions */ + xml_set_element_handler( $xmlParser, "_xmlcb_startElement", "_xmlcb_endElement" ); + xml_set_character_data_handler( $xmlParser, "_xmlcb_cData" ); + + /* Open the file */ + if( !( $fp = fopen( $file, "r" ) ) ) { + die( "Unable to open file" ); + } + + /* Process the file */ + while( $data = fread( $fp, 4096 ) ) { + if( !xml_parse( $xmlParser, $data, feof( $fp ) ) ) { + die( sprint( "XML error: %s at line %d", + xml_error_string( xml_get_error_code( $xmlParser ) ), + xml_get_current_line_number( $xmlParser ) ) ); + } + } + + /* Return the array of queries */ + return $this->sqlArray; + } + + /** + * Loads a schema into the database + * + * Accepts an array of SQL queries generated by the parser + * and executes them. + * + * @param array $sqlArray Array of SQL statements + * @param boolean $continueOnErr Don't fail out if an error is encountered + * @return integer 0 if failed, 1 if errors, 2 if successful + */ + function ExecuteSchema( $sqlArray, $continueOnErr = TRUE ) { + $err = $this->dict->ExecuteSQLArray( $sqlArray, $continueOnErr ); + + /* Return the success code */ + return $err; + } + + /** + * XML Callback to process start elements + * + * @access private + */ + function _xmlcb_startElement( $parser, $name, $attrs ) { + + $dbType = $this->dbType; + if( isset( $this->table ) ) $table = &$this->table; + if( isset( $this->index ) ) $index = &$this->index; + if( isset( $this->querySet ) ) $querySet = &$this->querySet; + $this->currentElement = $name; + + /* Process the element. Ignore unimportant elements. */ + if( in_array( trim( $name ), array( "SCHEMA", "DESCR", "COL", "CONSTRAINT" ) ) ) { + return FALSE; + } + + switch( $name ) { + + case "TABLE": /* Table element */ + if( $this->supportedPlatform( $attrs['PLATFORM'] ) ) { + $this->table = new dbTable( $attrs['NAME'] ); + } else { + unset( $this->table ); + } + break; + + case "FIELD": /* Table field */ + if( isset( $this->table ) ) { + $fieldName = $attrs['NAME']; + $fieldType = $attrs['TYPE']; + isset( $attrs['SIZE'] ) ? $fieldSize = $attrs['SIZE'] : $fieldSize = NULL; + isset( $attrs['OPTS'] ) ? $fieldOpts = $attrs['OPTS'] : $fieldOpts = NULL; + $this->table->addField( $fieldName, $fieldType, $fieldSize, $fieldOpts ); + } + break; + + case "KEY": /* Table field option */ + if( isset( $this->table ) ) { + $this->table->addFieldOpt( $this->table->currentField, 'KEY' ); + } + break; + + case "NOTNULL": /* Table field option */ + if( isset( $this->table ) ) { + $this->table->addFieldOpt( $this->table->currentField, 'NOTNULL' ); + } + break; + + case "AUTOINCREMENT": /* Table field option */ + if( isset( $this->table ) ) { + $this->table->addFieldOpt( $this->table->currentField, 'AUTOINCREMENT' ); + } + break; + + case "DEFAULT": /* Table field option */ + if( isset( $this->table ) ) { + $this->table->addFieldOpt( $this->table->currentField, 'DEFAULT', $attrs['VALUE'] ); + } + break; + + case "INDEX": /* Table index */ + if( $this->supportedPlatform( $attrs['PLATFORM'] ) ) { + $this->index = new dbIndex( $attrs['NAME'], $attrs['TABLE'] ); + } else { + if( isset( $this->index ) ) unset( $this->index ); + } + break; + + case "SQL": /* Freeform SQL queryset */ + if( $this->supportedPlatform( $attrs['PLATFORM'] ) ) { + $this->querySet = new dbQuerySet( $attrs ); + } else { + if( isset( $this->querySet ) ) unset( $this->querySet ); + } + break; + + case "QUERY": /* Queryset SQL query */ + if( isset( $this->querySet ) ) { + /* Ignore this query set if a platform is specified and it's different than the */ + /* current connection platform. */ + if( $this->supportedPlatform( $attrs['PLATFORM'] ) ) { + $this->querySet->buildQuery(); + } else { + if( isset( $this->querySet->query ) ) unset( $this->querySet->query ); + } + } + break; + + default: + print "OPENING ELEMENT '$name'0y8W~T-&+Oahq;AR3n3+d5J34iT$E;2GmW0 z!90{Af|xA8F*Sl`7b+SE#%dEBM4@V!a2h?Um{d|R*d!x%N)sdykQtG+o?Yz?y>J4jKL?{u4th_p<-buL`})*|gq%p!EB!_nAp +{#C+9vwR%amPB zI9reYegot`$Xce+=XOGRX)&j-eqicb%!;8GzzPGiGB&AE!tS{2S#@12BG;i)eW*7R zo7Kh;6)|*p@gS-_wha`~)~3}a)Y51)p{9)87vZq# (!|huSb`{I5LB7 zGJ%zF)lP#rHMG6j1Y=Rh)+jB@q%HRS(yhoFT(&L2r&c7kb%P&~XRCK2{~^9d(Z0Bk zzL(KNMeb82+u4Id1O4r_=In&9+WhxCR%vp*8my3gZ}NN(6%10VK@HD6Yz>WYvt1-d zGS)LVEL#ey&tp{n3Wpyqc!)@{wpE3bvVYcH1>b|kyqzijsNB4-^R!LY4_rA7c(lv* zon7$jI;A&365rFnslA&ivMkGjjOhrwV1ktM@Z(0O?!<#$QVvr$4xr6%C=!XQSThr) zRPQ3bLdTJ#KP1frvJ~)76+lA*Ee9d}a{L%6LhnLm4J%8Rh*l-7B&*)fme00ah+w4j zn6@0(i|QVOa-#*<6}N+m1Lt7BMVpKs52oOPvxuZG_eZmycUzNkKE-{{m|u2l@9LxV zUXM`m#`y7^&i(FHY}OT|y;eP=JWlTIUHzsKBb3r6A=}}WOZ4Cy*1a(STS+<}8g_Bj zU=bVcb)~n2-Qf|$Q{7v#*3s6*=AAOB;m>jR9Q}#!*O^ Y9{t?y+c$YXMcYu#K3h(?RaY0C?Wt6-r_iZSys*QgGslqPd4O+530s)8XSjvE zDr-0PXQE` Ie#VMsG|VQKRZG`(YBFh0Wp`)a6N%%TWF){ zo0ogF J*dCxvzzpu?V1h(;P&nM|^vGNfq`lfEuRVq1AB5)h1- zPixE)ZGdFRl Wg@dH;t7 zPg~|wv+c88i2?xtgTMd)-(S#ReG6TCdj~s1LwoK2j4=L7lAQq!{}fUL%J*31)53F+ zXwx0+iuW{>Iu%03tp0AADH2VnQM+%jyCD<;jkqpI=+;g@FX~hb({nv^J)B4vcG!8H z8^QOtmI+s818AFkks7VU-|H%zH>xI0ayJ{`ly^%^*D6pvPpj4+kSK`NntCKfvHp@(gcE!XMfNx zaSqQ8+%W335*dp?8Nc|AV8d;MwW$!>)>aPOiI5$rGk6ZZ6!#lRXVry|w$$OOx=zE| zdS#By@iIk&0L_ZoSqvS}uhR`4EX>JXtbLxh2~|UyDCAUq&2Xz4#^^*)MMJ%na@faO zXRG0<0ib4Anr8)#<<@lqZtkP1Q1v* rLjHX#&=0o* zFPYkt^W1Fpik0tS85~?SZP+n-S5x9dY05OF!rqY=6*0;^j_^(c3oRj{{Zzuj&w^t; znN|(^H2(} kynq#zz(KQ9|Azel7_q7K* zC)m%M%$xJ$c=?0A_Y;)_@bgu(l71UYu=ObI7dgTo>!+Wn_6b#~gxU EL$qb zW6LLskC|+|M@Ps)uGt=nfoeoPW%4;RPds*gf| F<{ctsHlG&)ne9UGp#R?6)b;r=P-Rh@1`80}`SaS)| zH^Y(g@~x#w;Vfb-z~0dR!PlIkOO7yUHYmLGbFuMfMq$GFd76-Mk=;cq${H(C={85_ z%{IZG$=5v{PR9$wIP(0Wm^n~qDk8`+26cfH*xXGn7_n0+yIr*3xQEjd^vmbJ*g=>a zNy-{TupOlM_N0%8qxkWsLdEJ8 w?h`#n>&cV>y(L7H?-I%-Mk6ZExez+3UC@q|JNGr`C z5v*P18WAc_gDzckdMfWI!!|2|`sytcqvHx&jRyxJucs%mMTl8I}0>dddnVQk^4zsE})>I_)b1 zpu+5wdEvstZu@uLlD*D~J?uFgWtd>r=aE56u3;I=EzG{~984J3rgg06`+lBih;m9Y zShegrm^Pj}v |gX#g9f|BDl^mR(3c|p(WEiehFQd@-h-@3E)h>2AelTC~J+9P}{-ZP% zhTS%w5p`SG*9=;R)6Ma82^zEhAr~)F(`a?Cv1$CzEM*P*D@*Q+rIf!^m;ZmsQvZ&~ zE_~&^yF&0E(A|2w=4VPUln}BMg-t|S9uQ^GGB%E#UZE&g2 A<;!w-{D#@Q>bMn;kLa^;E?TsU67WbfldB`N$;S6_TpA7I;2vMfS^FF8Ota_JK) zLS?5Fj6F@d+ghenkcXBJ8XqIk@BpNjbx3 pO=M zc{iZ}bNYhuL>uX)2rD6fw|w{lW2Z@s0{F<81l`(N#mZ6rH#f_m)t0X3$|~i`W7U>j zG@6oK=s_sdeph9I-!>i)4 Zkrjm*+Is@+ty9;-Rb??0yiF zv|c$ub^{Hd>F?yyYAu@8h8CUqNy0F_$)yZq`d}sYV|!pFBuUAbIZ^59I62f*(%3oN z%^j|29D+k+OOL(wNd6)P^u=f`I+^O}I(t$dF_DHJ7zx&ui!R10KFN*-88r*A9>-67 zv?h-2ogazjES3z|Fltx~gN#qhXF*F ZFPP9-;*_M llEVYzYS5n?Cp zM%adak>=?WqkA3unOTDuJ?JHROS3gp2F7;DtxBULqkh2$!xkc1AtCjzenp1&8dpoJ z^sO7Jrzpv4M!(~~=nMLAhQ0?@IL&}oRIZ};i@w6{)du{y0=+toM2qEYsT!D5sA-m> zc|IEOMMVo~s UOFup5sLIqmL%I7Jq8>2*gfCJJ?)e?VCWSxtuIB`OZX~oxU)!Gu)o%4|N z0aGDd3_}e^mx&{(cW4v5x*g_;g^(0Cf3erOA5AP57 b^@mN; !RsT=Hs!Z9lTdY?CcjRmdY=6;C7`jZ0Kz^v z`ggRTY%B)}A|4;`ksEkCwghl@jj8CN`!^;w+UcQ4YXV?wzLniig6Hs+&wajApX;$b zh;EOCT#=9Xrazm1}p9Lr&qFIcrvP?s+UcS;rIF$Xx4k@mK@u!PYPa9A44a7a%be zACkEGJt!|#mad;b|IA~_j&S=A-v9u_uT+8Iuke_iowc3)zmBs4 6epaQ8tIa87BAef3yT`L_fPV0=}{X zOe$9G!!amYs9(d4CK7^)WtgO}f9u@*bk@s?PkVArrW7Jf 8RWzv+%;^ehCdj4&CV(a!vWkYR~hzm+v?PN-d_UX`#TjMkA zs|5@M!~Bwon1J5)xhU$6^==>?H1@+vb3mr7l3(`{C@`}O(fLSWYT NvW~uU(9_o(KD(hSGRez*zc zvK#<*dVVli#xP7v6 z!&v#t{FEn|6WdqHXV}j%xRQG0pXK>yY^~8$Qbv4XOZ_Vq{VS#5|4Rt(D61L1MhovD z3;gn *cv>wV1*d;R&@EF`&u`B)-%>WIv=XSAZt|z%Wi;IRzJS?|Jp$OVJ)@ zSY0t|S#MCRUg$nQq{;|$g=sA9IVx_4iKr&(PE^jCCPf)WLs+tx7ByTgQf^NPCF09c z^^?~JtGoBH1Mt}6&(>qix;c9XC})AFmu!;EQ}hVQzAm>^kd+ btiw$Eoq`%zNmlEHHS8k}PuoX(%J0?kYEo+VysXfFC` z#|?fy9*$#l{3+t#sCwet!4qK=bV=jIXn|fH#BO>1!p|OASEj_e8RX{~UnZ{WR77Og zJC){!sG&e5b{t0Q1uWHnUr4Z2Vj eO__%_PQ02VKhVLH<<+jSG( zZB&-A(exsSvaxDKdkyckenPkB!P^`7pBG%&?C!4TYq6O@|61y1VfyccLV*gJ*6FlJ zo?nzQ7S2+tQ>T!g@J%j#nn`U{2x4Krc=$I=&Mna%<3!Q> 0GP+%@S6h1&&w>OpktaJG8*J{(|`?;JJ9Wj`)!a*0$_m8)VHX z9WQ6P^eErugb&mxvP^;akL__=yxodRNLfZE3NjdL@EDB{CxH{EcE=|E;vKX|6o(&C z7a9r&sp4ZrC)1*WVt&9!FjhzO=ADY$W)M>(zNRJePeDv}#I%7fV0Rsvj1UEOJV12@ zyGT0VOBP_+8qVPUJ0c%J+P|HqG?IOK89PkSCwhP9zni($pG-Kb1Tb8Sk~k4LvE||B z%szzvCgF>(BCu%$r}322Rikygw*QkFbUPxK8XBM>^jHtCVC->*)&t48u@k;F(s}dv zY=|CAVl=`(m@b*QDo8}3gG#V`ee1tqKe$rX-)OVSUeE32Nf4tSE>SZ4F|mo|{&A^( zCR-_eTHtBORS2;^nq!nWyWY^LF?~sSy{kj5I?u?hXqddOLKi(aJL#x0X?qc0V4z9D zs@J^g&Q`Vr0 30&u z0I@8-wKt6{p3d0x7^Ilq{3*2)m`{M!=tSO1SS-xcELTwe5LHz0=t?CrOZh23{C C@(B6^1Z4~A_$x-Opv>GOK$@~DbQThGfpfcYG&ZF{7KRuZ(CbW$3qS{|C+ozms zQume@iT&VY?nn}HAOn()z{PmAjY_o3Q1gGvD(2CoW75Z# 3ku$BhJYgY*MbePE*k^PWtZ26`Xp_7?RO8+e z$wGk(Te$U(TzF={MTAlXCOU+nS|-QXk;k}-fEy@x5jWd)wXx|em^^17{Wvp9rl3j1 zsRn^JGoc{VYd%Hwz~Ol|!?ZsrkiC>^nM&y~@SUTI*8dLBo~Id(M1Mi!i`mll?RqB* z?t1gi!{vzPe$N|ZNZkedTc2sIL43dL@Uej GLziaD6TPIz8v z0?6gVOtQ~>N@9kV?PUr^DUcnlZXOxj2wl`~3*wYd!<1l)_3Mki7n6539?LVgNlwpE zj+=D@Db+Iwl|_WC_R|TEQ`bppWUYiX=I6JI;!h-}Wqjo#yBE74@7V651$y?=IrbAq zRU<~ 7);x!Z1IN6Kkb2{d}2$tL`F$!u!T5FPi%rID*Z>6Ji z;+i83zth@{4nUBu>=f7wt?k&M3I!y4m8X|O&BQ9lry0xwBH1URuQC2c%>T2CO#K(I zW@%_(s{5~9-+xT-w~DiBhjjJU7m)vU{gtBKKPJ#K*0wM-a-g=cGXA^y``;_gp)yj! zFp%hfn+`@)L_qHAzV>y2fPc*fpk- t-ii~Vq)Up;Na@& z>iYWn^YarN92^1y0umAu3JMAu8X5)$1{M|;4h{|;9{$IV9|#Bth=_ lq$ ief`WpgqN0+LlCrY0ii(P=s;Zirn!38W zhK7cwrlyvbmbSLGj*gD5uCAV*p1!`mfq{Xcq2bpXGd4ChF)=YUH8nFcGdDN4u&}VS zw6wCavbMIiv9YnWwY9Uev;XoG92^`S9i5z oD;^X5J5)u*<6O)pXl9Q8DQc_Y=Q@@rnJv}`mBO^02Gb<}AJ3BikCnq;I zH!m+QKR>^qprEj@u&Ai0xVX5aq@=X8w5+VGyu7@kqN1|0vZ|`8y1Kfirlz*Gwyv)3 z3la?t4ULVBO-)VB&CM+>Ev>DsZEbDs?d=^M9i5$>U0q$>-QB-`|L*DO>Fw?9>+9?9 z?;jW#7#tiN8X6iN9v&GP866!R8yg!RAOAwv gwv++S>a1`o_k_=H}+s*4Fm+_Rh}E?(Xj1-roNH{ue|K4-b!y zj*gFyPfkuwPfyRz&d$%zFD@=FFE9Ur-kY17+uPf_ySqPs{@mZ+KRi4 zP`S_y}iG`e|&s=;e0Plp66@dXo(8&DY#_l>ELAf_{N5!;R06VV}JqRer>-$ zvo-M2s23gZ>D4#^c>;+2?+IBX_{ygh43busB${;_^pM4k0~M$=f#a#Mx@g}ox0(QY zI%rvdeHI@8`sB@bcpbpSCxV);=DFh6OaM_qDS@hAI)48gM6L`NZN7bF4M^Ys0Ow!k zDtiZ43qyMoLqi8@eS7==kxdp7{~kvMRmW@lGD@xz^455X=$;BDDVP p6T{pa#o;xR%qoLn>bzhLHAu%;ta%MLH&XKt#8X(I0xF$Be3U{h@R$8)C>8 zBPu%tN%lJ=(&3#%-Gj1~^7~oZm6X)vlrRB@Jr*j&GL9XWu!k_TE&Woh% z*e6ph5vq0b1QWF6` -y6N>zhWwN&tcnMiGUSFESrxky&xHMm z2fV7wN@(Gx>97`MUMOAOrtB$donF>nq281uGF$H9jDF~`a9Z&70EnX5iTK}@WHi0e zTpbs)wm~n5kI10N>Nd_VT=5y$pdZTaYO&Zrk|KjHt&1t5c!`?=Pu8YqqVVEo=^)j; z3v6Y!S2UuzD5^qXald|{U`|+kzrxvRB26ekI>vAoFn_n*+XK>lEh(1?&t~{>k~k9~ z MJoguYtd%C0oBU_G?h;*M-|6goqF~Q&&)nbL< O1n*P+{w6BXKTU*>j $w8Ca zql9iE&BHw3Gs-}^gAC*7H9{H-@Dwoxtd1c%caUbv>qr9_6=~$kYAw)S==%$E;wnVg z-l7=SEvE^<;rKT3oY7$@v#$cZ2*WBRlxhbF1G!!NPi*$#9{jKUQ$JhtU=ks}>m`z; z>9OZ6wyprFaDc|`VJ~L5_Pbq!w|R%v9m?eb!RmX^JhRvOU%#J)o0RI~!7Yu)4Tt wN?rvaHZ46rm9j^l_#A<#MY?CaGDJpGJr zP&Ju5BTsiq+D;jvT4+h~+z^kVK=Xc!wt~hx?EW4E_ZBN$D1`PajTA-?A`r>^+;v>d zgRE7eh5cFxFUe!YQO1(#272ZQ8Cb&G_U&b5^bmp y^=In z${(v54no(2q=>C@`mvJrHn7pFRH5aYp+wN}vuoPwh}!&|tjC7xVa3=Y<$L0zcK0=? zOS)06vb7uMorpWEo*Z>qo9as@qY}HRr?{k2Cyya^(;c-rv5bvh{6`-ZLB?U}Ght|b z+(}Fc$NE$2OZ5&|60e;A?t7y>EuNjn!?x^Mm2&^CS>qSPSBC%i}$Ja%ryD&1qkeiBoelS 6rc+W0(~s>Z lll!l! zc|rB-@Ie#G2j+X2e6
%2=eSzPp#}ey;^946wZ)Zy-OOCFJ3uAR$`waZx!zxCcH%1JN_ H~t!wTfGUiW=|X!f3b-yR;sqM ze`Y|3#@geZ?5$=j$^bw0Zx1*Vq&L8Rdh!A~g!vH+;A+7YcbU6**e-GQ`*gF;-Qc=( zl8kZ3Uar5fptrkjNAl9eoctA6#OvX-M`a>lAwGriVBqnLAt8)-oL;ID#HcAlE)jEj zobKoKBPqFvI73>v`_F{bP*@2Le7V}H?3`J1mn%WA $V%#TIut;W#$@F`F!FR2FAOx2B&5ELg>e93{as3(l)WMuW8)pm0ol$b)KlH3p zI`)6<@x 32ia!*o8n_IFa|_Nf(?q zbdim{yDeQxmvsb1Cfv52jW4&S^iwfoIDT)k(S$PCDo$hg2cHY?I=t_3;Odbb2pEOk zogxLj?TNwpvaxbstUq1dw!@~i?s%e?LE_{_?8pH}|HG;?7s?P7iMNcbK`B)ZoLO~m zP76Cvi1ZO5lwi3E8Bw;>!IZKOhx|=EkH?)2u(U|c(_JOM H;{dah)o2N?t!4dOR zE?Qvasj2UsWcx$ONBJi%%;=XWZ+sm&nBhs?Y)}#5hBb6JoW3V|j2zwQR$z#-wK^Z) zQGc58@sn~gK*5xaeijfhRoP8DT&iv>J3hVQ5dJR8oK(qx%yRX+nN k#O zHQtf_hNM+mRIO*~VY-Y#4t{EBvD!aTL2)F=(+LSvYoey#UG-l8hJo)=Fykut<-{Hd z!n6%HY21oNzcV>cZPV6Af;YRKZF#sAz?rqCjmreI)!qgyuSvJA3_?l5d!>k UrB2)xrJt%Y&4r(@Q_c8UUMWhM$74x(O^ zgATsM_iJ`S T-?fHu}EA4N^2@{!mL|Hq8-8q zp_K2W*N|Wvy7hf@M|Y>>uil&A!s9cAE`}|}-%Gib%0!f0$*fvefz18D!yNfZgwGYg za!WKJ^K8mK)r&5@tX#l!JR)V=l+<^w#O}Q+zgiB(LdyYqlX;^yo1Yw=)mUL+E-rG$ zZap%JDiLC_LEkE>s=kID9!HB?ThbW(i-;LMI8f!8RMz07aFK_6m4j6-ARm2>h!GSb z6wC@aKQQ4axS;7<$KohyUNL^<6nIomnu9%f86Sys+EG%cz~Ef6?g9V RFuT^H5;Q|Yeem{?rn z+7khR&Y1H>SJ<-rVcV)riK?H={H5j9dv6MImR1J4@^ZEPxcSudvS4rM+|(QU{Z8wT z1Y^t3g?ehLxR+Smr2FzolQYGH09{!@X9l54_4arsGpvoybbT6#@8Zu;82s7Z=0MeE ze|+=QeWS$$fUt2zTAS0@ctKM4Kk5h-lo35vZb@`UBE<4!Ud@@XM~$=I!X2SNcAORd zsAsRo^k=I_an~ROFZtz9rb{Ke3&&}Q?5ClsQQsxsDl>o2^#1=Gi^ctFO(E`Fxgq${ z`17yU6p{ax#{Wm{{eN<+DE@WBiaeeP!tdBc>ZSohtoz$l$Em+JtUQ|Xi8o9LAvEG6 zSHr&UaWEtptl0ViLo(tZ*osWPjx&ru_0b}PN*$7D*U$G;q=wptFj{;3d~a7+9K}3j ziU6$;Yh}G-$dxOX7DukOc#)P(Q=zdp6ofw;kLJcxO@J5|=$a>y@2nci4e_&e8G^QU zfJmh#D)Go2lTazV`_|9ijNgQTMnk(LPW-Wrw?$%@0IHej<4qKT-JRGtV2WC`bf%Mh zWWSo!dxUZ>mN%I?egQDk^i@3}m&{2Bq^24#FU0$dQCH|Hm_t*6Nrm1vsq=y8$LDD< z*oz<4hv+_4i~{SB;1uJmghm2-J_gOu_Uvf$nTy~1CILH#giBqBE1pAokAr{}zztkS z7wq1^kaRp*2pz>`{icf~X{UZ#5H?bb+VkU#r^^L wM wM3vHJDF$+@w7ns|GMXwyU2T0; zy%G);h)(rT(7+I#Z|9F!A!f>ozZYz}--#woGLuMZKN3|rvO>;+qQCv%Wz8A=hZtC6 zKTV~Tl(Fhcqx0sY!zndDET)B+C8c|LG*Xr1|FvNyQXnFpi%~_kgnrR?D@L|ZK6^R( zid$27^Rd+PlVNsSH0e7G_g!$0`sdMA@&ngZt9l0rE)XOiuwAUEg*8+4o6#DU3TY^{ zGuVGNtfcE->flRiAY5>g1P>DtBWrE{#zL<$NR%z(5mQ#pt3zM=MONC4s`tZ*)6L`4 z@qNoC*x`frm9@3=&Eb9R{(^E{j>W)JyIyBr&E`bE 8YN(eZr7WGbH2 zbY^9Nv4zd8Mr`h mXBuR$~4$b!mAPhX ZNqAZJ$8&cv5)G+Op%A1@;3k z3Cogtvg;}~-iydU;+p)0jbM&*o6vy9-k{kAY$Hg0WTwF=eq`HH`p`Llk*v{YlVSuc zQxC(XG!6ZXxh7T6X{Iukr@cTt9ULdF%qK^fls1BUJEgH66RD>-_Lf3l{AB2d;>~Xl z2ggcPwnudYI6Zh^h``sInI;cWxZpteao1y!3sQ{7Y*Xk4Q$ke9#Cep0ST0}_BMd+S zVNm#eo-7)e-yqFbv>h-@w4S)NKI3*bg2o^wNS#^zM!3VZ%pcXYs3Yq3ikb mhvkxWuHOE6~&g!V;=@}v}fRF;h@aKXt%>f@xaW!oy4?%M>~M=D1eucA7; z`YB9p5Xw~-ZPQ0LF@m`DP857GvQVTrKsfWr@-g)NaMy7y^$>Y>YrHpQT&{^=SW9c~ zojikeKU&agKj`sqSv+6fr4~G3x*N6gty2^~KD3@|aU*FRQ}bNze~F(1BYSg<{71uz zv0BrxV7GIvIh+=EU$(ODyrq`rRtArRaaB*|^V^X3kDg5#RoTWl#$kiLOg*@|dujSk zu2!)7r&sGcZ{Xm}vus@xZN~y-)h >mq?2^nTyJ zn_#Zj+$+a~SyH*gR8tNIOdR>5CC=5KXF;|z_n6#V%UZ-t@hw#wm=0rNR}6r<+pIuX zGf)HB+Fc0=D9WE7^7Sm`6g;MYF}0yXqfYn^h36%!9W-XP%f-DM`iQ=<<~~uMm5x#< zxcSjLG-|X8xAt?nF&mqa=}<{zhg?1R;qmxr+c49)BuZ6?M|Sm*H0@1g{S@{4t&8tb zF?Z)hltsu>N!uF~Hwtk3{(KX_{hKik MdRVHYr$&ksViRGT6pmBbq7{pz(Jh^}(3_ulwlW{z8&30wjD?OVOy42gByf)~{ zXhRi8Axa}K*x0$)^wzv^Vy2d>$^((xC1gJk_Wv=%Tkf0DoXnid}Yf(k3=}Z_GbenNS(idQZpdDF)p?6HwB+>2tFkPws;O%1x zbTP?Dt-bTws{P@i%XrVfd6m Z4 z)Et?0Nb56xIWmS)D?zTGliwTILZt}5^J{*<2@UFE+OV3D{Iuog1{U+Sm4Wba!Y@QW zH{*e2hk_XmMCVdB8~O8^s`eH~ef*JmI7m6cQ2KfGs(eMU<4=|Z5zzJb{^EbsyhH8s zsk5!{4TirQ9-puEB>(r;QdCM_flor>?~aWw_&=}Y#59)`5FI?|_GYmQ=z(5GxXiQ? z9{zwBDOoRQ#FCEqM>rR*$17?qImz(+UDjdx$=+ED2PP`VSI3nII~d#0b3=!_a>9(3 z6X_y}a;{z(ZvH2=4F-}l@s35N6%~@~9+Q6a=X$qplPX16tKDGIr_Qp2R|Xh?wIl+0 zS9M!VWG?QZ(H|`^a;&(yIupihN4Q2|`)3q9gL3Fr<<#)7WF6646!VS^{tRoC~Yt zXCJ xEL8iU>_v~Wo1Vw9Y6)>dGG5l$j7tl z?8~f`6;KhfBRf5W<;*V^@x<%JFzuvHWm4N?#j<>0@Io$K(P^bXy?p|Ox_W( 3`+|+ZKJ^fJJ{r&RNO^3%y QT z`zp;c^P)MJ2bI51{ctbb9BuQ>_!DQmnI~bzO@8feAkg!^4~}QGq2k=IcCwybGGrv& z7YpI_ITw<`_5fJYV!R);$tOJpi?CT@faH!+dJMR}Nz8=cERyJBw_8viabwJ)a4i-! zK2GI4R8UnD5e}H)Oj3=ZFCZVjN-&m&p+oSO0L_KQyLfgE p @8yqpLr!RTtm9)WJ?D8Dgq zVC!RlBI)DNLYNAvIqR3Y5=z#8qkTCFNutZ}5ObJ5ImUVX1Mg{C9fT8fWg>etG)_y$ zV8TLuMUoGflZZ;&>CKQCY5Z6p>*?(7J}@sO9Iri;oPT2O?#W@uydf x@y2}$t0!A;b2I9}-m--7&j#r|vMkiG zew<*&Gp=g%HGYSY=-3wGj!>t!Mc8UDEPor%r%8B{L2| zql@-*cXvt7g8R8&LWrxA?XN6Ld*W&@2O& jPPYqM+|_av{DGyCGQ?-oYT(Qn1s7vmQX21r*;WDPqFBO;1#PkV5L zB7%y0Cvg1c-Y)mI2V*~Jyx(@Fl3Z9Bo&Ic(0 zk`uf{-goiBRtq7DPpu6QPb^J0t48j8qm?VsY4ZPM>I?rvQ+`-sz}4+YhP35>E4pyh zSU&8bHu%daf{%Q9X`d-JW)xhpUS(Q;di55;TAZ1tXQ$mr3Q3LWSQPbBQICaJFlg&K z`*6ZaYHo%Y<^w~0yxm#3&4Fc$eU+4B4dh(5M %@9U1kn)3$vfFKru6*u*1HU0%CJmZoDxY8`yyD A+1E0^v`UFdPRY4UFjUKDN9#v1`pD9%Pe++4y~p=X3Js1xIoW! z_^v`Uh&6c7h|b^l(t`RZrd2r8%7NfF1gv#hz+v>9hN48dQ#@;+6OCrP@VW?`ZMG4p z?83jOykx0C7c&mSc);z2kE$6Rc0-`f_;YBD=ewoFd)X(LsZ6bE>?Vf_;i31s;A^Jj zzj4_MgGD|DcLZlB*E*jVXGBeQkvu_i<`=fq^_v#5buLfOF?#lPeZvEPf_rFkH8U?K zn281@uY^Hu5ycVcz@Ldwh%iYapucE^w1Vg{%mTWEFDWXuY})N~C3HUAZ3Jk)!Bz(Y z u zp8_=7X6TG^+hGzkS}Rd`FM^C(>=dphRqOJjLvfQ|f3lY^Pcs(lNY*sSwTaO;7v6<- z`JlqHLZVO$Vp$~y=$%p Pss`Ur0IFz%nFl_NhE~x(aG+Mw(%ho`6kFg- 7RbWuf=3!y%nf &X~wS z^w1kNzZebr{B8x;>?lv^U3)}BQ$tco0?rS=42m$NeS F(rV~+0aA?gV5TmkLLl4CRgzugDrQtwTu zC&6SiN{M`uqz!@N|38%7Q dGU&?e&b*QDp|ovL?K(~j`j3u`sY_ 0=$T0YyytZpC>u5N=o19t zaJ1-hAKJvKokGteMj5?Jc!2}DZH+n7v+t8Dn>y(ZU`HT|QLzZ*@-_)@Js&CQL~bE# zEsPZSjq{^f)G{g7M+u4>f}q_l0y^AcgG1M~T;!Y6QFuw8$P;m6{sHd*C@TR2@*?*S zIZBi8j8nhm0{{XpYw07LMBDfqUMvWyHJ8GlgbEr@(ivg2F;GZUbumA@i@ao Q|FB`2x*N#*7*+axo;MW6d1~!ee{p|CT$A< zbT2D$FR4#b&Uoaf2$){e94*ZLaN}{Vw-+T3YY>WTzP=e@B+(2h%B=nxZ}f0N84;|S zSbz{@TB%=35g@u;1pee;S}Qpp5)lbwa)bWGq<~4&yCv$~WB68L9^0XAyK?{h28b>S z2Ld7H+?=k7N$TxylmxGpZVx?sAc<&6l6O&=w*Ba{fJ6STL~q@{uG&AMW{uUmkl(N# zo*!Wx6qhq`Z8paN^4KOmg3mAoG#m)mG+Y7cz bxK#;jXjk9V~M0wv_$YhWWuTTvZKJp+EKDwm+&< z2)_O6r`QftoOAHDv+B?x2&F9g*!KwMG#_x5cKWcRIWd{!{j{5RvgHr~;f1qP5ymHL zeZV&q3I(Of;shzq{knsF1osWV6%&I4()u-%iyZK-lHb8XF@0h&W_m9*|7>-*j!|PO zv(YAj^~FxYU_26Bq!ZYjQVH@z96Y<$a&WU?*A{3|s6&Ft-RJ|EvKebDfi-?~2Dr|| zWr_=!@XTHox?lvneY20I(Xh6e?r|HP+eA7Hy5HVu^>;L)%V8~Lf((`(vsaBFU;!er zx=}sVs_qUi-3G}{iW%#lj3kqEuwW29NY0o9W8;LmWsDIMCrHr_FGiD-tSo>FuE9u+ z4twLdEm5;@V$_?=$^QTy%FM-uU!4HRWGyaAqd<)4K;HP>QQxyRLk1h?faFoLn?0WT zflOgQ+p$p=5qgar7OCd!s<0;KnEZsDS_~>gBpAgxQByHkb!#iFj)$P8!U_Bxw-}x3 zripPS=9CBgf!_c;>!nGt2_Kn16cXpsiH>iG^MZ3w%HnLtlp`@>I&K HI!8a0AsHzM#a~Oo~AGn7U7!; z6nNC&bV<6 P5=oUgXD|RM|dkb^}nmB$7trh F%4N*=J8akQVST}T(ME|`Zm?YLudlHA#AYi6VwmnQ94!R^nY z UPlxQKq-Td-SCEG!-DhXdx(AwnboFF2Q^2p*qVczA92} z3cw);lft2A=J7>{JbeA$CyzNKkSjPni2-;Ve}`6=$d;GzA_mmysyn>9C+eajYg=DP zj?4!5O}a;SmzRMxO$ElCwS9!EuK$Bjn1V%HWv8kpC|Jw(y`R8daGO@CG!PR89_!`u zs2zL!h>d>EnZBpBz=+R=xPY9d3TVF=J@Q#r!W?eWb;Pr*PI~pj>;v>+_$3Kk;Tfl} zWQ(eyLl;0`VjKZ@! )+ % zMoSlo*6T2%Z~dDbKc{(RErLL04~ k^!f(IMrqr?VUSg_b;;7yZ$p zTY4*q(mG-=1fb53>3l(T(b&K;(cGBCcEpsBz?cM!pss%x?IRt6UH^nE=d-RONgLD= zFf!DD%4#(^=Ji?OD4?k1B173EEI6YwVmub 0r cy|AMi)K}6`Q z0$2x#(L)z9O9O(GKB~ePIk3`&@!%W`s8USe?`AE;5B;U7cJp(X7NO)tyALC`_=JTE z*iZV*e}(2!ItUjc+Y^)ZRe( o!0D#uVXQRYlhpV` %muolB+a>=`Xr&(mgy5hekg%MHA) zh^xV7oni%WXoh)krgB{ZHUms;a!ExV-k4)2OMn%lO?>C!JW0w!F@Eeo4Wtr>>t2_w z(O >?Nk@II!|@ZYV?W5IJEif>@xNeKn~0 zLim^{?&nla$C!-0FiW+&T!ES7iS0AstOvh-f}Xm&nr!ev-ut!61NILorgQjhomw?_ zNQh?l@J0KH^+fxvIVkL%gt|BniF5y1pO=6@s!g|?)h%;4W?%>l`5>v+E_>|MB;^*S zF1nx 0lqS#96DAMvl;reVI8wwNY#EKLr59VlE=&73Ma r%*JYiE9Me7rX-FK|8 kN-&?05>??x`)<{p$l1Wz!QO`$N${}r1YBL&sDTAMHk>cmqCjn>sZ z8nw8ai{ij&>$ bhJR%6}S*Vlsz&D1pzm)ppxQOr+k=z%k zB_g=r={AGr1vZ#!%SA!D50AXa>|(z`w5x}d5l!i>NetL$mEEU@F0PT`fswIrAH8PB zQzk%{_WnCp3AHo(n7^$#il@xsRF1APJEj!I%Vo>T59?cX=9arb;F=0p?7K7)E#0`t zQc__MC>Bn}HJEksd}m0D6lO?%Zz>IYeDjbw8$J}zZ*MpW@4M=ATYf?sCCd_Y;qCeY zp3t@A8!kwen7xnXyGy?eLX40X&xktFt~%s0<6eQ`O(6xurkS9hm6E#Qb`i=U zv A7ouqjRwKrsqF z%!fI;LBzd#6p!X5%X^~?c3UH_5Xb9wNCt4nRfC>nwe#X8i_>(#Mx0rWkrsoXgDK}) zzyGVirVZ-+d(Yukv)w)qFWh{ z*&hew5;*P}qyyd{kRAWp26<$Q)CR!du4ppxZj zmYH+)Bf+TLiXR>qDfPbb6{RWe)zxaNl#}#fYFNw%GZp95<7qKYAnVt6Rvt_|;+XPR z`9vXsi*iSbF#yWgKK3#wtciaqcTBuILc7gddmer+=#R5domP#fJ%v9T{ZEC>N(D!c zF!di&-7lmJRt{1B36j_q?nDxY9*S4O`#Tsbci9Mt&Sczf?xJ1R5NYjYHSURE;__B7 zMKN}LnJ9Sl!S{dDnJjvZT(AYL<)L=-`FO#os?88-vR&$E@WwogK}1%LTibTw7*|Hz z(k`PzX*r81rwjD!XGIa(@wde3CPc5%!)bx`8X|d@@;Jp2hNDV}lU*r=NeL&O>isqF zvz=N<$q_%}FQ6hz-GMWp5|l%7)B+Yyz&w)y%r#5|Gz6}`>q$d%!0#?}JwO+4( yD|*~#c(?GSTLDlr1vnSIu W>OQkG#Ak!@Xkrno8`P5LXXEYo3sAYl zxJj(#safg~Qu_#6(5_Fcv@=(a< F(sG5Mjfr+? >Mvbmo- zMEVU$dQd`(WDl??6g*B#neWgAE&Q3gC1mL*48Ig6o|eQ1;iq#!KUc3V5suj9N&uA8 zy=gf86>_5F#ZiCO#)7CTTVGrF#>3Tv6Rv6Jy!eyUs)X2;%T4(sxl(Y1qAc?aH9v_( z*8b$Y8m9T?!ofepgvxG&;iAbOWf#JO%65=wly UR(Mw{02}&T8!s=>Nflsb^e(vLGZR(^# a5 zL4={`Rw5FfiqPEEZX$C?{T^|T#aZ)GeLShi4L_f)nc7nQqR)$r+~s_N>^|eF*1Nn5 zSTYHoBjHlS 54cs5AkPVX7#G-B#xr&S}o>V+>VA%JW zLM#VyU}>Az4d0Jv@jOkpScri R66ip%VK#vM8dKe6N^?9?WrSP*2s?(i vcA)nU+6IZtF^ptz5_{@VolSOSw*tkRCY`e&k^HcRU81bnyVjI=y ziM{j`w?SCsD#ztQrdCch7+rv>_D-&)`ogqFR@v6J0b-=?&$LijgWpsxMw%stJrE{P z-vHNj)ch&tsVFQOAA&d~lb59=OIQ@uRNqyUDC&&UkGKruEL&8N MhQg?DpMiu5RL)NsNdWJz*uZ6)QM=%P z=1Zex2`v{SGPY_n;H5P6W;(7PdJYIuBN1MQC_0H}6wt2LJGwzkVSj5<>AaSwn<=d4 zUDQIsf>B#j`pcc_ZF`k8)6^NfB7_%NbHt%5#|Cim6Tcph7e1Tcqjup M4(Im-bki^%m zBgb+rwFZ9SGu0^$LGpLR;PJf7dxp~U-Z?{0d=1%G5A3{xjT&ZRSQTq1%(=d5(Xn z7bW$E1QSpbhM8np67I}+XJtiCV?MR0N|6O{iv9jXNUc1y^oz3jD#|#CR;VZwRBmeG zVp$6B1pB$l5&H#j+x`8KUl(?8Kl%5*^!LDyDv3npmF={{w7zT&t8g>(LU^C?8{~ED z16;L}=P>Dc@s8u%Oe?oAff*VwXHRq`E#l&ZDn*xZyB~S>huPz2p7LEVBO&+Z(Y 533S=#GTAr*@N)i}Sq2wH zVrqj8BfTX}ppA+P`NZCm)Gldxm*%C F7*+&;WvCM*F@~drv+wF9E0hk!v^o70 ztPs0x2~G5*VS=p{u=Z5V4ki9N863-S-Wjl{v|i0T6e5Cj0F>;|opYJ-aZb%p>Ub6e z)7qwf%pm`=`%4pYJj3Pst%K1xKB`mc8cFne`L2=7Pv|#tDz12;*ol+V(dZ3AMgj!5 zCatj}ea>=$BX5yu(amM5jv!h{+Z-FM &tgBn}yJrb0xJ0 =*q0!Q#uuzx+Iu#CcEs#MowN>AYIH|cTcAj28Ug owbJzK31bK|Nqzs0n)^ELys8J=)}h&h^$XGQ$^Ae5Q0kH&IJ$=M-7CZ-mYkHTR% z*M05`H0YS(Br!&7+Dff8=TeLFd*KOl^1vk(MXQ>ivxpr>zF(I#^hzv#vMie@->yWK z%EoT8!YIfrri76WfYUnfAt2kx>K}GWHm5fSNL#!R6e#E~>_ns YM2A@a47KKhM))8+MLl7Z-P#<97ED(QC~FBgx)e81{6R zlP`ngod|5wsVi4(ts}9+R}2M!r{!AT-jA~w9^pIDq Z{D7E$xE{-U~@V5$5&XLqu%1=E{<#dpIVm$wsi^IejK~FKLkE OVuk?qcC!6t`!XIHr>vF9x;_`DHwY8kmfQ6#Z7LP7Y99Tb+ol 3XWZ;N@0@892lqSa2{ zd^Z<8WWj+((0hmsfSJxz4qm ;!R~=_Fkq>K5$Ov_qQj( zr>jR9txT8xeXD;9^7%CFw@Nd7y-bJYuqMvK+(pJN-rnE)2#P_;?ZOoN^^gm-_9Dsz z0^A+s&e0Bj0O8GS9lU$`8k$t!#h><)q8^H)w!8{?9eG2`ckJ0)d2R5_C+M}={2= z2jp3~sqx*^-XX^W8+qx$+ZuZy(|oC0gZk+bGx|3GyT+G{e;HK|;Qb1r`4Zwc+dK %U}eOKkK#gezsT3I1eN|PCC67|%!Ly9(VX-6XW5oGKv_tDsR z H%dPq{%l!#TLg!nI z@tGTmQ1+yjuJ)1r=ckd#v39VyA*KxC@mtq6Hk5W<4qRf1zu6a59 !zi?jeX=caM3GlvG^Rp#;$VkMdmR~t|mre4K<4rj^+?90=&x& z=luSO5kM^CZQsqTEI;-1gWNymwwaF&xbj*MjBoGkftG{}ukDUhyGN1(Ap7@KJ=4s# z-I{*IRG3^_1^<}?WcME# t?EU!+6VRdd(zK4L?XTRl8G^RUFKPk~{l}sG zN)ws&O442RvRTe81RNM9q)i`Vb3v`0SIIsXc=GLI(k4(FvoMp4_D_1IN7e4I1@U&K zJ&{ZAhQHq(E8m=JE-ScWy^VDiO<9nu=f$@LX3fTM-m?2or0eSq{}-XiZ`|e1f0tv2 zY+>#LjwB)Wo&vC<7ZaJ^#My@z`{S;K`fzh<)f|kBf2)xB HP`mV(;tD6A$Ieg!hG51?R0LYoT}??aBeaa>HT=BkOqf>Nz~leRJ%4%5)9t;E z0*QF^TE0@VK91RV2li^xTXnCCNzT=#v C#>_d&ObyUnvO+Sk zTLdb1i>} }SuE<|FFD8~IolZlPRe?pb z_aXz>tyJHGQ#n`BWY$S84zgraO=*s;1VW?^HZbhL>(XO%8z&j?Zk6QO?crxwuS<8m z`@C16t_$QFx$F^ddeL7*5LXpG`r~LyeJoi3r^@*Zxyv*)kC}dcU4S;hK8q?a@r50= z=VpRIn$PB3*T80OcRU=XX4{tfujUODuY)jG)(f>myZXjd {&sgH>}vZG}~WrIoz`n zHlCtQ*Fz2-Nx>O2GUc+T)|bxY0H3wZAm@+SpuK2k)<=*5jAm^`-a$2=(ohIuglP#O zfoBG$`_p S|N z5yod3MK@|%BWOC=26EfU+*0#~Uf{c{r zgdePT VKV!LXIpF}Jrt4Pv?Mw)1> zMbRW!wGwvTMCjBstp_^})=>l;sf03y&zwcdh4?6Ev|V;>=ZmQ)mM6Kt>G`U;qOhw6 zCgK65 {DlY2sLMLuNJ!Xvc#(Xfb^0 &lal z4_{RS-LgchgbYSyQ}d>c!7WNm |6HPlBGgl8t0sl zaLNXE+_Rhv3-+lt{NePYHR+S$c@&yiOvKDWj(~=G&1qA^YV|;cbc}lKE4PDm3mcDC zdlyMuoKsa~e;Hu1W85H#((#MYLM+YSjF^Z&9IVXD0G4r5z!dmeX0SXYr#l*;lQq26 z(TvM#2J(U13tATo28-e^C! fTJjt26pmIRh-}(Afvel#XB)$AmcPuzx`< z7sRf@K4xZ??$m^#f7%vaqXhtzFOR(MqQ{?SxY8fM*t5ME_HTHq0{5JYCTaNXxvah4 zyDm*e+fjIx_Cts|mE#9+WP7yB(X@r2@Het@=L$TFif$;RuxX6by(NZ@a8r2dtWmcd zQDOzpI2{q-wY~tErKW5nU)}StPXA`PfKH#N3=3Dt2z54=N!Ue}sMyE$zbN$4N5L}E z?!q%USW z>7L>2#On%WVUq%c1RO(9&0Y~OWkb+v0Z`V401p!BbK9bADyLYHi!&kOzT}Xd0cw!w zqf3Av%vDvf>7No$@=hL#m HyJnE^aFW|czNbTHFG8yb;qsYNV8ad!H8MKEMdH+h1t8=IDp;$nD4 z+xM9Me2`EN6JeNOfFPRZcYKH+YDs-cPhHCnu0`cvT?SwLplYwcj`!cvZ@?0IY_ELp z^LLwB{!IG6OG$lLs{gR$IZB{r2;@qhEXH5(hF(9sDW8xE))?`ZQRKDYHR!umJ@>zH zzRUVYtv;hFx`7MSX wf8NuXDZ|8KZ6vKn}Q?>~!kHeWi10W9i-T8Z&fyyB=g9 %-j}dIwgt@ie _t4OETE<#z^#N|#AAVO$(YkN zNT$;NtFUYj{G5g=u_z4L%6=|@qlqur&*ImA8Xu-Wtb_rW|FBnp`*bEcY|<|$4b zrd(;%1Q^%|aDkC& oi^n>oq^2q#cC7)USx@3vT?SSY(Z)q ziyY(UqMQ1??_U7q&M1aL0LnbW^tUr~1x%YiB!L^U;%Z$Que+h!^<4D5$_Yq;5y6%| z?BuUW6R85Cikfqrn-~txAQ|CY{}`XX_h+xtbrXTcfTJo+`A}NCVnCWq>7H!9f(-qp zI6e)&+8n0aN)vjSoMmNI#BEi=#H}i2HViObnzON?!m5o`(`4AbJw`A5s@ORe2Euw< z+xd^|TWqIT#bCv{0+^({9i#$Rz9bgpWCkAsSED^5->LFR(#qb_MXS(E-*}*C(k( awfPKETx@DFw7~|J! tp#bVg!FDYfNivr z;de(@%wyId_23ZQ>~UfzYv2irXBP2-BK;r6HfqMYmF^bR00Pa-)-`N0hFH5}v62?^ z-H#p~xwJ?O4cGoC`k}vEqDH}TapUf}X5WuwS+O8loxn(oiyCiZGbjUp{T!8ww6Lp5 z{|NmZspVsb6KC!wB2iPf FZA-YI%Tl9xHbk#u*WiMh3ix+O@8dv4&yLGk-{$8>Og&S~njt&9;jXXY=s#S& zffZZl(?2k(BhC)K_Gf5-A_M4a?K4k-Z@1TGJO9Fd8!~ez67U?2Pk7>)CxP%s0O<<- zOqiO?zC?oIT2Rs8_;-OMeDlgmcWxW>O*gw+4(q!&*P?!vs-mo|0p{&QzwtHgv6>_f z >DJbVs`dgY>5F4J@QoM%~B{@4%@7A-3UtiuYh2t=Fn7}L$YLgy_n{n<$D&x$lB zz1&(xSznV3vlW5N(!3&8DxZ${pn0x_DA7W-3A3Ka!LqLuiuDyCCS7w=Sc|;SAWW}6Go6O!xXM*q5m1`z&S2wx$!SXW*bLag=~v@&zL zoD#9D%2wJ|wN|x8S|!Rth6)YD*mIJSsSkO8p~S8bg^P?LR65fFc$aq6Ryfqwc`$te z@x7X6uPXcl82;jPiy?-$dx7S$Yh-qHIed&=40j7qDQ70ju!^DiG0T%(9M?vnK>A4M z71Cm~`dn`1<+p4&U8fk%vXLM%j3i~Wl@9){`t9-EwA@#N-<)P&DrgY*TO`oHid$Vp zEH4V`{e&JOs!s|S(Qqp6)P;NPbm{oP&(HBN2%aCvJT^Lnx5b~D{cZfDy|7rv?BBqd zi%I 65*9c3Y=oTDM zbDY^8j7^2Y-rWxA8Vi56$o0^o>SY=0ziR(#qpo=OFj=%sE*%2os|{jK lho%bF+vMC_>q0BnAzpA}M7y!21#heJ={3N)}cL!(wljbmQ)Tg9VK9 zHBsc`jiDmU?`ukZbIoW@`N|*yZP;_G0))0Id`4gL*cpf>c8E=CN}!ZtEjCb|E)tE9 zj-WcNI56 tVM4e~&F356wF<6|F#j^4e70qm8T>Ol1{*liB_ zyXG nYQIh;lB(pO-X}`lD0w@tR?!_Ed`D0Q!Sev`iHz& zDoI0k_>T>3-K5SWsbdsiS8+zOk-sqaz_tnO?h>C#9v9-vt8u63Zb+@5D!rfaQaQE9 z+uB^4)pc~v45J6dp)PXRZrL6p7RDqe@vQJjM+R{)_eI>}wi`}`9J`*gAp$50KbbK| zC4?q8=IuYw28od7?M-Ru5Nz9l@Nj@wMv%rzcS*L^<>sLUk&?t%OV8(X4dttqUJU5O zKYuEnRbEjnV|3UWUFPS<(49SO@2O4J+Ft=$0ANiDCO0S`_nwI$_E=2`?8p_UFSd4g zq1{-drW<^V@Zau6l${Ig<)vkG<_u{jFr%@^2W%1MZgXV0nb(sH7_@FL49ei<1AX`t zE2(9{EBC}y-~3uE+2^3B<%8+5sS1+p-W|8|%?5&0Eg?4J1qCC~Z>L-kKEo_$-lT{g zTLs#{W+-zDF5qjrqbjr~tD7Gg;U2u|%U6~vaEV`#IXL5?m>=WpRxJq;Qz%v>nlaYx zb#dZHtt3-P0>CmG;COfLH?`1kcG0awkBr@&g5gfrF113qU>x}|gm*(kUopq6i{X`N zW;VC`_lP-b6K^&LQq7#8csX3j7t1pU8eF0)Mk;(U93v-sGhBi^HsZC~(#%solz7ES ze30LugP3RXRwwYn4sj@@HujyZ@IS7)q_#F7v89wNxCGEr?;EMwrv|h0Q{`B3@d`QT zDtMj@fB1e1HG^O8LREH9rmxHEXGWcVpJ501TAaM_}{4a!lX+--%EU*gsy4HqY#N zFxB$DwNx)?DZm5JTSYarwXo3;mF)AL;9AAsQgTa~P5<4ycJFt`bq1EdYBwNM(~d9D zOpw1wu7G|=J4o&1OA$dj%_j+g*uGiee^=~eQ0^qK&h_zH+e5EZ>jtGSLw{gHU{ejb zkY-;*I>nvzn}?)A9nA4Fv(epALf(|z!qaarn NJ{w?^v*a>=VWJ3iU$MmT0<1iAj>a4Fh`{dndyrKo=% z8fPq*KMDfsQ;b#;%jUJW117W4W1|FuM_WX`PFoj*?fpSLJyT?zarryhV4!g f`cax3W5UTGMx ab$)VYJ@0I3XJzMV!)!zMUHX#w zSV~t7KAr74|7_j59<0*#E ;mKFEZe^&|-n5;2 zI4s)kz|>G{b~kfXw{#23SyE~J(iBEFauNnAS0jGS?-=RvFN)|wnTUGUX?N;MPvt>Q z(T0$6%PJir#UlzE)xr pYZ%PG`89fvbVr1`^Tj+%wsK&XlLWTJ z@1A+CtqnUfJL4-KNFWfuUa jyFUPqpa7pLlaT?7}#m3e2pP*laYr*X}5Mrvuda$!G)={zxO%~L{?by|yG^dN= zBKi+S%QXRkCZ#mVlVs!r{-VlFu+ic)DJKCip>*3qweroZWo~aaZcz<91VT5Qf&cyr z(!Jzu5R7R0<=5}tf!Zl7pJiY;RVREeKS(46UqP%pNJ1}oPQNys+>>ifLV6LeRt6k< zkXkG*&Wej$Ms;xH#;5XPFt4lSsSpf^gkOiplx=f~EjjeloHNH9et6?Wvjiyl!hLc8 zk{aEff6)-e*V_hFd})xs-+yr=(0nvD<^J0__p^9{_2m**sl`h%N{qFk+SSgimVDkM zH_@{vxPb{MR+DK}%9eB5AaGmX2z5qWM!Yc7ph72vho!4wa3TEN-s7_$bt^4|16`K1 zhK{$4kR|f4Fd1AO6U&co^;G_)>{n^SJ?ibaT6*!TeBz^tE5Lx4%@8hHzKYhJ!PGE| zl*fKd0;|9_)*bq$qN!#GH>p$Aox5;rT8IDT M^|lE0b~=aU zlCFSu3#1rYg0OgdkpdFw&SQW6c&{H^o<4R*g1Fj{BoA&A(7L9+soN#g`uzcQs>D>~ zgMx*(0*XxZl1g$-+Py>qODYvV8i@A{4D?7tf(v{!L9H`A{yAzE$~JE|I>s>#%L(h@ zcgBpi*Y%y=`8}U5If8PoMzwI;L&@U{f)m6DM`aSM%qe^ZZ6! oBF;w!oV$@-3gPN5Rq!Dl*K&ho?Wo za}rpK2p6LB16PeZ`b5bs@O+8#hN4uH@^)cBTSu)iYTjqE*#5Q*I@wJGXZE z`3k=|F-lzPLA;Gww<94_@UX|2E+I1f2TR3?0|+mr2NF<(B3n=aqi_gSqwIhzqPB2O z6y4X-yjWS&-~5Zo g6V)iA0DAQ8DhrDtK(b@ #d|CSICd-eN&766l~F`;q9^!MIW7bRQZ_D#4v&%rGW?uY39Vl=R>Dd=4AR9%*Aoy zvorX#@2wWcroiT|^3B=(dH3MNkgJ``?hmsO7f)Q-Fd9nwMdSj}E)>u{WyBkY+pvCE z4}Zdxkeco$w$X)rviLTpzyW_jfhOX@KxD4MYMp&(5;@cCMw_5a{g)U?Mw2cENCl*x znh$`_MdvNk+63J`R&;|xRGm45-v#e_BOX$r?pK9%5h!pbubTmS`G;AC<^t A`~MD#bpKXQ95DS&4l*0q{*H;PhIZMHOn_rCVF5lQU~B zhs^7^tBUA6W3uN_H;?i!xuyX*in`gJzBVQ0Q^-bPI6?Mgm-&z}pl}?BG%jp^E ujaZREGQA3?u6NMs!aQE~2 zeyBr6RM0IF^i^j8$GfSrk@gO|lU)#P^oa+{bg}^jKHp&PwKUT+O;Sp>v--FVRH;`c zC9bw`S9U^l#>v6n+0k{F0bh!}pc8a&`XEi$2uN8}Gv?&fhZA-?swDBQm_GQh&Wc>Q zq7Ihm^u?$8AmowL*K6}a`~%(aPq-nl2`P3ss8({)^ca#-I#J*+Z^zk~*6Ws_(Wpv$ zX{ Z+_s#{fS*rXMShC z6tf+~>(S_V={m8!L-YeD2Z13MrmYvSaYI?z`0f*waO&jKf9${u*hqTiS;Kt*Q!+;q zYHAF?CQMCJck>T)HETyJ%Ubvncwa|OUObf|Di_i)L}=ya^z!X_r`aaR-kRl=wV~zJ z`Eqb(ip(v;YV4`ipf{s#183Ou-52}mOttp^cIEH$)4{!RM-D-))^U!^Dg>*f_M8vO zjBU*PID15qN$=affk#GtPi1B865KkQgQ}Nw619XxVyas8d ppkB$ zdK&4fztkp|P-rj_s1Dt59m3(a5h@~~St}SX#uh@=G75IXfEG5?%>5+QHhi0gsZu0W zD6B~xPxMOY<@VN> aWm#t6vVN}gS!A9FR!A%)?ER~ zuXQIigx~!_cHT`CRuC3O{AqZBL5KO6MTXhnUYR0CVi5IDxL?5gKFKf9FcjQjHHKcy zZvgZ8ga)jFq9=Bh&(y8;*g-IHjt17FVX|QHdoUywuGy;ntW*IKc5YVX*hPJ{HRn_e za!I;Y#Xqc3z-fY*P)Q{~z0KnjKaEm?Nr)_#ym%$!-Js&jOa0`aHzu%VNXp!Pd!w%E zfOIR64LWvcp%Q47TLoGxiMznO2* 6l z@WyxQhsC(1#y6x #grDGR8E}*CDad9|5+aJnOn?mTNOnU=4Mvv49thqaVrOZJ!>{X=|+of z_e%t-(zoY8Lu y8Oe52al8bPqMAJX80)s9;YW%V&KM)SsK+>({!Yr2H@I?ya=< z*SgC(`48*PDm83&EnzeO+-KlzcwdFHo)yzO6wZA%mxpcv*?%yG4C))ffCQI>ub+M( z<^S2Fu8q<^KZ@KxY% Zu{vA?)F+S{`WAL-OI1`vf%aK zjHjR0eCPQ4LYC*=zQ1O5zxCdIU-&=o$J>M-@9sZ{R1TX~V9@o^D)JYwx5daL&J4P{ zih*H8>}owQ14<|}@IqMv41XO#OtfVXx(0d%dWO2id5Hx^1_s6kriO;VJ4aX z1S^tq67#a5N96 H1x?bUj01#EFY1vaZJQSAN%j80 M!gLf+4clrlo0J> b+pdf%!>^RL`N`5{6+d_ArH&cEHgUU~wI*O1!o3-Z<`}6%?+h zcSRxl@eo;-;kh{q!^?<}M!gFK*%cYu cTQBFWQl@@3X=u}#CYcP*UMYd*}4N=y>4@?DG0y;1i-4al6q8>4d zY{_j~qAY *BMDY6~gGKjJRt?oiBFU4Ug=Eyj* zrAakJS&A}F4zvg~u! u{9875xg9Oj4A@H0*xx7 k`w4SQ3fU^vCK9cJj8LIO6e}C3x{(6HU~UG63!NYy0IK#y!T - Manual is at http://php.weblogs.com/adodb_manual - - */ - - if (!defined('_ADODB_LAYER')) { - define('_ADODB_LAYER',1); - - //============================================================================================== - // CONSTANT DEFINITIONS - //============================================================================================== - - define('ADODB_BAD_RS',' Bad $rs in %s. Connection or SQL invalid. Try using $connection->debug=true;
'); - - define('ADODB_FETCH_DEFAULT',0); - define('ADODB_FETCH_NUM',1); - define('ADODB_FETCH_ASSOC',2); - define('ADODB_FETCH_BOTH',3); - - /* - Controls ADODB_FETCH_ASSOC field-name case. Default is 2, use native case-names. - This currently works only with mssql, odbc, oci8po and ibase derived drivers. - - 0 = assoc lowercase field names. $rs->fields['orderid'] - 1 = assoc uppercase field names. $rs->fields['ORDERID'] - 2 = use native-case field names. $rs->fields['OrderID'] - */ - if (!defined('ADODB_ASSOC_CASE')) define('ADODB_ASSOC_CASE',2); - - // allow [ ] @ ` and . in table names - define('ADODB_TABLE_REGEX','([]0-9a-z_\`\.\@\[-]*)'); - - - if (!defined('ADODB_PREFETCH_ROWS')) define('ADODB_PREFETCH_ROWS',10); - - /** - * Set ADODB_DIR to the directory where this file resides... - * This constant was formerly called $ADODB_RootPath - */ - if (!defined('ADODB_DIR')) define('ADODB_DIR',dirname(__FILE__)); - - define('TIMESTAMP_FIRST_YEAR',100); - - //============================================================================================== - // GLOBAL VARIABLES - //============================================================================================== - - GLOBAL - $ADODB_vers, // database version - $ADODB_Database, // last database driver used - $ADODB_COUNTRECS, // count number of records returned - slows down query - $ADODB_CACHE_DIR, // directory to cache recordsets - $ADODB_EXTENSION, // ADODB extension installed - $ADODB_COMPAT_PATCH, // 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... - - //============================================================================================== - // GLOBAL SETUP - //============================================================================================== - - if (strnatcmp(PHP_VERSION,'4.3.0')>=0) { - define('ADODB_PHPVER',0x4300); - } else if (strnatcmp(PHP_VERSION,'4.2.0')>=0) { - define('ADODB_PHPVER',0x4200); - } else if (strnatcmp(PHP_VERSION,'4.0.5')>=0) { - define('ADODB_PHPVER',0x4050); - } else { - define('ADODB_PHPVER',0x4000); - } - $ADODB_EXTENSION = defined('ADODB_EXTENSION'); - //if (extension_loaded('dbx')) define('ADODB_DBX',1); - - /** - Accepts $src and $dest arrays, replacing string $data - */ - function ADODB_str_replace($src, $dest, $data) - { - if (ADODB_PHPVER >= 0x4050) return str_replace($src,$dest,$data); - - $s = reset($src); - $d = reset($dest); - while ($s !== false) { - $data = str_replace($s,$d,$data); - $s = next($src); - $d = next($dest); - } - return $data; - } - - function ADODB_Setup() - { - GLOBAL - $ADODB_vers, // database version - $ADODB_Database, // last database driver used - $ADODB_COUNTRECS, // count number of records returned - slows down query - $ADODB_CACHE_DIR, // directory to cache recordsets - $ADODB_FETCH_MODE; - - $ADODB_FETCH_MODE = ADODB_FETCH_DEFAULT; - - if (!isset($ADODB_CACHE_DIR)) { - $ADODB_CACHE_DIR = '/tmp'; - } else { - // do not accept url based paths, eg. http:/ or ftp:/ - if (strpos($ADODB_CACHE_DIR,'://') !== false) - die("Illegal path http:// or ftp://"); - } - - - // Initialize random number generator for randomizing cache flushes - srand(((double)microtime())*1000000); - - /** - * Name of last database driver loaded into memory. Set by ADOLoadCode(). - */ - $ADODB_Database = ''; - - /** - * ADODB version as a string. - */ - $ADODB_vers = 'V3.40 7 April 2003 (c) 2000-2003 John Lim (jlim@natsoft.com.my). All rights reserved. Released BSD & LGPL.'; - - /** - * Determines whether recordset->RecordCount() is used. - * Set to false for highest performance -- RecordCount() will always return -1 then - * for databases that provide "virtual" recordcounts... - */ - $ADODB_COUNTRECS = true; - } - - - //============================================================================================== - // CHANGE NOTHING BELOW UNLESS YOU ARE CODING - //============================================================================================== - - ADODB_Setup(); - - //============================================================================================== - // CLASS ADOFieldObject - //============================================================================================== - /** - * Helper class for FetchFields -- holds info on a column - */ - class ADOFieldObject { - var $name = ''; - var $max_length=0; - var $type=""; - - // additional fields by dannym... (danny_milo@yahoo.com) - var $not_null = false; - // actually, this has already been built-in in the postgres, fbsql AND mysql module? ^-^ - // so we can as well make not_null standard (leaving it at "false" does not harm anyways) - - var $has_default = false; // this one I have done only in mysql and postgres for now ... - // others to come (dannym) - var $default_value; // default, if any, and supported. Check has_default first. - } - - - //============================================================================================== - // CLASS ADOConnection - //============================================================================================== - - include_once(ADODB_DIR.'/adodb-connection.inc.php'); - - - - //============================================================================================== - // CLASS ADOFetchObj - //============================================================================================== - - /** - * Internal placeholder for record objects. Used by ADORecordSet->FetchObj(). - */ - class ADOFetchObj { - }; - - //============================================================================================== - // CLASS ADORecordSet_empty - //============================================================================================== - - /** - * Lightweight recordset when there are no records to be returned - */ - class ADORecordSet_empty - { - var $dataProvider = 'empty'; - var $databaseType = false; - var $EOF = true; - var $_numOfRows = 0; - var $fields = false; - var $connection = false; - function RowCount() {return 0;} - function RecordCount() {return 0;} - function PO_RecordCount(){return 0;} - function Close(){return true;} - function FetchRow() {return false;} - function FieldCount(){ return 0;} - } - - //============================================================================================== - // DATE AND TIME FUNCTIONS - //============================================================================================== - include_once(ADODB_DIR.'/adodb-time.inc.php'); - - //============================================================================================== - // CLASS ADORecordSet - //============================================================================================== - include_once(ADODB_DIR.'/adodb-recordset.inc.php'); - - //============================================================================================== - // CLASS ADORecordSet_array - //============================================================================================== - - /** - * This class encapsulates the concept of a recordset created in memory - * as an array. This is useful for the creation of cached recordsets. - * - * Note that the constructor is different from the standard ADORecordSet - */ - - class ADORecordSet_array extends ADORecordSet - { - var $databaseType = 'array'; - - var $_array; // holds the 2-dimensional data array - var $_types; // the array of types of each column (C B I L M) - var $_colnames; // names of each column in array - var $_skiprow1; // skip 1st row because it holds column names - var $_fieldarr; // holds array of field objects - var $canSeek = true; - var $affectedrows = false; - var $insertid = false; - var $sql = ''; - var $compat = false; - /** - * Constructor - * - */ - function ADORecordSet_array($fakeid=1) - { - global $ADODB_FETCH_MODE,$ADODB_COMPAT_FETCH; - - // fetch() on EOF does not delete $this->fields - $this->compat = !empty($ADODB_COMPAT_FETCH); - $this->ADORecordSet($fakeid); // fake queryID - $this->fetchMode = $ADODB_FETCH_MODE; - } - - - /** - * Setup the Array. Later we will have XML-Data and CSV handlers - * - * @param array is a 2-dimensional array holding the data. - * The first row should hold the column names - * unless paramter $colnames is used. - * @param typearr holds an array of types. These are the same types - * used in MetaTypes (C,B,L,I,N). - * @param [colnames] array of column names. If set, then the first row of - * $array should not hold the column names. - */ - function InitArray($array,$typearr,$colnames=false) - { - $this->_array = $array; - $this->_types = $typearr; - if ($colnames) { - $this->_skiprow1 = false; - $this->_colnames = $colnames; - } else $this->_colnames = $array[0]; - - $this->Init(); - } - /** - * Setup the Array and datatype file objects - * - * @param array is a 2-dimensional array holding the data. - * The first row should hold the column names - * unless paramter $colnames is used. - * @param fieldarr holds an array of ADOFieldObject's. - */ - function InitArrayFields($array,$fieldarr) - { - $this->_array = $array; - $this->_skiprow1= false; - if ($fieldarr) { - $this->_fieldobjects = $fieldarr; - } - $this->Init(); - } - - function GetArray($nRows=-1) - { - if ($nRows == -1 && $this->_currentRow <= 0 && !$this->_skiprow1) { - return $this->_array; - } else { - return ADORecordSet::GetArray($nRows); - } - } - - function _initrs() - { - $this->_numOfRows = sizeof($this->_array); - if ($this->_skiprow1) $this->_numOfRows -= 1; - - $this->_numOfFields =(isset($this->_fieldobjects)) ? - sizeof($this->_fieldobjects):sizeof($this->_types); - } - - /* Use associative array to get fields array */ - function Fields($colname) - { - if ($this->fetchMode & ADODB_FETCH_ASSOC) 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)]]; - } - - function &FetchField($fieldOffset = -1) - { - if (isset($this->_fieldobjects)) { - return $this->_fieldobjects[$fieldOffset]; - } - $o = new ADOFieldObject(); - $o->name = $this->_colnames[$fieldOffset]; - $o->type = $this->_types[$fieldOffset]; - $o->max_length = -1; // length not known - - return $o; - } - - function _seek($row) - { - if (sizeof($this->_array) && $row < $this->_numOfRows) { - $this->fields = $this->_array[$row]; - return true; - } - return false; - } - - function MoveNext() - { - if (!$this->EOF) { - $this->_currentRow++; - - $pos = $this->_currentRow; - if ($this->_skiprow1) $pos += 1; - - if ($this->_numOfRows <= $pos) { - if (!$this->compat) $this->fields = false; - } else { - $this->fields = $this->_array[$pos]; - return true; - } - $this->EOF = true; - } - - return false; - } - - function _fetch() - { - $pos = $this->_currentRow; - if ($this->_skiprow1) $pos += 1; - - if ($this->_numOfRows <= $pos) { - if (!$this->compat) $this->fields = false; - return false; - } - - $this->fields = $this->_array[$pos]; - return true; - } - - function _close() - { - return true; - } - - } // ADORecordSet_array - - //============================================================================================== - // HELPER FUNCTIONS - //============================================================================================== - - /** - * Synonym for ADOLoadCode. - * - * @deprecated - */ - function ADOLoadDB($dbType) - { - return ADOLoadCode($dbType); - } - - /** - * Load the code for a specific database driver - */ - function ADOLoadCode($dbType) - { - GLOBAL $ADODB_Database; - - if (!$dbType) return false; - $ADODB_Database = strtolower($dbType); - switch ($ADODB_Database) { - case 'maxsql': $ADODB_Database = 'mysqlt'; break; - case 'postgres': - case 'pgsql': $ADODB_Database = 'postgres7'; break; - } - // Karsten Kraus- return @include_once(ADODB_DIR."/drivers/adodb-".$ADODB_Database.".inc.php"); - } - - /** - * synonym for ADONewConnection for people like me who cannot remember the correct name - */ - function &NewADOConnection($db='') - { - return ADONewConnection($db); - } - - /** - * Instantiate a new Connection class for a specific database driver. - * - * @param [db] is the database Connection object to create. If undefined, - * use the last database driver that was loaded by ADOLoadCode(). - * - * @return the freshly created instance of the Connection class. - */ - function &ADONewConnection($db='') - { - GLOBAL $ADODB_Database; - - $rez = true; - if ($db) { - if ($ADODB_Database != $db) ADOLoadCode($db); - } else { - if (!empty($ADODB_Database)) { - ADOLoadCode($ADODB_Database); - } else { - $rez = false; - } - } - - $errorfn = (defined('ADODB_ERROR_HANDLER')) ? ADODB_ERROR_HANDLER : false; - if (!$rez) { - if ($errorfn) { - // raise an error - $errorfn('ADONewConnection', 'ADONewConnection', -998, - "could not load the database driver for '$db", - $dbtype); - } else - ADOConnection::outp( " ADONewConnection: Unable to load database driver '$db'
",false); - - return false; - } - - $cls = 'ADODB_'.$ADODB_Database; - $obj = new $cls(); - if ($errorfn) { - $obj->raiseErrorFn = $errorfn; - } - return $obj; - } - - function &NewDataDictionary(&$conn) - { - $provider = $conn->dataProvider; - if ($provider !== 'native' && $provider != 'odbc' && $provider != 'ado') - $drivername = $conn->dataProvider; - else { - $drivername = $conn->databaseType; - if (substr($drivername,0,5) == 'odbc_') $drivername = substr($drivername,5); - else if (substr($drivername,0,4) == 'ado_') $drivername = substr($drivername,4); - else if ($drivername == 'oracle') $drivername = 'oci8'; - } - include_once(ADODB_DIR.'/adodb-lib.inc.php'); - include_once(ADODB_DIR.'/adodb-datadict.inc.php'); - $path = ADODB_DIR."/datadict/datadict-$drivername.inc.php"; - - if (!file_exists($path)) { - ADOConnection::outp("Database driver '$path' not available"); - return false; - } - include_once($path); - $class = "ADODB2_$drivername"; - $dict = new $class(); - $dict->connection = &$conn; - $dict->upperName = strtoupper($drivername); - if (is_resource($conn->_connectionID)) - $dict->serverInfo = $conn->ServerInfo(); - - return $dict; - } - - - /** - * Save a file $filename and its $contents (normally for caching) with file locking - */ - function adodb_write_file($filename, $contents,$debug=false) - { - # http://www.php.net/bugs.php?id=9203 Bug that flock fails on Windows - # So to simulate locking, we assume that rename is an atomic operation. - # First we delete $filename, then we create a $tempfile write to it and - # rename to the desired $filename. If the rename works, then we successfully - # modified the file exclusively. - # What a stupid need - having to simulate locking. - # Risks: - # 1. $tempfile name is not unique -- very very low - # 2. unlink($filename) fails -- ok, rename will fail - # 3. adodb reads stale file because unlink fails -- ok, $rs timeout occurs - # 4. another process creates $filename between unlink() and rename() -- ok, rename() fails and cache updated - if (strpos(strtoupper(PHP_OS),'WIN') !== false) { - // skip the decimal place - $mtime = substr(str_replace(' ','_',microtime()),2); - // unlink will let some latencies develop, so uniqid() is more random - @unlink($filename); - // getmypid() actually returns 0 on Win98 - never mind! - $tmpname = $filename.uniqid($mtime).getmypid(); - if (!($fd = fopen($tmpname,'a'))) return false; - $ok = ftruncate($fd,0); - if (!fwrite($fd,$contents)) $ok = false; - fclose($fd); - chmod($tmpname,0644); - if (!@rename($tmpname,$filename)) { - unlink($tmpname); - $ok = false; - } - if (!$ok) { - if ($debug) ADOConnection::outp( " Rename $tmpname ".($ok? 'ok' : 'failed')); - } - return $ok; - } - if (!($fd = fopen($filename, 'a'))) return false; - if (flock($fd, LOCK_EX) && ftruncate($fd, 0)) { - $ok = fwrite( $fd, $contents ); - fclose($fd); - chmod($filename,0644); - }else { - fclose($fd); - if ($debug)ADOConnection::outp( " Failed acquiring lock for $filename
\n"); - $ok = false; - } - - return $ok; - } - - - function adodb_backtrace($print=true) - { - $s = ''; - if (PHPVERSION() >= 4.3) { - - $MAXSTRLEN = 64; - - $s = ''; - $traceArr = debug_backtrace(); - array_shift($traceArr); - $tabs = sizeof($traceArr)-1; - foreach ($traceArr as $arr) { - for ($i=0; $i < $tabs; $i++) $s .= ' '; - $tabs -= 1; - $s .= ''; - if (isset($arr['class'])) $s .= $arr['class'].'.'; - foreach($arr['args'] as $v) { - if (is_null($v)) $args[] = 'null'; - else if (is_array($v)) $args[] = 'Array['.sizeof($v).']'; - else if (is_object($v)) $args[] = 'Object:'.get_class($v); - else if (is_bool($v)) $args[] = $v ? 'true' : 'false'; - else { - $v = (string) @$v; - $str = htmlspecialchars(substr($v,0,$MAXSTRLEN)); - if (strlen($v) > $MAXSTRLEN) $str .= '...'; - $args[] = $str; - } - } - - $s .= $arr['function'].'('.implode(', ',$args).')'; - $s .= sprintf(" # line %4d, file: %s", - $arr['line'],$arr['file'],$arr['file']); - $s .= "\n"; - } - $s .= ''; - if ($print) print $s; - } - return $s; - } - -} // defined -?> \ No newline at end of file + + Manual is at http://php.weblogs.com/adodb_manual + + */ + + if (!defined('_ADODB_LAYER')) { + define('_ADODB_LAYER',1); + + /* ============================================================================================== */ + /* CONSTANT DEFINITIONS */ + /* ============================================================================================== */ + + define('ADODB_BAD_RS','Bad $rs in %s. Connection or SQL invalid. Try using $connection->debug=true;
'); + + define('ADODB_FETCH_DEFAULT',0); + define('ADODB_FETCH_NUM',1); + define('ADODB_FETCH_ASSOC',2); + define('ADODB_FETCH_BOTH',3); + + /* + Controls ADODB_FETCH_ASSOC field-name case. Default is 2, use native case-names. + This currently works only with mssql, odbc, oci8po and ibase derived drivers. + + 0 = assoc lowercase field names. $rs->fields['orderid'] + 1 = assoc uppercase field names. $rs->fields['ORDERID'] + 2 = use native-case field names. $rs->fields['OrderID'] + */ + if (!defined('ADODB_ASSOC_CASE')) define('ADODB_ASSOC_CASE',2); + + /* allow [ ] @ ` and . in table names */ + define('ADODB_TABLE_REGEX','([]0-9a-z_\`\.\@\[-]*)'); + + + if (!defined('ADODB_PREFETCH_ROWS')) define('ADODB_PREFETCH_ROWS',10); + + /** + * Set ADODB_DIR to the directory where this file resides... + * This constant was formerly called $ADODB_RootPath + */ + if (!defined('ADODB_DIR')) define('ADODB_DIR',dirname(__FILE__)); + + if (!defined('TIMESTAMP_FIRST_YEAR')) define('TIMESTAMP_FIRST_YEAR',100); + + /* ============================================================================================== */ + /* GLOBAL VARIABLES */ + /* ============================================================================================== */ + + GLOBAL + $ADODB_vers, /* database version */ + $ADODB_Database, /* last database driver used */ + $ADODB_COUNTRECS, /* count number of records returned - slows down query */ + $ADODB_CACHE_DIR, /* directory to cache recordsets */ + $ADODB_EXTENSION, /* ADODB extension installed */ + $ADODB_COMPAT_PATCH, /* 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... */ + + /* ============================================================================================== */ + /* GLOBAL SETUP */ + /* ============================================================================================== */ + + if (strnatcmp(PHP_VERSION,'4.3.0')>=0) { + define('ADODB_PHPVER',0x4300); + } else if (strnatcmp(PHP_VERSION,'4.2.0')>=0) { + define('ADODB_PHPVER',0x4200); + } else if (strnatcmp(PHP_VERSION,'4.0.5')>=0) { + define('ADODB_PHPVER',0x4050); + } else { + define('ADODB_PHPVER',0x4000); + } + $ADODB_EXTENSION = defined('ADODB_EXTENSION'); + /* if (extension_loaded('dbx')) define('ADODB_DBX',1); */ + + /** + Accepts $src and $dest arrays, replacing string $data + */ + function ADODB_str_replace($src, $dest, $data) + { + if (ADODB_PHPVER >= 0x4050) return str_replace($src,$dest,$data); + + $s = reset($src); + $d = reset($dest); + while ($s !== false) { + $data = str_replace($s,$d,$data); + $s = next($src); + $d = next($dest); + } + return $data; + } + + function ADODB_Setup() + { + GLOBAL + $ADODB_vers, /* database version */ + $ADODB_Database, /* last database driver used */ + $ADODB_COUNTRECS, /* count number of records returned - slows down query */ + $ADODB_CACHE_DIR, /* directory to cache recordsets */ + $ADODB_FETCH_MODE; + + $ADODB_FETCH_MODE = ADODB_FETCH_DEFAULT; + + if (!isset($ADODB_CACHE_DIR)) { + $ADODB_CACHE_DIR = '/tmp'; + } else { + /* do not accept url based paths, eg. http:/ or ftp:/ */ + if (strpos($ADODB_CACHE_DIR,':/* ') !== false) + die("Illegal path http:/* or ftp://"); + } + + + /* Initialize random number generator for randomizing cache flushes */ + srand(((double)microtime())*1000000); + + /** + * Name of last database driver loaded into memory. Set by ADOLoadCode(). + */ + $ADODB_Database = ''; + + /** + * ADODB version as a string. + */ + $ADODB_vers = 'V3.60 16 June 2003 (c) 2000-2003 John Lim (jlim@natsoft.com.my). All rights reserved. Released BSD & LGPL.'; + + /** + * Determines whether recordset->RecordCount() is used. + * Set to false for highest performance -- RecordCount() will always return -1 then + * for databases that provide "virtual" recordcounts... + */ + $ADODB_COUNTRECS = true; + } + + + /* ============================================================================================== */ + /* CHANGE NOTHING BELOW UNLESS YOU ARE CODING */ + /* ============================================================================================== */ + + ADODB_Setup(); + + /* ============================================================================================== */ + /* CLASS ADOFieldObject */ + /* ============================================================================================== */ + /** + * Helper class for FetchFields -- holds info on a column + */ + class ADOFieldObject { + var $name = ''; + var $max_length=0; + var $type=""; + + /* additional fields by dannym... (danny_milo@yahoo.com) */ + var $not_null = false; + /* actually, this has already been built-in in the postgres, fbsql AND mysql module? ^-^ */ + /* so we can as well make not_null standard (leaving it at "false" does not harm anyways) */ + + var $has_default = false; /* this one I have done only in mysql and postgres for now ... */ + /* others to come (dannym) */ + var $default_value; /* default, if any, and supported. Check has_default first. */ + } + + + + function ADODB_TransMonitor($dbms, $fn, $errno, $errmsg, $p1, $p2, &$thisConnection) + { + /* print "Errorno ($fn errno=$errno m=$errmsg) "; */ + + $thisConnection->_transOK = false; + if ($thisConnection->_oldRaiseFn) { + $fn = $thisConnection->_oldRaiseFn; + $fn($dbms, $fn, $errno, $errmsg, $p1, $p2,$thisConnection); + } + } + + /* ============================================================================================== */ + /* CLASS ADOConnection */ + /* ============================================================================================== */ + + /** + * Connection object. For connecting to databases, and executing queries. + */ + class ADOConnection { + /* */ + /* PUBLIC VARS */ + /* */ + var $dataProvider = 'native'; + var $databaseType = ''; /* / RDBMS currently in use, eg. odbc, mysql, mssql */ + var $database = ''; /* / Name of database to be used. */ + var $host = ''; /* / The hostname of the database server */ + var $user = ''; /* / The username which is used to connect to the database server. */ + var $password = ''; /* / Password for the username. For security, we no longer store it. */ + var $debug = false; /* / if set to true will output sql statements */ + var $maxblobsize = 256000; /* / maximum size of blobs or large text fields -- some databases die otherwise like foxpro */ + var $concat_operator = '+'; /* / default concat operator -- change to || for Oracle/Interbase */ + var $fmtDate = "'Y-m-d'"; /* / used by DBDate() as the default date format used by the database */ + var $fmtTimeStamp = "'Y-m-d, h:i:s A'"; /* / used by DBTimeStamp as the default timestamp fmt. */ + var $true = '1'; /* / string that represents TRUE for a database */ + var $false = '0'; /* / string that represents FALSE for a database */ + var $replaceQuote = "\\'"; /* / string to use to replace quotes */ + var $charSet=false; /* / character set to use - only for interbase */ + var $metaDatabasesSQL = ''; + var $metaTablesSQL = ''; + var $uniqueOrderBy = false; /* / All order by columns have to be unique */ + var $emptyDate = ' '; + /* -- */ + var $hasInsertID = false; /* / supports autoincrement ID? */ + var $hasAffectedRows = false; /* / supports affected rows for update/delete? */ + var $hasTop = false; /* / support mssql/access SELECT TOP 10 * FROM TABLE */ + var $hasLimit = false; /* / support pgsql/mysql SELECT * FROM TABLE LIMIT 10 */ + var $readOnly = false; /* / this is a readonly database - used by phpLens */ + var $hasMoveFirst = false; /* / has ability to run MoveFirst(), scrolling backwards */ + var $hasGenID = false; /* / can generate sequences using GenID(); */ + var $hasTransactions = true; /* / has transactions */ + /* -- */ + var $genID = 0; /* / sequence id used by GenID(); */ + var $raiseErrorFn = false; /* / error function to call */ + var $upperCase = false; /* / uppercase function to call for searching/where */ + var $isoDates = false; /* / accepts dates in ISO format */ + var $cacheSecs = 3600; /* / cache for 1 hour */ + var $sysDate = false; /* / name of function that returns the current date */ + var $sysTimeStamp = false; /* / name of function that returns the current timestamp */ + var $arrayClass = 'ADORecordSet_array'; /* / name of class used to generate array recordsets, which are pre-downloaded recordsets */ + + var $noNullStrings = false; /* / oracle specific stuff - if true ensures that '' is converted to ' ' */ + var $numCacheHits = 0; + var $numCacheMisses = 0; + var $pageExecuteCountRows = true; + var $uniqueSort = false; /* / indicates that all fields in order by must be unique */ + var $leftOuter = false; /* / operator to use for left outer join in WHERE clause */ + var $rightOuter = false; /* / operator to use for right outer join in WHERE clause */ + var $ansiOuter = false; /* / whether ansi outer join syntax supported */ + var $autoRollback = false; /* autoRollback on PConnect(). */ + var $poorAffectedRows = false; /* affectedRows not working or unreliable */ + + var $fnExecute = false; + var $fnCacheExecute = false; + var $blobEncodeType = false; /* false=not required, 'I'=encode to integer, 'C'=encode to char */ + var $dbxDriver = false; + + /* */ + /* PRIVATE VARS */ + /* */ + var $_oldRaiseFn = false; + var $_transOK = null; + var $_connectionID = false; /* / The returned link identifier whenever a successful database connection is made. */ + var $_errorMsg = ''; /* / A variable which was used to keep the returned last error message. The value will */ + /* / then returned by the errorMsg() function */ + + var $_queryID = false; /* / This variable keeps the last created result link identifier */ + + var $_isPersistentConnection = false; /* / A boolean variable to state whether its a persistent connection or normal connection. */ + var $_bindInputArray = false; /* / set to true if ADOConnection.Execute() permits binding of array parameters. */ + var $autoCommit = true; /* / do not modify this yourself - actually private */ + var $transOff = 0; /* / temporarily disable transactions */ + var $transCnt = 0; /* / count of nested transactions */ + + var $fetchMode=false; + + /** + * Constructor + */ + function ADOConnection() + { + die('Virtual Class -- cannot instantiate'); + } + + /** + Get server version info... + + @returns An array with 2 elements: $arr['string'] is the description string, + and $arr[version] is the version (also a string). + */ + function ServerInfo() + { + return array('description' => '', 'version' => ''); + } + + function _findvers($str) + { + if (preg_match('/([0-9]+\.([0-9\.])+)/',$str, $arr)) return $arr[1]; + else return ''; + } + + /** + * All error messages go through this bottleneck function. + * You can define your own handler by defining the function name in ADODB_OUTP. + */ + function outp($msg,$newline=true) + { + global $HTTP_SERVER_VARS; + + if (defined('ADODB_OUTP')) { + $fn = ADODB_OUTP; + $fn($msg,$newline); + return; + } + + if ($newline) $msg .= "
\n"; + + if (isset($HTTP_SERVER_VARS['HTTP_USER_AGENT'])) echo $msg; + else echo strip_tags($msg); + flush(); + } + + /** + * Connect to database + * + * @param [argHostname] Host to connect to + * @param [argUsername] Userid to login + * @param [argPassword] Associated password + * @param [argDatabaseName] database + * @param [forceNew] force new connection + * + * @return true or false + */ + function Connect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "", $forceNew = false) + { + if ($argHostname != "") $this->host = $argHostname; + if ($argUsername != "") $this->user = $argUsername; + if ($argPassword != "") $this->password = $argPassword; /* not stored for security reasons */ + if ($argDatabaseName != "") $this->database = $argDatabaseName; + + $this->_isPersistentConnection = false; + if ($fn = $this->raiseErrorFn) { + if ($forceNew) { + if ($this->_nconnect($this->host, $this->user, $this->password, $this->database)) return true; + } else { + if ($this->_connect($this->host, $this->user, $this->password, $this->database)) return true; + } + $err = $this->ErrorMsg(); + if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'"; + $fn($this->databaseType,'CONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this); + } else { + if ($forceNew) { + if ($this->_nconnect($this->host, $this->user, $this->password, $this->database)) return true; + } else { + if ($this->_connect($this->host, $this->user, $this->password, $this->database)) return true; + } + } + if ($this->debug) ADOConnection::outp( $this->host.': '.$this->ErrorMsg()); + return false; + } + + function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName) + { + return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName); + } + + + /** + * Always force a new connection to database - currently only works with oracle + * + * @param [argHostname] Host to connect to + * @param [argUsername] Userid to login + * @param [argPassword] Associated password + * @param [argDatabaseName] database + * + * @return true or false + */ + function NConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "") + { + return $this->Connect($argHostname, $argUsername, $argPassword, $argDatabaseName, true); + } + + /** + * Establish persistent connect to database + * + * @param [argHostname] Host to connect to + * @param [argUsername] Userid to login + * @param [argPassword] Associated password + * @param [argDatabaseName] database + * + * @return return true or false + */ + function PConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "") + { + if (defined('ADODB_NEVER_PERSIST')) + return $this->Connect($argHostname,$argUsername,$argPassword,$argDatabaseName); + + if ($argHostname != "") $this->host = $argHostname; + if ($argUsername != "") $this->user = $argUsername; + if ($argPassword != "") $this->password = $argPassword; + if ($argDatabaseName != "") $this->database = $argDatabaseName; + + $this->_isPersistentConnection = true; + + if ($fn = $this->raiseErrorFn) { + if ($this->_pconnect($this->host, $this->user, $this->password, $this->database)) return true; + $err = $this->ErrorMsg(); + if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'"; + $fn($this->databaseType,'PCONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this); + } else + if ($this->_pconnect($this->host, $this->user, $this->password, $this->database)) return true; + + if ($this->debug) ADOConnection::outp( $this->host.': '.$this->ErrorMsg()); + return false; + } + + /* Format date column in sql string given an input format that understands Y M D */ + function SQLDate($fmt, $col=false) + { + if (!$col) $col = $this->sysDate; + return $col; /* child class implement */ + } + + /** + * Should prepare the sql statement and return the stmt resource. + * For databases that do not support this, we return the $sql. To ensure + * compatibility with databases that do not support prepare: + * + * $stmt = $db->Prepare("insert into table (id, name) values (?,?)"); + * $db->Execute($stmt,array(1,'Jill')) or die('insert failed'); + * $db->Execute($stmt,array(2,'Joe')) or die('insert failed'); + * + * @param sql SQL to send to database + * + * @return return FALSE, or the prepared statement, or the original sql if + * if the database does not support prepare. + * + */ + function Prepare($sql) + { + return $sql; + } + + /** + * Some databases, eg. mssql require a different function for preparing + * stored procedures. So we cannot use Prepare(). + * + * Should prepare the stored procedure and return the stmt resource. + * For databases that do not support this, we return the $sql. To ensure + * compatibility with databases that do not support prepare: + * + * @param sql SQL to send to database + * + * @return return FALSE, or the prepared statement, or the original sql if + * if the database does not support prepare. + * + */ + function PrepareSP($sql) + { + return $this->Prepare($sql); + } + + /** + * PEAR DB Compat + */ + function Quote($s) + { + return $this->qstr($s,false); + } + + function q(&$s) + { + $s = $this->qstr($s,false); + } + + /** + * PEAR DB Compat - do not use internally. + */ + function ErrorNative() + { + return $this->ErrorNo(); + } + + + /** + * PEAR DB Compat - do not use internally. + */ + function nextId($seq_name) + { + return $this->GenID($seq_name); + } + + /** + * Lock a row, will escalate and lock the table if row locking not supported + * will normally free the lock at the end of the transaction + * + * @param $table name of table to lock + * @param $where where clause to use, eg: "WHERE row=12". If left empty, will escalate to table lock + */ + function RowLock($table,$where) + { + return false; + } + + function CommitLock($table) + { + return $this->CommitTrans(); + } + + function RollbackLock($table) + { + return $this->RollbackTrans(); + } + + /** + * PEAR DB Compat - do not use internally. + * + * The fetch modes for NUMERIC and ASSOC for PEAR DB and ADODB are identical + * for easy porting :-) + * + * @param mode The fetchmode ADODB_FETCH_ASSOC or ADODB_FETCH_NUM + * @returns The previous fetch mode + */ + function SetFetchMode($mode) + { + $old = $this->fetchMode; + $this->fetchMode = $mode; + + if ($old === false) { + global $ADODB_FETCH_MODE; + return $ADODB_FETCH_MODE; + } + return $old; + } + + + /** + * PEAR DB Compat - do not use internally. + */ + function &Query($sql, $inputarr=false) + { + $rs = &$this->Execute($sql, $inputarr); + if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error(); + return $rs; + } + + + /** + * PEAR DB Compat - do not use internally + */ + function &LimitQuery($sql, $offset, $count) + { + $rs = &$this->SelectLimit($sql, $count, $offset); /* swap */ + if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error(); + return $rs; + } + + + /** + * PEAR DB Compat - do not use internally + */ + function Disconnect() + { + return $this->Close(); + } + + /* + Usage in oracle + $stmt = $db->Prepare('select * from table where id =:myid and group=:group'); + $db->Parameter($stmt,$id,'myid'); + $db->Parameter($stmt,$group,'group',64); + $db->Execute(); + + @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. + + */ + function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false) + { + return false; + } + + /** + Improved method of initiating a transaction. Used together with CompleteTrans(). + Advantages include: + + a. StartTrans/CompleteTrans is nestable, unlike BeginTrans/CommitTrans/RollbackTrans. + Only the outermost block is treated as a transaction.
+ b. CompleteTrans auto-detects SQL errors, and will rollback on errors, commit otherwise.
+ c. All BeginTrans/CommitTrans/RollbackTrans inside a StartTrans/CompleteTrans block + are disabled, making it backward compatible. + */ + function StartTrans($errfn = 'ADODB_TransMonitor') + { + + if ($this->transOff > 0) { + $this->transOff += 1; + return; + } + + $this->_oldRaiseFn = $this->raiseErrorFn; + $this->raiseErrorFn = $errfn; + $this->_transOK = true; + + if ($this->debug && $this->transCnt > 0) ADOConnection::outp("Bad Transaction: StartTrans called within BeginTrans"); + $this->BeginTrans(); + $this->transOff = 1; + } + + /** + Used together with StartTrans() to end a transaction. Monitors connection + for sql errors, and will commit or rollback as appropriate. + + @autoComplete if true, monitor sql errors and commit and rollback as appropriate, + and if set to false force rollback even if no SQL error detected. + @returns true on commit, false on rollback. + */ + function CompleteTrans($autoComplete = true) + { + if ($this->transOff > 1) { + $this->transOff -= 1; + return true; + } + $this->raiseErrorFn = $this->_oldRaiseFn; + + $this->transOff = 0; + if ($this->_transOK && $autoComplete) $this->CommitTrans(); + else $this->RollbackTrans(); + + return $this->_transOK; + } + + /* + At the end of a StartTrans/CompleteTrans block, perform a rollback. + */ + function FailTrans() + { + if ($this->debug && $this->transOff == 0) { + ADOConnection::outp("FailTrans outside StartTrans/CompleteTrans"); + } + $this->_transOK = false; + } + /** + * 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. + * @param [arg3] reserved for john lim for future use + * @return RecordSet or false + */ + function &Execute($sql,$inputarr=false,$arg3=false) + { + if ($this->fnExecute) { + $fn = $this->fnExecute; + $fn($this,$sql,$inputarr); + } + if (!$this->_bindInputArray && $inputarr) { + $sqlarr = explode('?',$sql); + $sql = ''; + $i = 0; + foreach($inputarr as $v) { + + $sql .= $sqlarr[$i]; + /* from Ron Baldwin*/ + /* Only quote string types */ + if (gettype($v) == 'string') + $sql .= $this->qstr($v); + else if ($v === null) + $sql .= 'NULL'; + else + $sql .= $v; + $i += 1; + + } + $sql .= $sqlarr[$i]; + if ($i+1 != sizeof($sqlarr)) + ADOConnection::outp( "Input Array does not match ?: ".htmlspecialchars($sql)); + $inputarr = false; + } + /* debug version of query */ + if ($this->debug) { + global $HTTP_SERVER_VARS; + + $ss = ''; + if ($inputarr) { + foreach ($inputarr as $kk => $vv) { + if (is_string($vv) && strlen($vv)>64) $vv = substr($vv,0,64).'...'; + $ss .= "($kk=>'$vv') "; + } + $ss = "[ $ss ]"; + } + $sqlTxt = str_replace(',',', ',is_array($sql) ?$sql[0] : $sql); + + /* check if running from browser or command-line */ + $inBrowser = isset($HTTP_SERVER_VARS['HTTP_USER_AGENT']); + + if ($inBrowser) + ADOConnection::outp( "
\n($this->databaseType): ".htmlspecialchars($sqlTxt)."$ss
\n
\n",false); + else + ADOConnection::outp( "=----\n($this->databaseType): ".($sqlTxt)." \n-----\n",false); + flush(); + + $this->_queryID = $this->_query($sql,$inputarr,$arg3); + + /* + Alexios Fakios notes that ErrorMsg() must be called before ErrorNo() for mssql + because ErrorNo() calls Execute('SELECT @ERROR'), causing recure + */ + if ($this->databaseType == 'mssql') { + /* ErrorNo is a slow function call in mssql, and not reliable */ + /* in PHP 4.0.6 */ + if($emsg = $this->ErrorMsg()) { + $err = $this->ErrorNo(); + if ($err) { + ADOConnection::outp($err.': '.$emsg); + flush(); + } + } + } else + if (!$this->_queryID) { + $e = $this->ErrorNo(); + $m = $this->ErrorMsg(); + ADOConnection::outp($e .': '. $m ); + flush(); + } + } else { + /* non-debug version of query */ + + $this->_queryID =@$this->_query($sql,$inputarr,$arg3); + + } + /* error handling if query fails */ + if ($this->_queryID === false) { + $fn = $this->raiseErrorFn; + if ($fn) { + $fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,$inputarr,$this); + } + return false; + } else if ($this->_queryID === true) { + /* return simplified empty recordset for inserts/updates/deletes with lower overhead */ + $rs = new ADORecordSet_empty(); + return $rs; + } + + /* return real recordset from select statement */ + $rsclass = "ADORecordSet_".$this->databaseType; + $rs = new $rsclass($this->_queryID,$this->fetchMode); /* &new not supported by older PHP versions */ + $rs->connection = &$this; /* Pablo suggestion */ + $rs->Init(); + if (is_array($sql)) $rs->sql = $sql[0]; + else $rs->sql = $sql; + + if ($rs->_numOfRows <= 0) { + global $ADODB_COUNTRECS; + + if ($ADODB_COUNTRECS) { + if (!$rs->EOF){ + $rs = &$this->_rs2rs($rs,-1,-1,!is_array($sql)); + $rs->_queryID = $this->_queryID; + } else + $rs->_numOfRows = 0; + } + } + return $rs; + } + + function CreateSequence($seqname='adodbseq',$startID=1) + { + if (empty($this->_genSeqSQL)) return false; + return $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID)); + } + + function DropSequence($seqname) + { + if (empty($this->_dropSeqSQL)) return false; + return $this->Execute(sprintf($this->_dropSeqSQL,$seqname)); + } + + /** + * Generates a sequence id and stores it in $this->genID; + * GenID is only available if $this->hasGenID = true; + * + * @param seqname name of sequence to use + * @param startID if sequence does not exist, start at this ID + * @return 0 if not supported, otherwise a sequence id + */ + + function GenID($seqname='adodbseq',$startID=1) + { + if (!$this->hasGenID) { + return 0; /* formerly returns false pre 1.60 */ + } + + $getnext = sprintf($this->_genIDSQL,$seqname); + $rs = @$this->Execute($getnext); + if (!$rs) { + $createseq = $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID)); + $rs = $this->Execute($getnext); + } + if ($rs && !$rs->EOF) $this->genID = reset($rs->fields); + else $this->genID = 0; /* false */ + + if ($rs) $rs->Close(); + + return $this->genID; + } + + /** + * @return the last inserted ID. Not all databases support this. + */ + function Insert_ID() + { + if ($this->hasInsertID) return $this->_insertid(); + if ($this->debug) ADOConnection::outp( 'Insert_ID error
'); + return false; + } + + + /** + * Portable Insert ID. Pablo Roca+ * + * @return the last inserted ID. All databases support this. But aware possible + * problems in multiuser environments. Heavy test this before deploying. + */ + function PO_Insert_ID($table="", $id="") + { + if ($this->hasInsertID){ + return $this->Insert_ID(); + } else { + return $this->GetOne("SELECT MAX($id) FROM $table"); + } + } + + + /** + * @return # rows affected by UPDATE/DELETE + */ + function Affected_Rows() + { + if ($this->hasAffectedRows) { + $val = $this->_affectedrows(); + return ($val < 0) ? false : $val; + } + + if ($this->debug) ADOConnection::outp( ' Affected_Rows error
',false); + return false; + } + + + /** + * @return the last error message + */ + function ErrorMsg() + { + return '!! '.strtoupper($this->dataProvider.' '.$this->databaseType).': '.$this->_errorMsg; + } + + + /** + * @return the last error number. Normally 0 means no error. + */ + function ErrorNo() + { + return ($this->_errorMsg) ? -1 : 0; + } + + function MetaError($err=false) + { + include_once(ADODB_DIR."/adodb-error.inc.php"); + if ($err === false) $err = $this->ErrorNo(); + return adodb_error($this->dataProvider,$this->databaseType,$err); + } + + function MetaErrorMsg($errno) + { + include_once(ADODB_DIR."/adodb-error.inc.php"); + return adodb_errormsg($errno); + } + + /** + * @returns an array with the primary key columns in it. + */ + function MetaPrimaryKeys($table, $owner=false) + { + /* owner not used in base class - see oci8 */ + $p = array(); + $objs =& $this->MetaColumns($table); + if ($objs) { + foreach($objs as $v) { + if (!empty($v->primary_key)) + $p[] = $v->name; + } + } + if (sizeof($p)) return $p; + return false; + } + + + /** + * Choose a database to connect to. Many databases do not support this. + * + * @param dbName is the name of the database to select + * @return true or false + */ + function SelectDB($dbName) + {return false;} + + + /** + * Will select, getting rows from $offset (1-based), for $nrows. + * This simulates the MySQL "select * from table limit $offset,$nrows" , and + * the PostgreSQL "select * from table limit $nrows offset $offset". Note that + * MySQL and PostgreSQL parameter ordering is the opposite of the other. + * eg. + * SelectLimit('select * from table',3); will return rows 1 to 3 (1-based) + * SelectLimit('select * from table',3,2); will return rows 3 to 5 (1-based) + * + * Uses SELECT TOP for Microsoft databases (when $this->hasTop is set) + * BUG: Currently SelectLimit fails with $sql with LIMIT or TOP clause already set + * + * @param sql + * @param [offset] is the row to start calculations from (1-based) + * @param [nrows] is the number of rows to get + * @param [inputarr] array of bind variables + * @param [arg3] is a private parameter only used by jlim + * @param [secs2cache] is a private parameter only used by jlim + * @return the recordset ($rs->databaseType == 'array') + */ + function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$arg3=false,$secs2cache=0) + { + if ($this->hasTop && $nrows > 0) { + /* suggested by Reinhard Balling. Access requires top after distinct */ + /* Informix requires first before distinct - F Riosa */ + $ismssql = (strpos($this->databaseType,'mssql') !== false); + if ($ismssql) $isaccess = false; + else $isaccess = (strpos($this->databaseType,'access') !== false); + + if ($offset <= 0) { + + /* access includes ties in result */ + if ($isaccess) { + $sql = preg_replace( + '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.$nrows.' ',$sql); + + if ($secs2cache>0) return $this->CacheExecute($secs2cache, $sql,$inputarr,$arg3); + else return $this->Execute($sql,$inputarr,$arg3); + } else if ($ismssql){ + $sql = preg_replace( + '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.$nrows.' ',$sql); + } else { + $sql = preg_replace( + '/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.$nrows.' ',$sql); + } + } else { + $nn = $nrows + $offset; + if ($isaccess || $ismssql) { + $sql = preg_replace( + '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql); + } else { + $sql = preg_replace( + '/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql); + } + } + } + + /* if $offset>0, we want to skip rows, and $ADODB_COUNTRECS is set, we buffer rows */ + /* 0 to offset-1 which will be discarded anyway. So we disable $ADODB_COUNTRECS. */ + global $ADODB_COUNTRECS; + + $savec = $ADODB_COUNTRECS; + $ADODB_COUNTRECS = false; + + if ($offset>0){ + if ($secs2cache>0) $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr,$arg3); + else $rs = &$this->Execute($sql,$inputarr,$arg3); + } else { + if ($secs2cache>0) $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr,$arg3); + else $rs = &$this->Execute($sql,$inputarr,$arg3); + } + $ADODB_COUNTRECS = $savec; + if ($rs && !$rs->EOF) { + return $this->_rs2rs($rs,$nrows,$offset); + } + /* print_r($rs); */ + return $rs; + } + + + /** + * Convert database recordset to an array recordset + * input recordset's cursor should be at beginning, and + * old $rs will be closed. + * + * @param rs the recordset to copy + * @param [nrows] number of rows to retrieve (optional) + * @param [offset] offset by number of rows (optional) + * @return the new recordset + */ + function &_rs2rs(&$rs,$nrows=-1,$offset=-1,$close=true) + { + if (! $rs) return false; + + $dbtype = $rs->databaseType; + if (!$dbtype) { + $rs = &$rs; /* required to prevent crashing in 4.2.1, but does not happen in 4.3.1 -- why ? */ + return $rs; + } + if (($dbtype == 'array' || $dbtype == 'csv') && $nrows == -1 && $offset == -1) { + $rs->MoveFirst(); + $rs = &$rs; /* required to prevent crashing in 4.2.1, but does not happen in 4.3.1-- why ? */ + return $rs; + } + $flds = array(); + for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++) { + $flds[] = $rs->FetchField($i); + } + $arr =& $rs->GetArrayLimit($nrows,$offset); + /* print_r($arr); */ + if ($close) $rs->Close(); + + $arrayClass = $this->arrayClass; + + $rs2 = new $arrayClass(); + $rs2->connection = &$this; + $rs2->sql = $rs->sql; + $rs2->dataProvider = $this->dataProvider; + $rs2->InitArrayFields($arr,$flds); + return $rs2; + } + + + function &GetArray($sql, $inputarr=false) + { + return $this->GetAll($sql,$inputarr); + } + + /** + * Return first element of first row of sql statement. Recordset is disposed + * for you. + * + * @param sql SQL statement + * @param [inputarr] input bind array + */ + function GetOne($sql,$inputarr=false) + { + global $ADODB_COUNTRECS; + $crecs = $ADODB_COUNTRECS; + $ADODB_COUNTRECS = false; + + $ret = false; + $rs = &$this->Execute($sql,$inputarr); + if ($rs) { + if (!$rs->EOF) $ret = reset($rs->fields); + $rs->Close(); + } + $ADODB_COUNTRECS = $crecs; + return $ret; + } + + function CacheGetOne($secs2cache,$sql=false,$inputarr=false) + { + $ret = false; + $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr); + if ($rs) { + if (!$rs->EOF) $ret = reset($rs->fields); + $rs->Close(); + } + + return $ret; + } + + function GetCol($sql, $inputarr = false, $trim = false) + { + $rv = false; + $rs = &$this->Execute($sql, $inputarr); + if ($rs) { + if ($trim) { + while (!$rs->EOF) { + $rv[] = trim(reset($rs->fields)); + $rs->MoveNext(); + } + } else { + while (!$rs->EOF) { + $rv[] = reset($rs->fields); + $rs->MoveNext(); + } + } + $rs->Close(); + } + return $rv; + } + + function CacheGetCol($secs, $sql = false, $inputarr = false,$trim=false) + { + $rv = false; + $rs = &$this->CacheExecute($secs, $sql, $inputarr); + if ($rs) { + if ($trim) { + while (!$rs->EOF) { + $rv[] = trim(reset($rs->fields)); + $rs->MoveNext(); + } + } else { + while (!$rs->EOF) { + $rv[] = reset($rs->fields); + $rs->MoveNext(); + } + } + $rs->Close(); + } + return $rv; + } + + /* + Calculate the offset of a date for a particular database and generate + appropriate SQL. Useful for calculating future/past dates and storing + in a database. + + If dayFraction=1.5 means 1.5 days from now, 1.0/24 for 1 hour. + */ + function OffsetDate($dayFraction,$date=false) + { + if (!$date) $date = $this->sysDate; + return '('.$date.'+'.$dayFraction.')'; + } + + + /** + * Return all rows. Compat with PEAR DB + * + * @param sql SQL statement + * @param [inputarr] input bind array + */ + function &GetAll($sql,$inputarr=false) + { + global $ADODB_COUNTRECS; + + $savec = $ADODB_COUNTRECS; + $ADODB_COUNTRECS = false; + $rs = $this->Execute($sql,$inputarr); + $ADODB_COUNTRECS = $savec; + + if (!$rs) + if (defined('ADODB_PEAR')) return ADODB_PEAR_Error(); + else return false; + $arr =& $rs->GetArray(); + $rs->Close(); + return $arr; + } + + function &CacheGetAll($secs2cache,$sql=false,$inputarr=false) + { + global $ADODB_COUNTRECS; + + $savec = $ADODB_COUNTRECS; + $ADODB_COUNTRECS = false; + $rs = $this->CacheExecute($secs2cache,$sql,$inputarr); + $ADODB_COUNTRECS = $savec; + + if (!$rs) + if (defined('ADODB_PEAR')) return ADODB_PEAR_Error(); + else return false; + + $arr =& $rs->GetArray(); + $rs->Close(); + return $arr; + } + + + + /** + * Return one row of sql statement. Recordset is disposed for you. + * + * @param sql SQL statement + * @param [inputarr] input bind array + */ + function &GetRow($sql,$inputarr=false) + { + global $ADODB_COUNTRECS; + $crecs = $ADODB_COUNTRECS; + $ADODB_COUNTRECS = false; + + $rs = $this->Execute($sql,$inputarr); + + $ADODB_COUNTRECS = $crecs; + if ($rs) { + $arr = array(); + if (!$rs->EOF) $arr = $rs->fields; + $rs->Close(); + return $arr; + } + + return false; + } + + function &CacheGetRow($secs2cache,$sql=false,$inputarr=false) + { + $rs = $this->CacheExecute($secs2cache,$sql,$inputarr); + if ($rs) { + $arr = false; + if (!$rs->EOF) $arr = $rs->fields; + $rs->Close(); + return $arr; + } + return false; + } + + /** + * Insert or replace a single record. Note: this is not the same as MySQL's replace. + * ADOdb's Replace() uses update-insert semantics, not insert-delete-duplicates of MySQL. + * Also note that no table locking is done currently, so it is possible that the + * record be inserted twice by two programs... + * + * $this->Replace('products', array('prodname' =>"'Nails'","price" => 3.99), 'prodname'); + * + * $table table name + * $fieldArray associative array of data (you must quote strings yourself). + * $keyCol the primary key field name or if compound key, array of field names + * autoQuote set to true to use a hueristic to quote strings. Works with nulls and numbers + * but does not work with dates nor SQL functions. + * has_autoinc the primary key is an auto-inc field, so skip in insert. + * + * Currently blob replace not supported + * + * returns 0 = fail, 1 = update, 2 = insert + */ + + function Replace($table, $fieldArray, $keyCol, $autoQuote=false, $has_autoinc=false) + { + if (count($fieldArray) == 0) return 0; + $first = true; + $uSet = ''; + + if (!is_array($keyCol)) { + $keyCol = array($keyCol); + } + foreach($fieldArray as $k => $v) { + if ($autoQuote && !is_numeric($v) and substr($v,0,1) != "'" and strcasecmp($v,'null')!=0) { + $v = $this->qstr($v); + $fieldArray[$k] = $v; + } + if (in_array($k,$keyCol)) continue; /* skip UPDATE if is key */ + + if ($first) { + $first = false; + $uSet = "$k=$v"; + } else + $uSet .= ",$k=$v"; + } + + $first = true; + foreach ($keyCol as $v) { + if ($first) { + $first = false; + $where = "$v=$fieldArray[$v]"; + } else { + $where .= " and $v=$fieldArray[$v]"; + } + } + + if ($uSet) { + $update = "UPDATE $table SET $uSet WHERE $where"; + + $rs = $this->Execute($update); + if ($rs) { + if ($this->poorAffectedRows) { + /* + The Select count(*) wipes out any errors that the update would have returned. + http://phplens.com/lens/lensforum/msgs.php?id=5696 + */ + if ($this->ErrorNo()<>0) return 0; + + # affected_rows == 0 if update field values identical to old values + # for mysql - which is silly. + + $cnt = $this->GetOne("select count(*) from $table where $where"); + if ($cnt > 0) return 1; /* record already exists */ + } else + if (($this->Affected_Rows()>0)) return 1; + } + + } + /* print "Error=".$this->ErrorNo().'
'; */ + $first = true; + foreach($fieldArray as $k => $v) { + if ($has_autoinc && in_array($k,$keyCol)) continue; /* skip autoinc col */ + + if ($first) { + $first = false; + $iCols = "$k"; + $iVals = "$v"; + } else { + $iCols .= ",$k"; + $iVals .= ",$v"; + } + } + $insert = "INSERT INTO $table ($iCols) VALUES ($iVals)"; + $rs = $this->Execute($insert); + return ($rs) ? 2 : 0; + } + + + /** + * Will select, getting rows from $offset (1-based), for $nrows. + * This simulates the MySQL "select * from table limit $offset,$nrows" , and + * the PostgreSQL "select * from table limit $nrows offset $offset". Note that + * MySQL and PostgreSQL parameter ordering is the opposite of the other. + * eg. + * CacheSelectLimit(15,'select * from table',3); will return rows 1 to 3 (1-based) + * CacheSelectLimit(15,'select * from table',3,2); will return rows 3 to 5 (1-based) + * + * BUG: Currently CacheSelectLimit fails with $sql with LIMIT or TOP clause already set + * + * @param [secs2cache] seconds to cache data, set to 0 to force query. This is optional + * @param sql + * @param [offset] is the row to start calculations from (1-based) + * @param [nrows] is the number of rows to get + * @param [inputarr] array of bind variables + * @param [arg3] is a private parameter only used by jlim + * @return the recordset ($rs->databaseType == 'array') + */ + function &CacheSelectLimit($secs2cache,$sql,$nrows=-1,$offset=-1,$inputarr=false, $arg3=false) + { + if (!is_numeric($secs2cache)) { + if ($sql === false) $sql = -1; + if ($offset == -1) $offset = false; + /* sql, nrows, offset,inputarr,arg3 */ + return $this->SelectLimit($secs2cache,$sql,$nrows,$offset,$inputarr,$this->cacheSecs); + } else { + if ($sql === false) ADOConnection::outp( "Warning: \$sql missing from CacheSelectLimit()"); + return $this->SelectLimit($sql,$nrows,$offset,$inputarr,$arg3,$secs2cache); + } + } + + /** + * Flush cached recordsets that match a particular $sql statement. + * If $sql == false, then we purge all files in the cache. + */ + function CacheFlush($sql=false,$inputarr=false) + { + global $ADODB_CACHE_DIR; + + if (strlen($ADODB_CACHE_DIR) > 1 && !$sql) { + if (strpos(strtoupper(PHP_OS),'WIN') !== false) { + $cmd = 'del /s '.str_replace('/','\\',$ADODB_CACHE_DIR).'\adodb_*.cache'; + } else { + $cmd = 'rm -rf '.$ADODB_CACHE_DIR.'/??/adodb_*.cache'; + /* old version 'rm -f `find '.$ADODB_CACHE_DIR.' -name adodb_*.cache`'; */ + } + if ($this->debug) { + ADOConnection::outp( "CacheFlush: $cmd
\n", system($cmd),""); + } else { + exec($cmd); + } + return; + } + $f = $this->_gencachename($sql.serialize($inputarr),false); + adodb_write_file($f,''); /* is adodb_write_file needed? */ + @unlink($f); + } + + /** + * Private function to generate filename for caching. + * Filename is generated based on: + * + * - sql statement + * - database type (oci8, ibase, ifx, etc) + * - database name + * - userid + * + * We create 256 sub-directories in the cache directory ($ADODB_CACHE_DIR). + * 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) + { + global $ADODB_CACHE_DIR; + + $m = md5($sql.$this->databaseType.$this->database.$this->user); + $dir = $ADODB_CACHE_DIR.'/'.substr($m,0,2); + if ($createdir && !file_exists($dir)) { + $oldu = umask(0); + if (!mkdir($dir,0771)) + if ($this->debug) ADOConnection::outp( "Unable to mkdir $dir for $sql"); + umask($oldu); + } + return $dir.'/adodb_'.$m.'.cache'; + } + + + /** + * Execute SQL, caching recordsets. + * + * @param [secs2cache] seconds to cache data, set to 0 to force query. + * This is an optional parameter. + * @param sql SQL statement to execute + * @param [inputarr] holds the input data to bind to + * @param [arg3] reserved for john lim for future use + * @return RecordSet or false + */ + function &CacheExecute($secs2cache,$sql=false,$inputarr=false,$arg3=false) + { + if (!is_numeric($secs2cache)) { + $arg3 = $inputarr; + $inputarr = $sql; + $sql = $secs2cache; + $secs2cache = $this->cacheSecs; + } + include_once(ADODB_DIR.'/adodb-csvlib.inc.php'); + + $md5file = $this->_gencachename($sql.serialize($inputarr),true); + $err = ''; + + if ($secs2cache > 0){ + $rs = &csv2rs($md5file,$err,$secs2cache); + $this->numCacheHits += 1; + } else { + $err='Timeout 1'; + $rs = false; + $this->numCacheMisses += 1; + } + if (!$rs) { + /* no cached rs found */ + if ($this->debug) { + if (get_magic_quotes_runtime()) { + ADOConnection::outp("Please disable magic_quotes_runtime - it corrupts cache files :("); + } + ADOConnection::outp( " $md5file cache failure: $err (see sql below)"); + } + $rs = &$this->Execute($sql,$inputarr,$arg3); + if ($rs) { + $eof = $rs->EOF; + $rs = &$this->_rs2rs($rs); /* read entire recordset into memory immediately */ + $txt = _rs2serialize($rs,false,$sql); /* serialize */ + + if (!adodb_write_file($md5file,$txt,$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"); + } + if ($rs->EOF && !$eof) { + $rs->MoveFirst(); + /* $rs = &csv2rs($md5file,$err); */ + $rs->connection = &$this; /* Pablo suggestion */ + } + + } else + @unlink($md5file); + } else { + if ($this->fnCacheExecute) { + $fn = $this->fnCacheExecute; + $fn($this, $secs2cache, $sql, $inputarr); + } + /* ok, set cached object found */ + $rs->connection = &$this; /* Pablo suggestion */ + if ($this->debug){ + global $HTTP_SERVER_VARS; + + $inBrowser = isset($HTTP_SERVER_VARS['HTTP_USER_AGENT']); + $ttl = $rs->timeCreated + $secs2cache - time(); + $s = is_array($sql) ? $sql[0] : $sql; + if ($inBrowser) $s = ''.htmlspecialchars($s).''; + + ADOConnection::outp( " $md5file reloaded, ttl=$ttl [ $s ]"); + } + } + return $rs; + } + + + /** + * Generates an Update Query based on an existing recordset. + * $arrFields is an associative array of fields with the value + * that should be assigned. + * + * Note: This function should only be used on a recordset + * that is run against a single table and sql should only + * be a simple select stmt with no groupby/orderby/limit + * + * "Jonathan Younger"+ */ + function GetUpdateSQL(&$rs, $arrFields,$forceUpdate=false,$magicq=false) + { + include_once(ADODB_DIR.'/adodb-lib.inc.php'); + return _adodb_getupdatesql($this,$rs,$arrFields,$forceUpdate,$magicq); + } + + + /** + * Generates an Insert Query based on an existing recordset. + * $arrFields is an associative array of fields with the value + * that should be assigned. + * + * Note: This function should only be used on a recordset + * that is run against a single table. + */ + function GetInsertSQL(&$rs, $arrFields,$magicq=false) + { + include_once(ADODB_DIR.'/adodb-lib.inc.php'); + return _adodb_getinsertsql($this,$rs,$arrFields,$magicq); + } + + + /** + * Update a blob column, given a where clause. There are more sophisticated + * blob handling functions that we could have implemented, but all require + * a very complex API. Instead we have chosen something that is extremely + * simple to understand and use. + * + * Note: $blobtype supports 'BLOB' and 'CLOB', default is BLOB of course. + * + * Usage to update a $blobvalue which has a primary key blob_id=1 into a + * field blobtable.blobcolumn: + * + * UpdateBlob('blobtable', 'blobcolumn', $blobvalue, 'blob_id=1'); + * + * Insert example: + * + * $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); + * $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1'); + */ + + function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') + { + return $this->Execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false; + } + + /** + * Usage: + * UpdateBlob('TABLE', 'COLUMN', '/path/to/file', 'ID=1'); + * + * $blobtype supports 'BLOB' and 'CLOB' + * + * $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); + * $conn->UpdateBlob('blobtable','blobcol',$blobpath,'id=1'); + */ + function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB') + { + $fd = fopen($path,'rb'); + if ($fd === false) return false; + $val = fread($fd,filesize($path)); + fclose($fd); + return $this->UpdateBlob($table,$column,$val,$where,$blobtype); + } + + function BlobDecode($blob) + { + return $blob; + } + + function BlobEncode($blob) + { + return $blob; + } + + /** + * Usage: + * UpdateClob('TABLE', 'COLUMN', $var, 'ID=1', 'CLOB'); + * + * $conn->Execute('INSERT INTO clobtable (id, clobcol) VALUES (1, null)'); + * $conn->UpdateClob('clobtable','clobcol',$clob,'id=1'); + */ + function UpdateClob($table,$column,$val,$where) + { + return $this->UpdateBlob($table,$column,$val,$where,'CLOB'); + } + + + /** + * $meta contains the desired type, which could be... + * C for character. You will have to define the precision yourself. + * X for teXt. For unlimited character lengths. + * B for Binary + * F for floating point, with no need to define scale and precision + * N for decimal numbers, you will have to define the (scale, precision) yourself + * D for date + * T for timestamp + * L for logical/Boolean + * I for integer + * R for autoincrement counter/integer + * and if you want to use double-byte, add a 2 to the end, like C2 or X2. + * + * + * @return the actual type of the data or false if no such type available + */ + function ActualType($meta) + { + switch($meta) { + case 'C': + case 'X': + return 'VARCHAR'; + case 'B': + + case 'D': + case 'T': + case 'L': + + case 'R': + + case 'I': + case 'N': + return false; + } + } + + /* + * Maximum size of C field + */ + function CharMax() + { + return 255; /* make it conservative if not defined */ + } + + + /* + * Maximum size of X field + */ + function TextMax() + { + return 4000; /* make it conservative if not defined */ + } + + /** + * Close Connection + */ + function Close() + { + return $this->_close(); + + /* "Simon Lee" reports that persistent connections need */ + /* to be closed too! */ + /* if ($this->_isPersistentConnection != true) return $this->_close(); */ + /* else return true; */ + } + + /** + * Begin a Transaction. Must be followed by CommitTrans() or RollbackTrans(). + * + * @return true if succeeded or false if database does not support transactions + */ + function BeginTrans() {return false;} + + + /** + * If database does not support transactions, always return true as data always commited + * + * @param $ok set to false to rollback transaction, true to commit + * + * @return true/false. + */ + function CommitTrans($ok=true) + { return true;} + + + /** + * If database does not support transactions, rollbacks always fail, so return false + * + * @return true/false. + */ + function RollbackTrans() + { return false;} + + + /** + * return the databases that the driver can connect to. + * Some databases will return an empty array. + * + * @return an array of database names. + */ + function MetaDatabases() + { + global $ADODB_FETCH_MODE; + + if ($this->metaDatabasesSQL) { + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); + + $arr = $this->GetCol($this->metaDatabasesSQL); + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + + return $arr; + } + + return false; + } + + /** + * @return array of tables for current database. + */ + function &MetaTables() + { + global $ADODB_FETCH_MODE; + + if ($this->metaTablesSQL) { + /* complicated state saving by the need for backward compat */ + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); + + $rs = $this->Execute($this->metaTablesSQL); + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + + if ($rs === false) return false; + $arr =& $rs->GetArray(); + $arr2 = array(); + for ($i=0; $i < sizeof($arr); $i++) { + $arr2[] = $arr[$i][0]; + } + $rs->Close(); + return $arr2; + } + return false; + } + + + /** + * List columns in a database as an array of ADOFieldObjects. + * See top of file for definition of object. + * + * @param table table name to query + * @param upper uppercase table name (required by some databases) + * + * @return array of ADOFieldObjects for current table. + */ + function &MetaColumns($table,$upper=true) + { + global $ADODB_FETCH_MODE; + + if (!empty($this->metaColumnsSQL)) { + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); + $rs = $this->Execute(sprintf($this->metaColumnsSQL,($upper)?strtoupper($table):$table)); + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + if ($rs === false) return false; + + $retarr = array(); + while (!$rs->EOF) { /* print_r($rs->fields); */ + $fld = new ADOFieldObject(); + $fld->name = $rs->fields[0]; + $fld->type = $rs->fields[1]; + if (isset($rs->fields[3]) && $rs->fields[3]) { + if ($rs->fields[3]>0) $fld->max_length = $rs->fields[3]; + $fld->scale = $rs->fields[4]; + if ($fld->scale>0) $fld->max_length += 1; + } else + $fld->max_length = $rs->fields[2]; + + $retarr[strtoupper($fld->name)] = $fld; + + $rs->MoveNext(); + } + $rs->Close(); + return $retarr; + } + return false; + } + + /** + * List columns names in a table as an array. + * @param table table name to query + * + * @return array of column names for current table. + */ + function &MetaColumnNames($table) + { + $objarr =& $this->MetaColumns($table); + if (!is_array($objarr)) return false; + + $arr = array(); + foreach($objarr as $v) { + $arr[] = $v->name; + } + return $arr; + } + + /** + * Different SQL databases used different methods to combine strings together. + * This function provides a wrapper. + * + * param s variable number of string parameters + * + * Usage: $db->Concat($str1,$str2); + * + * @return concatenated string + */ + function Concat() + { + $arr = func_get_args(); + return implode($this->concat_operator, $arr); + } + + + /** + * Converts a date "d" to a string that the database can understand. + * + * @param d a date in Unix date time format. + * + * @return date string in database date format + */ + function DBDate($d) + { + + if (empty($d) && $d !== 0) return 'null'; + + if (is_string($d) && !is_numeric($d)) { + if ($d === 'null') return $d; + if ($this->isoDates) return "'$d'"; + $d = ADOConnection::UnixDate($d); + } + + return adodb_date($this->fmtDate,$d); + } + + + /** + * Converts a timestamp "ts" to a string that the database can understand. + * + * @param ts a timestamp in Unix date time format. + * + * @return timestamp string in database timestamp format + */ + function DBTimeStamp($ts) + { + if (empty($ts) && $ts !== 0) return 'null'; + + if (is_string($ts) && !is_numeric($ts)) { + if ($ts === 'null') return $ts; + if ($this->isoDates) return "'$ts'"; + else $ts = ADOConnection::UnixTimeStamp($ts); + } + + return adodb_date($this->fmtTimeStamp,$ts); + } + + /** + * Also in ADORecordSet. + * @param $v is a date string in YYYY-MM-DD format + * + * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format + */ + function UnixDate($v) + { + if (!preg_match( "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})|", + ($v), $rr)) return false; + + if ($rr[1] <= TIMESTAMP_FIRST_YEAR) return 0; + /* h-m-s-MM-DD-YY */ + return @adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]); + } + + + /** + * Also in ADORecordSet. + * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format + * + * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format + */ + function UnixTimeStamp($v) + { + if (!preg_match( + "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ -]?(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|", + ($v), $rr)) return false; + if ($rr[1] <= TIMESTAMP_FIRST_YEAR && $rr[2]<= 1) return 0; + + /* h-m-s-MM-DD-YY */ + if (!isset($rr[5])) return adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]); + return @adodb_mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1]); + } + + /** + * Also in ADORecordSet. + * + * Format database date based on user defined format. + * + * @param v is the character date in YYYY-MM-DD format, returned by database + * @param fmt is the format to apply to it, using date() + * + * @return a date formated as user desires + */ + + function UserDate($v,$fmt='Y-m-d') + { + $tt = $this->UnixDate($v); + /* $tt == -1 if pre TIMESTAMP_FIRST_YEAR */ + if (($tt === false || $tt == -1) && $v != false) return $v; + else if ($tt == 0) return $this->emptyDate; + else if ($tt == -1) { /* pre-TIMESTAMP_FIRST_YEAR */ + } + + return adodb_date($fmt,$tt); + + } + + + /** + * Correctly quotes a string so that all strings are escaped. We prefix and append + * to the string single-quotes. + * 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) + { + if (!$magic_quotes) { + + if ($this->replaceQuote[0] == '\\'){ + /* only since php 4.0.5 */ + $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s); + /* $s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s)); */ + } + return "'".str_replace("'",$this->replaceQuote,$s)."'"; + } + + /* undo magic quotes for " */ + $s = str_replace('\\"','"',$s); + + if ($this->replaceQuote == "\\'") /* ' already quoted, no need to change anything */ + return "'$s'"; + else {/* change \' to '' for sybase/mssql */ + $s = str_replace('\\\\','\\',$s); + return "'".str_replace("\\'",$this->replaceQuote,$s)."'"; + } + } + + + /** + * Will select the supplied $page number from a recordset, given that it is paginated in pages of + * $nrows rows per page. It also saves two boolean values saying if the given page is the first + * and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination. + * + * See readme.htm#ex8 for an example of usage. + * + * @param sql + * @param nrows is the number of rows per page to get + * @param page is the page number to get (1-based) + * @param [inputarr] array of bind variables + * @param [arg3] is a private parameter only used by jlim + * @param [secs2cache] is a private parameter only used by jlim + * @return the recordset ($rs->databaseType == 'array') + * + * NOTE: phpLens uses a different algorithm and does not use PageExecute(). + * + */ + function &PageExecute($sql, $nrows, $page, $inputarr=false, $arg3=false, $secs2cache=0) + { + include_once(ADODB_DIR.'/adodb-lib.inc.php'); + if ($this->pageExecuteCountRows) return _adodb_pageexecute_all_rows($this, $sql, $nrows, $page, $inputarr, $arg3, $secs2cache); + return _adodb_pageexecute_no_last_page($this, $sql, $nrows, $page, $inputarr, $arg3, $secs2cache); + + } + + + /** + * Will select the supplied $page number from a recordset, given that it is paginated in pages of + * $nrows rows per page. It also saves two boolean values saying if the given page is the first + * and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination. + * + * @param secs2cache seconds to cache data, set to 0 to force query + * @param sql + * @param nrows is the number of rows per page to get + * @param page is the page number to get (1-based) + * @param [inputarr] array of bind variables + * @param [arg3] is a private parameter only used by jlim + * @return the recordset ($rs->databaseType == 'array') + */ + function &CachePageExecute($secs2cache, $sql, $nrows, $page,$inputarr=false, $arg3=false) + { + /*switch($this->dataProvider) { + case 'postgres': + case 'mysql': + break; + default: $secs2cache = 0; break; + }*/ + return $this->PageExecute($sql,$nrows,$page,$inputarr,$arg3,$secs2cache); + } + +} /* end class ADOConnection */ + + + + /* ============================================================================================== */ + /* CLASS ADOFetchObj */ + /* ============================================================================================== */ + + /** + * Internal placeholder for record objects. Used by ADORecordSet->FetchObj(). + */ + class ADOFetchObj { + }; + + /* ============================================================================================== */ + /* CLASS ADORecordSet_empty */ + /* ============================================================================================== */ + + /** + * Lightweight recordset when there are no records to be returned + */ + class ADORecordSet_empty + { + var $dataProvider = 'empty'; + var $databaseType = false; + var $EOF = true; + var $_numOfRows = 0; + var $fields = false; + var $connection = false; + function RowCount() {return 0;} + function RecordCount() {return 0;} + function PO_RecordCount(){return 0;} + function Close(){return true;} + function FetchRow() {return false;} + function FieldCount(){ return 0;} + } + + /* ============================================================================================== */ + /* DATE AND TIME FUNCTIONS */ + /* ============================================================================================== */ + include_once(ADODB_DIR.'/adodb-time.inc.php'); + + /* ============================================================================================== */ + /* CLASS ADORecordSet */ + /* ============================================================================================== */ + + + /** + * RecordSet class that represents the dataset returned by the database. + * To keep memory overhead low, this class holds only the current row in memory. + * No prefetching of data is done, so the RecordCount() can return -1 ( which + * means recordcount not known). + */ + class ADORecordSet { + /* + * public variables + */ + var $dataProvider = "native"; + var $fields = false; /* / holds the current row data */ + var $blobSize = 100; /* / any varchar/char field this size or greater is treated as a blob */ + /* / in other words, we use a text area for editting. */ + var $canSeek = false; /* / indicates that seek is supported */ + var $sql; /* / sql text */ + var $EOF = false; /* / Indicates that the current record position is after the last record in a Recordset object. */ + + var $emptyTimeStamp = ' '; /* / what to display when $time==0 */ + var $emptyDate = ' '; /* / what to display when $time==0 */ + var $debug = false; + var $timeCreated=0; /* / datetime in Unix format rs created -- for cached recordsets */ + + var $bind = false; /* / used by Fields() to hold array - should be private? */ + var $fetchMode; /* / default fetch mode */ + var $connection = false; /* / the parent connection */ + /* + * private variables + */ + var $_numOfRows = -1; /** number of rows, or -1 */ + var $_numOfFields = -1; /** number of fields in recordset */ + var $_queryID = -1; /** This variable keeps the result link identifier. */ + var $_currentRow = -1; /** This variable keeps the current row in the Recordset. */ + var $_closed = false; /** has recordset been closed */ + var $_inited = false; /** Init() should only be called once */ + var $_obj; /** Used by FetchObj */ + var $_names; /** Used by FetchObj */ + + var $_currentPage = -1; /** Added by Iván Oliva to implement recordset pagination */ + var $_atFirstPage = false; /** Added by Iván Oliva to implement recordset pagination */ + var $_atLastPage = false; /** Added by Iván Oliva to implement recordset pagination */ + var $_lastPageNo = -1; + var $_maxRecordCount = 0; + var $dateHasTime = false; + + /** + * Constructor + * + * @param queryID this is the queryID returned by ADOConnection->_query() + * + */ + function ADORecordSet($queryID) + { + $this->_queryID = $queryID; + } + + + + function Init() + { + if ($this->_inited) return; + $this->_inited = true; + if ($this->_queryID) @$this->_initrs(); + else { + $this->_numOfRows = 0; + $this->_numOfFields = 0; + } + + if ($this->_numOfRows != 0 && $this->_numOfFields && $this->_currentRow == -1) { + + $this->_currentRow = 0; + if ($this->EOF = ($this->_fetch() === false)) { + $this->_numOfRows = 0; /* _numOfRows could be -1 */ + } + } else { + $this->EOF = true; + } + } + + + /** + * Generate a SELECT tag string from a recordset, and return the string. + * If the recordset has 2 cols, we treat the 1st col as the containing + * the text to display to the user, and 2nd col as the return value. Default + * strings are compared with the FIRST column. + * + * @param name name of SELECT tag + * @param [defstr] the value to hilite. Use an array for multiple hilites for listbox. + * @param [blank1stItem] true to leave the 1st item in list empty + * @param [multiple] true for listbox, false for popup + * @param [size] #rows to show for listbox. not used by popup + * @param [selectAttr] additional attributes to defined for SELECT tag. + * useful for holding javascript onChange='...' handlers. + & @param [compareFields0] when we have 2 cols in recordset, we compare the defstr with + * column 0 (1st col) if this is true. This is not documented. + * + * @return HTML + * + * changes by glen.davies@cce.ac.nz to support multiple hilited items + */ + function GetMenu($name,$defstr='',$blank1stItem=true,$multiple=false, + $size=0, $selectAttr='',$compareFields0=true) + { + include_once(ADODB_DIR.'/adodb-lib.inc.php'); + return _adodb_getmenu($this, $name,$defstr,$blank1stItem,$multiple, + $size, $selectAttr,$compareFields0); + } + + /** + * Generate a SELECT tag string from a recordset, and return the string. + * If the recordset has 2 cols, we treat the 1st col as the containing + * the text to display to the user, and 2nd col as the return value. Default + * strings are compared with the SECOND column. + * + */ + function GetMenu2($name,$defstr='',$blank1stItem=true,$multiple=false,$size=0, $selectAttr='') + { + include_once(ADODB_DIR.'/adodb-lib.inc.php'); + return _adodb_getmenu($this,$name,$defstr,$blank1stItem,$multiple, + $size, $selectAttr,false); + } + + + /** + * return recordset as a 2-dimensional array. + * + * @param [nRows] is the number of rows to return. -1 means every row. + * + * @return an array indexed by the rows (0-based) from the recordset + */ + function &GetArray($nRows = -1) + { + global $ADODB_EXTENSION; if ($ADODB_EXTENSION) return adodb_getall($this,$nRows); + + $results = array(); + $cnt = 0; + while (!$this->EOF && $nRows != $cnt) { + $results[] = $this->fields; + $this->MoveNext(); + $cnt++; + } + return $results; + } + + /* + * Some databases allow multiple recordsets to be returned. This function + * will return true if there is a next recordset, or false if no more. + */ + function NextRecordSet() + { + return false; + } + + /** + * return recordset as a 2-dimensional array. + * Helper function for ADOConnection->SelectLimit() + * + * @param offset is the row to start calculations from (1-based) + * @param [nrows] is the number of rows to return + * + * @return an array indexed by the rows (0-based) from the recordset + */ + function &GetArrayLimit($nrows,$offset=-1) + { + if ($offset <= 0) { + return $this->GetArray($nrows); + } + + $this->Move($offset); + + $results = array(); + $cnt = 0; + while (!$this->EOF && $nrows != $cnt) { + $results[$cnt++] = $this->fields; + $this->MoveNext(); + } + + return $results; + } + + + /** + * Synonym for GetArray() for compatibility with ADO. + * + * @param [nRows] is the number of rows to return. -1 means every row. + * + * @return an array indexed by the rows (0-based) from the recordset + */ + function &GetRows($nRows = -1) + { + return $this->GetArray($nRows); + } + + /** + * return whole recordset as a 2-dimensional associative array if there are more than 2 columns. + * The first column is treated as the key and is not included in the array. + * If there is only 2 columns, it will return a 1 dimensional array of key-value pairs unless + * $force_array == true. + * + * @param [force_array] has only meaning if we have 2 data columns. If false, a 1 dimensional + * array is returned, otherwise a 2 dimensional array is returned. If this sounds confusing, + * read the source. + * + * @param [first2cols] means if there are more than 2 cols, ignore the remaining cols and + * instead of returning array[col0] => array(remaining cols), return array[col0] => col1 + * + * @return an associative array indexed by the first column of the array, + * or false if the data has less than 2 cols. + */ + function &GetAssoc($force_array = false, $first2cols = false) { + $cols = $this->_numOfFields; + if ($cols < 2) { + return false; + } + $numIndex = isset($this->fields[0]); + $results = array(); + + if (!$first2cols && ($cols > 2 || $force_array)) { + if ($numIndex) { + while (!$this->EOF) { + $results[trim($this->fields[0])] = array_slice($this->fields, 1); + $this->MoveNext(); + } + } else { + while (!$this->EOF) { + $results[trim(reset($this->fields))] = array_slice($this->fields, 1); + $this->MoveNext(); + } + } + } else { + /* return scalar values */ + if ($numIndex) { + while (!$this->EOF) { + /* some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string */ + $results[trim(($this->fields[0]))] = $this->fields[1]; + $this->MoveNext(); + } + } else { + while (!$this->EOF) { + /* some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string */ + $v1 = trim(reset($this->fields)); + $v2 = ''.next($this->fields); + $results[$v1] = $v2; + $this->MoveNext(); + } + } + } + return $results; + } + + + /** + * + * @param v is the character timestamp in YYYY-MM-DD hh:mm:ss format + * @param fmt is the format to apply to it, using date() + * + * @return a timestamp formated as user desires + */ + function UserTimeStamp($v,$fmt='Y-m-d H:i:s') + { + $tt = $this->UnixTimeStamp($v); + /* $tt == -1 if pre TIMESTAMP_FIRST_YEAR */ + if (($tt === false || $tt == -1) && $v != false) return $v; + if ($tt == 0) return $this->emptyTimeStamp; + return adodb_date($fmt,$tt); + } + + + /** + * @param v is the character date in YYYY-MM-DD format, returned by database + * @param fmt is the format to apply to it, using date() + * + * @return a date formated as user desires + */ + function UserDate($v,$fmt='Y-m-d') + { + $tt = $this->UnixDate($v); + /* $tt == -1 if pre TIMESTAMP_FIRST_YEAR */ + if (($tt === false || $tt == -1) && $v != false) return $v; + else if ($tt == 0) return $this->emptyDate; + else if ($tt == -1) { /* pre-TIMESTAMP_FIRST_YEAR */ + } + return adodb_date($fmt,$tt); + + } + + + /** + * @param $v is a date string in YYYY-MM-DD format + * + * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format + */ + function UnixDate($v) + { + + if (!preg_match( "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})|", + ($v), $rr)) return false; + + if ($rr[1] <= TIMESTAMP_FIRST_YEAR) return 0; + /* h-m-s-MM-DD-YY */ + return @adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]); + } + + + /** + * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format + * + * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format + */ + function UnixTimeStamp($v) + { + + if (!preg_match( + "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ -]?(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|", + ($v), $rr)) return false; + if ($rr[1] <= TIMESTAMP_FIRST_YEAR && $rr[2]<= 1) return 0; + + /* h-m-s-MM-DD-YY */ + if (!isset($rr[5])) return adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]); + return @adodb_mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1]); + } + + + /** + * PEAR DB Compat - do not use internally + */ + function Free() + { + return $this->Close(); + } + + + /** + * PEAR DB compat, number of rows + */ + function NumRows() + { + return $this->_numOfRows; + } + + + /** + * PEAR DB compat, number of cols + */ + function NumCols() + { + return $this->_numOfFields; + } + + /** + * Fetch a row, returning false if no more rows. + * This is PEAR DB compat mode. + * + * @return false or array containing the current record + */ + function FetchRow() + { + if ($this->EOF) return false; + $arr = $this->fields; + $this->_currentRow++; + if (!$this->_fetch()) $this->EOF = true; + return $arr; + } + + + /** + * Fetch a row, returning PEAR_Error if no more rows. + * This is PEAR DB compat mode. + * + * @return DB_OK or error object + */ + function FetchInto(&$arr) + { + if ($this->EOF) return (defined('PEAR_ERROR_RETURN')) ? new PEAR_Error('EOF',-1): false; + $arr = $this->fields; + $this->MoveNext(); + return 1; /* DB_OK */ + } + + + /** + * Move to the first row in the recordset. Many databases do NOT support this. + * + * @return true or false + */ + function MoveFirst() + { + if ($this->_currentRow == 0) return true; + return $this->Move(0); + } + + + /** + * Move to the last row in the recordset. + * + * @return true or false + */ + function MoveLast() + { + if ($this->_numOfRows >= 0) return $this->Move($this->_numOfRows-1); + if ($this->EOF) return false; + while (!$this->EOF) { + $f = $this->fields; + $this->MoveNext(); + } + $this->fields = $f; + $this->EOF = false; + return true; + } + + + /** + * Move to next record in the recordset. + * + * @return true if there still rows available, or false if there are no more rows (EOF). + */ + function MoveNext() + { + if (!$this->EOF) { + $this->_currentRow++; + if ($this->_fetch()) return true; + } + $this->EOF = true; + /* -- tested error handling when scrolling cursor -- seems useless. + $conn = $this->connection; + if ($conn && $conn->raiseErrorFn && ($errno = $conn->ErrorNo())) { + $fn = $conn->raiseErrorFn; + $fn($conn->databaseType,'MOVENEXT',$errno,$conn->ErrorMsg().' ('.$this->sql.')',$conn->host,$conn->database); + } + */ + return false; + } + + /** + * Random access to a specific row in the recordset. Some databases do not support + * access to previous rows in the databases (no scrolling backwards). + * + * @param rowNumber is the row to move to (0-based) + * + * @return true if there still rows available, or false if there are no more rows (EOF). + */ + function Move($rowNumber = 0) + { + $this->EOF = false; + if ($rowNumber == $this->_currentRow) return true; + if ($rowNumber >= $this->_numOfRows) + if ($this->_numOfRows != -1) $rowNumber = $this->_numOfRows-2; + + if ($this->canSeek) { + + if ($this->_seek($rowNumber)) { + $this->_currentRow = $rowNumber; + if ($this->_fetch()) { + return true; + } + } else { + $this->EOF = true; + return false; + } + } else { + if ($rowNumber < $this->_currentRow) return false; + global $ADODB_EXTENSION; + if ($ADODB_EXTENSION) { + while (!$this->EOF && $this->_currentRow < $rowNumber) { + adodb_movenext($this); + } + } else { + + while (! $this->EOF && $this->_currentRow < $rowNumber) { + $this->_currentRow++; + + if (!$this->_fetch()) $this->EOF = true; + } + } + return !($this->EOF); + } + + $this->fields = false; + $this->EOF = true; + return false; + } + + + /** + * Get the value of a field in the current row by column name. + * Will not work if ADODB_FETCH_MODE is set to ADODB_FETCH_NUM. + * + * @param colname is the field to access + * + * @return the value of $colname column + */ + function Fields($colname) + { + return $this->fields[$colname]; + } + + function GetAssocKeys($upper=true) + { + $this->bind = array(); + for ($i=0; $i < $this->_numOfFields; $i++) { + $o = $this->FetchField($i); + if ($upper === 2) $this->bind[$o->name] = $i; + else $this->bind[($upper) ? strtoupper($o->name) : strtolower($o->name)] = $i; + } + } + + /** + * Use associative array to get fields array for databases that do not support + * associative arrays. Submitted by Paolo S. Asioli paolo.asioli@libero.it + * + * If you don't want uppercase cols, set $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC + * before you execute your SQL statement, and access $rs->fields['col'] directly. + * + * $upper 0 = lowercase, 1 = uppercase, 2 = whatever is returned by FetchField + */ + function GetRowAssoc($upper=1) + { + + if (!$this->bind) { + $this->GetAssocKeys($upper); + } + + $record = array(); + foreach($this->bind as $k => $v) { + $record[$k] = $this->fields[$v]; + } + + return $record; + } + + + /** + * Clean up recordset + * + * @return true or false + */ + function Close() + { + /* free connection object - this seems to globally free the object */ + /* and not merely the reference, so don't do this... */ + /* $this->connection = false; */ + if (!$this->_closed) { + $this->_closed = true; + return $this->_close(); + } else + return true; + } + + /** + * synonyms RecordCount and RowCount + * + * @return the number of rows or -1 if this is not supported + */ + function RecordCount() {return $this->_numOfRows;} + + + /* + * If we are using PageExecute(), this will return the maximum possible rows + * that can be returned when paging a recordset. + */ + function MaxRecordCount() + { + return ($this->_maxRecordCount) ? $this->_maxRecordCount : $this->RecordCount(); + } + + /** + * synonyms RecordCount and RowCount + * + * @return the number of rows or -1 if this is not supported + */ + function RowCount() {return $this->_numOfRows;} + + + /** + * Portable RecordCount. Pablo Roca + * + * @return the number of records from a previous SELECT. All databases support this. + * + * But aware possible problems in multiuser environments. For better speed the table + * must be indexed by the condition. Heavy test this before deploying. + */ + function PO_RecordCount($table="", $condition="") { + + $lnumrows = $this->_numOfRows; + /* the database doesn't support native recordcount, so we do a workaround */ + if ($lnumrows == -1 && $this->connection) { + IF ($table) { + if ($condition) $condition = " WHERE " . $condition; + $resultrows = &$this->connection->Execute("SELECT COUNT(*) FROM $table $condition"); + if ($resultrows) $lnumrows = reset($resultrows->fields); + } + } + return $lnumrows; + } + + /** + * @return the current row in the recordset. If at EOF, will return the last row. 0-based. + */ + function CurrentRow() {return $this->_currentRow;} + + /** + * synonym for CurrentRow -- for ADO compat + * + * @return the current row in the recordset. If at EOF, will return the last row. 0-based. + */ + function AbsolutePosition() {return $this->_currentRow;} + + /** + * @return the number of columns in the recordset. Some databases will set this to 0 + * if no records are returned, others will return the number of columns in the query. + */ + function FieldCount() {return $this->_numOfFields;} + + + /** + * Get the ADOFieldObject of a specific column. + * + * @param fieldoffset is the column position to access(0-based). + * + * @return the ADOFieldObject for that column, or false. + */ + function &FetchField($fieldoffset) + { + /* must be defined by child class */ + } + + /** + * Get the ADOFieldObjects of all columns in an array. + * + */ + function FieldTypesArray() + { + $arr = array(); + for ($i=0, $max=$this->_numOfFields; $i < $max; $i++) + $arr[] = $this->FetchField($i); + return $arr; + } + + /** + * Return the fields array of the current row as an object for convenience. + * The default case is lowercase field names. + * + * @return the object with the properties set to the fields of the current row + */ + function &FetchObj() + { + return FetchObject(false); + } + + /** + * Return the fields array of the current row as an object for convenience. + * The default case is uppercase. + * + * @param $isupper to set the object property names to uppercase + * + * @return the object with the properties set to the fields of the current row + */ + function &FetchObject($isupper=true) + { + if (empty($this->_obj)) { + $this->_obj = new ADOFetchObj(); + $this->_names = array(); + for ($i=0; $i <$this->_numOfFields; $i++) { + $f = $this->FetchField($i); + $this->_names[] = $f->name; + } + } + $i = 0; + $o = &$this->_obj; + for ($i=0; $i <$this->_numOfFields; $i++) { + $name = $this->_names[$i]; + if ($isupper) $n = strtoupper($name); + else $n = $name; + + $o->$n = $this->Fields($name); + } + return $o; + } + + /** + * Return the fields array of the current row as an object for convenience. + * The default is lower-case field names. + * + * @return the object with the properties set to the fields of the current row, + * or false if EOF + * + * Fixed bug reported by tim@orotech.net + */ + function &FetchNextObj() + { + return $this->FetchNextObject(false); + } + + + /** + * Return the fields array of the current row as an object for convenience. + * The default is upper case field names. + * + * @param $isupper to set the object property names to uppercase + * + * @return the object with the properties set to the fields of the current row, + * or false if EOF + * + * Fixed bug reported by tim@orotech.net + */ + function &FetchNextObject($isupper=true) + { + $o = false; + if ($this->_numOfRows != 0 && !$this->EOF) { + $o = $this->FetchObject($isupper); + $this->_currentRow++; + if ($this->_fetch()) return $o; + } + $this->EOF = true; + return $o; + } + + /** + * Get the metatype of the column. This is used for formatting. This is because + * many databases use different names for the same type, so we transform the original + * type to our standardised version which uses 1 character codes: + * + * @param t is the type passed in. Normally is ADOFieldObject->type. + * @param len is the maximum length of that field. This is because we treat character + * fields bigger than a certain size as a 'B' (blob). + * @param fieldobj is the field object returned by the database driver. Can hold + * additional info (eg. primary_key for mysql). + * + * @return the general type of the data: + * C for character < 200 chars + * X for teXt (>= 200 chars) + * B for Binary + * N for numeric floating point + * D for date + * T for timestamp + * L for logical/Boolean + * I for integer + * R for autoincrement counter/integer + * + * + */ + function MetaType($t,$len=-1,$fieldobj=false) + { + if (is_object($t)) { + $fieldobj = $t; + $t = $fieldobj->type; + $len = $fieldobj->max_length; + } + /* changed in 2.32 to hashing instead of switch stmt for speed... */ + static $typeMap = array( + 'VARCHAR' => 'C', + 'VARCHAR2' => 'C', + 'CHAR' => 'C', + 'C' => 'C', + 'STRING' => 'C', + 'NCHAR' => 'C', + 'NVARCHAR' => 'C', + 'VARYING' => 'C', + 'BPCHAR' => 'C', + 'CHARACTER' => 'C', + 'INTERVAL' => 'C', # Postgres + ## + 'LONGCHAR' => 'X', + 'TEXT' => 'X', + 'NTEXT' => 'X', + 'M' => 'X', + 'X' => 'X', + 'CLOB' => 'X', + 'NCLOB' => 'X', + 'LVARCHAR' => 'X', + ## + 'BLOB' => 'B', + 'IMAGE' => 'B', + 'BINARY' => 'B', + 'VARBINARY' => 'B', + 'LONGBINARY' => 'B', + 'B' => 'B', + ## + 'YEAR' => 'D', /* mysql */ + 'DATE' => 'D', + 'D' => 'D', + ## + 'TIME' => 'T', + 'TIMESTAMP' => 'T', + 'DATETIME' => 'T', + 'TIMESTAMPTZ' => 'T', + 'T' => 'T', + ## + 'BOOLEAN' => 'L', + 'BIT' => 'L', + 'L' => 'L', + ## + 'COUNTER' => 'R', + 'R' => 'R', + 'SERIAL' => 'R', /* ifx */ + 'INT IDENTITY' => 'R', + ## + 'INT' => 'I', + 'INTEGER' => 'I', + 'SHORT' => 'I', + 'TINYINT' => 'I', + 'SMALLINT' => 'I', + 'I' => 'I', + ## + 'LONG' => 'N', /* interbase is numeric, oci8 is blob */ + 'BIGINT' => 'N', /* this is bigger than PHP 32-bit integers */ + 'DECIMAL' => 'N', + 'DEC' => 'N', + 'REAL' => 'N', + 'DOUBLE' => 'N', + 'DOUBLE PRECISION' => 'N', + 'SMALLFLOAT' => 'N', + 'FLOAT' => 'N', + 'NUMBER' => 'N', + 'NUM' => 'N', + 'NUMERIC' => 'N', + 'MONEY' => 'N', + + ## informix 9.2 + 'SQLINT' => 'I', + 'SQLSERIAL' => 'I', + 'SQLSMINT' => 'I', + 'SQLSMFLOAT' => 'N', + 'SQLFLOAT' => 'N', + 'SQLMONEY' => 'N', + 'SQLDECIMAL' => 'N', + 'SQLDATE' => 'D', + 'SQLVCHAR' => 'C', + 'SQLCHAR' => 'C', + 'SQLDTIME' => 'T', + 'SQLINTERVAL' => 'N', + 'SQLBYTES' => 'B', + 'SQLTEXT' => 'X' + ); + + $tmap = false; + $t = strtoupper($t); + $tmap = @$typeMap[$t]; + switch ($tmap) { + case 'C': + + /* is the char field is too long, return as text field... */ + if (!empty($this->blobSize)) { + if ($len > $this->blobSize) return 'X'; + } else if ($len > 250) { + return 'X'; + } + return 'C'; + + case 'I': + if (!empty($fieldobj->primary_key)) return 'R'; + return 'I'; + + case false: + return 'N'; + + case 'B': + if (isset($fieldobj->binary)) + return ($fieldobj->binary) ? 'B' : 'X'; + return 'B'; + + case 'D': + if (!empty($this->dateHasTime)) return 'T'; + return 'D'; + + default: + if ($t == 'LONG' && $this->dataProvider == 'oci8') return 'B'; + return $tmap; + } + } + + function _close() {} + + /** + * set/returns the current recordset page when paginating + */ + function AbsolutePage($page=-1) + { + if ($page != -1) $this->_currentPage = $page; + return $this->_currentPage; + } + + /** + * set/returns the status of the atFirstPage flag when paginating + */ + function AtFirstPage($status=false) + { + if ($status != false) $this->_atFirstPage = $status; + return $this->_atFirstPage; + } + + function LastPageNo($page = false) + { + if ($page != false) $this->_lastPageNo = $page; + return $this->_lastPageNo; + } + + /** + * set/returns the status of the atLastPage flag when paginating + */ + function AtLastPage($status=false) + { + if ($status != false) $this->_atLastPage = $status; + return $this->_atLastPage; + } +} /* end class ADORecordSet */ + + /* ============================================================================================== */ + /* CLASS ADORecordSet_array */ + /* ============================================================================================== */ + + /** + * This class encapsulates the concept of a recordset created in memory + * as an array. This is useful for the creation of cached recordsets. + * + * Note that the constructor is different from the standard ADORecordSet + */ + + class ADORecordSet_array extends ADORecordSet + { + var $databaseType = 'array'; + + var $_array; /* holds the 2-dimensional data array */ + var $_types; /* the array of types of each column (C B I L M) */ + var $_colnames; /* names of each column in array */ + var $_skiprow1; /* skip 1st row because it holds column names */ + var $_fieldarr; /* holds array of field objects */ + var $canSeek = true; + var $affectedrows = false; + var $insertid = false; + var $sql = ''; + var $compat = false; + /** + * Constructor + * + */ + function ADORecordSet_array($fakeid=1) + { + global $ADODB_FETCH_MODE,$ADODB_COMPAT_FETCH; + + /* fetch() on EOF does not delete $this->fields */ + $this->compat = !empty($ADODB_COMPAT_FETCH); + $this->ADORecordSet($fakeid); /* fake queryID */ + $this->fetchMode = $ADODB_FETCH_MODE; + } + + + /** + * Setup the Array. Later we will have XML-Data and CSV handlers + * + * @param array is a 2-dimensional array holding the data. + * The first row should hold the column names + * unless paramter $colnames is used. + * @param typearr holds an array of types. These are the same types + * used in MetaTypes (C,B,L,I,N). + * @param [colnames] array of column names. If set, then the first row of + * $array should not hold the column names. + */ + function InitArray($array,$typearr,$colnames=false) + { + $this->_array = $array; + $this->_types = $typearr; + if ($colnames) { + $this->_skiprow1 = false; + $this->_colnames = $colnames; + } else $this->_colnames = $array[0]; + + $this->Init(); + } + /** + * Setup the Array and datatype file objects + * + * @param array is a 2-dimensional array holding the data. + * The first row should hold the column names + * unless paramter $colnames is used. + * @param fieldarr holds an array of ADOFieldObject's. + */ + function InitArrayFields($array,$fieldarr) + { + $this->_array = $array; + $this->_skiprow1= false; + if ($fieldarr) { + $this->_fieldobjects = $fieldarr; + } + $this->Init(); + } + + function &GetArray($nRows=-1) + { + if ($nRows == -1 && $this->_currentRow <= 0 && !$this->_skiprow1) { + return $this->_array; + } else { + return ADORecordSet::GetArray($nRows); + } + } + + function _initrs() + { + $this->_numOfRows = sizeof($this->_array); + if ($this->_skiprow1) $this->_numOfRows -= 1; + + $this->_numOfFields =(isset($this->_fieldobjects)) ? + sizeof($this->_fieldobjects):sizeof($this->_types); + } + + /* Use associative array to get fields array */ + function Fields($colname) + { + if ($this->fetchMode & ADODB_FETCH_ASSOC) 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)]]; + } + + function &FetchField($fieldOffset = -1) + { + if (isset($this->_fieldobjects)) { + return $this->_fieldobjects[$fieldOffset]; + } + $o = new ADOFieldObject(); + $o->name = $this->_colnames[$fieldOffset]; + $o->type = $this->_types[$fieldOffset]; + $o->max_length = -1; /* length not known */ + + return $o; + } + + function _seek($row) + { + if (sizeof($this->_array) && $row < $this->_numOfRows) { + $this->fields = $this->_array[$row]; + return true; + } + return false; + } + + function MoveNext() + { + if (!$this->EOF) { + $this->_currentRow++; + + $pos = $this->_currentRow; + if ($this->_skiprow1) $pos += 1; + + if ($this->_numOfRows <= $pos) { + if (!$this->compat) $this->fields = false; + } else { + $this->fields = $this->_array[$pos]; + return true; + } + $this->EOF = true; + } + + return false; + } + + function _fetch() + { + $pos = $this->_currentRow; + if ($this->_skiprow1) $pos += 1; + + if ($this->_numOfRows <= $pos) { + if (!$this->compat) $this->fields = false; + return false; + } + + $this->fields = $this->_array[$pos]; + return true; + } + + function _close() + { + return true; + } + + } /* ADORecordSet_array */ + + /* ============================================================================================== */ + /* HELPER FUNCTIONS */ + /* ============================================================================================== */ + + /** + * Synonym for ADOLoadCode. + * + * @deprecated + */ + function ADOLoadDB($dbType) + { + return ADOLoadCode($dbType); + } + + /** + * Load the code for a specific database driver + */ + function ADOLoadCode($dbType) + { + GLOBAL $ADODB_Database; + + if (!$dbType) return false; + $ADODB_Database = strtolower($dbType); + switch ($ADODB_Database) { + case 'maxsql': $ADODB_Database = 'mysqlt'; break; + case 'postgres': + case 'pgsql': $ADODB_Database = 'postgres7'; break; + } + /* Karsten Kraus */ + return @include_once(ADODB_DIR."/drivers/adodb-".$ADODB_Database.".inc.php"); + } + + /** + * synonym for ADONewConnection for people like me who cannot remember the correct name + */ + function &NewADOConnection($db='') + { + return ADONewConnection($db); + } + + /** + * Instantiate a new Connection class for a specific database driver. + * + * @param [db] is the database Connection object to create. If undefined, + * use the last database driver that was loaded by ADOLoadCode(). + * + * @return the freshly created instance of the Connection class. + */ + function &ADONewConnection($db='') + { + GLOBAL $ADODB_Database; + + $rez = true; + if ($db) { + if ($ADODB_Database != $db) ADOLoadCode($db); + } else { + if (!empty($ADODB_Database)) { + ADOLoadCode($ADODB_Database); + } else { + $rez = false; + } + } + + $errorfn = (defined('ADODB_ERROR_HANDLER')) ? ADODB_ERROR_HANDLER : false; + if (!$rez) { + if ($errorfn) { + /* raise an error */ + $errorfn('ADONewConnection', 'ADONewConnection', -998, + "could not load the database driver for '$db", + $dbtype); + } else + ADOConnection::outp( " ADONewConnection: Unable to load database driver '$db'
",false); + + return false; + } + + $cls = 'ADODB_'.$ADODB_Database; + $obj =& new $cls(); + if ($errorfn) $obj->raiseErrorFn = $errorfn; + + return $obj; + } + + function &NewDataDictionary(&$conn) + { + $provider = $conn->dataProvider; + $drivername = $conn->databaseType; + if ($provider !== 'native' && $provider != 'odbc' && $provider != 'ado') + $drivername = $conn->dataProvider; + else { + if (substr($drivername,0,5) == 'odbc_') $drivername = substr($drivername,5); + else if (substr($drivername,0,4) == 'ado_') $drivername = substr($drivername,4); + else + switch($drivername) { + case 'oracle': $drivername = 'oci8';break; + case 'sybase': $drivername = 'mssql';break; + case 'access': + case 'db2': + break; + default: + $drivername = 'generic'; + break; + } + } + include_once(ADODB_DIR.'/adodb-lib.inc.php'); + include_once(ADODB_DIR.'/adodb-datadict.inc.php'); + $path = ADODB_DIR."/datadict/datadict-$drivername.inc.php"; + + if (!file_exists($path)) { + ADOConnection::outp("Database driver '$path' not available"); + return false; + } + include_once($path); + $class = "ADODB2_$drivername"; + $dict =& new $class(); + $dict->dataProvider = $conn->dataProvider; + $dict->connection = &$conn; + $dict->upperName = strtoupper($drivername); + if (is_resource($conn->_connectionID)) + $dict->serverInfo = $conn->ServerInfo(); + + return $dict; + } + + + /** + * Save a file $filename and its $contents (normally for caching) with file locking + */ + function adodb_write_file($filename, $contents,$debug=false) + { + # http:/* www.php.net/bugs.php?id=9203 Bug that flock fails on Windows */ + # So to simulate locking, we assume that rename is an atomic operation. + # First we delete $filename, then we create a $tempfile write to it and + # rename to the desired $filename. If the rename works, then we successfully + # modified the file exclusively. + # What a stupid need - having to simulate locking. + # Risks: + # 1. $tempfile name is not unique -- very very low + # 2. unlink($filename) fails -- ok, rename will fail + # 3. adodb reads stale file because unlink fails -- ok, $rs timeout occurs + # 4. another process creates $filename between unlink() and rename() -- ok, rename() fails and cache updated + if (strpos(strtoupper(PHP_OS),'WIN') !== false) { + /* skip the decimal place */ + $mtime = substr(str_replace(' ','_',microtime()),2); + /* unlink will let some latencies develop, so uniqid() is more random */ + @unlink($filename); + /* getmypid() actually returns 0 on Win98 - never mind! */ + $tmpname = $filename.uniqid($mtime).getmypid(); + if (!($fd = fopen($tmpname,'a'))) return false; + $ok = ftruncate($fd,0); + if (!fwrite($fd,$contents)) $ok = false; + fclose($fd); + chmod($tmpname,0644); + if (!@rename($tmpname,$filename)) { + unlink($tmpname); + $ok = false; + } + if (!$ok) { + if ($debug) ADOConnection::outp( " Rename $tmpname ".($ok? 'ok' : 'failed')); + } + return $ok; + } + if (!($fd = fopen($filename, 'a'))) return false; + if (flock($fd, LOCK_EX) && ftruncate($fd, 0)) { + $ok = fwrite( $fd, $contents ); + fclose($fd); + chmod($filename,0644); + }else { + fclose($fd); + if ($debug)ADOConnection::outp( " Failed acquiring lock for $filename
\n"); + $ok = false; + } + + return $ok; + } + + + function adodb_backtrace($print=true) + { + $s = ''; + if (PHPVERSION() >= 4.3) { + + $MAXSTRLEN = 64; + + $s = ''; + $traceArr = debug_backtrace(); + array_shift($traceArr); + $tabs = sizeof($traceArr)-1; + + foreach ($traceArr as $arr) { + $args = array(); + for ($i=0; $i < $tabs; $i++) $s .= ' '; + $tabs -= 1; + $s .= ''; + if (isset($arr['class'])) $s .= $arr['class'].'.'; + if (isset($arr['args'])) + foreach($arr['args'] as $v) { + if (is_null($v)) $args[] = 'null'; + else if (is_array($v)) $args[] = 'Array['.sizeof($v).']'; + else if (is_object($v)) $args[] = 'Object:'.get_class($v); + else if (is_bool($v)) $args[] = $v ? 'true' : 'false'; + else { + $v = (string) @$v; + $str = htmlspecialchars(substr($v,0,$MAXSTRLEN)); + if (strlen($v) > $MAXSTRLEN) $str .= '...'; + $args[] = $str; + } + } + $s .= $arr['function'].'('.implode(', ',$args).')'; + $s .= @sprintf(" # line %4d, file: %s", + $arr['line'],$arr['file'],$arr['file']); + $s .= "\n"; + } + $s .= ''; + if ($print) print $s; + } + return $s; + } + +} /* defined */ +?> diff --git a/lib/adodb/crypt.inc.php b/lib/adodb/crypt.inc.php index 7266ab619d..57e8894fa9 100644 --- a/lib/adodb/crypt.inc.php +++ b/lib/adodb/crypt.inc.php @@ -1,64 +1,64 @@ - -class MD5Crypt{ - function keyED($txt,$encrypt_key) - { - $encrypt_key = md5($encrypt_key); - $ctr=0; - $tmp = ""; - for ($i=0;$ikeyED($tmp,$key)); - } - - function Decrypt($txt,$key) - { - $txt = $this->keyED(base64_decode($txt),$key); - $tmp = ""; - for ($i=0;$i = 58 && $randnumber <= 64) || ($randnumber >= 91 && $randnumber <= 96)) - { - $randnumber = rand(48,120); - } - - $randomPassword .= chr($randnumber); - } - return $randomPassword; - } - -} + */ +class MD5Crypt{ + function keyED($txt,$encrypt_key) + { + $encrypt_key = md5($encrypt_key); + $ctr=0; + $tmp = ""; + for ($i=0;$i keyED($tmp,$key)); + } + + function Decrypt($txt,$key) + { + $txt = $this->keyED(base64_decode($txt),$key); + $tmp = ""; + for ($i=0;$i = 58 && $randnumber <= 64) || ($randnumber >= 91 && $randnumber <= 96)) + { + $randnumber = rand(48,120); + } + + $randomPassword .= chr($randnumber); + } + return $randomPassword; + } + +} ?> \ No newline at end of file diff --git a/lib/adodb/datadict/datadict-access.inc.php b/lib/adodb/datadict/datadict-access.inc.php new file mode 100644 index 0000000000..7f821592ae --- /dev/null +++ b/lib/adodb/datadict/datadict-access.inc.php @@ -0,0 +1,92 @@ +debug) ADOConnection::outp("Warning: Access does not supported DEFAULT values (field $fname)"); + } + if ($fnotnull) $suffix .= ' NOT NULL'; + if ($fconstraint) $suffix .= ' '.$fconstraint; + return $suffix; + } + + function CreateDatabase($dbname,$options=false) + { + return array(); + } + + + function SetSchema($schema) + { + } + + function AlterColumnSQL($tabname, $flds) + { + if ($this->debug) ADOConnection::outp("AlterColumnSQL not supported"); + return array(); + } + + + function DropColumnSQL($tabname, $flds) + { + if ($this->debug) ADOConnection::outp("DropColumnSQL not supported"); + return array(); + } + +} + + +?> \ No newline at end of file diff --git a/lib/adodb/datadict/datadict-db2.inc.php b/lib/adodb/datadict/datadict-db2.inc.php new file mode 100644 index 0000000000..15912c7e48 --- /dev/null +++ b/lib/adodb/datadict/datadict-db2.inc.php @@ -0,0 +1,74 @@ +debug) ADOConnection::outp("AlterColumnSQL not supported"); + return array(); + } + + + function DropColumnSQL($tabname, $flds) + { + if ($this->debug) ADOConnection::outp("DropColumnSQL not supported"); + return array(); + } + +} + + +?> \ No newline at end of file diff --git a/lib/adodb/datadict/datadict-generic.inc.php b/lib/adodb/datadict/datadict-generic.inc.php new file mode 100644 index 0000000000..b25d2c5988 --- /dev/null +++ b/lib/adodb/datadict/datadict-generic.inc.php @@ -0,0 +1,122 @@ +debug) ADOConnection::outp("AlterColumnSQL not supported"); + return array(); + } + + + function DropColumnSQL($tabname, $flds) + { + if ($this->debug) ADOConnection::outp("DropColumnSQL not supported"); + return array(); + } + +} + +/* +//db2 + function ActualType($meta) + { + switch($meta) { + case 'C': return 'VARCHAR'; + case 'X': return 'VARCHAR'; + + case 'C2': return 'VARCHAR'; // up to 32K + case 'X2': return 'VARCHAR'; + + case 'B': return 'BLOB'; + + case 'D': return 'DATE'; + case 'T': return 'TIMESTAMP'; + + case 'L': return 'SMALLINT'; + case 'I': return 'INTEGER'; + case 'I1': return 'SMALLINT'; + case 'I2': return 'SMALLINT'; + case 'I4': return 'INTEGER'; + case 'I8': return 'BIGINT'; + + case 'F': return 'DOUBLE'; + case 'N': return 'DECIMAL'; + default: + return $meta; + } + } + +// ifx +function ActualType($meta) + { + switch($meta) { + case 'C': return 'VARCHAR';// 255 + case 'X': return 'TEXT'; + + case 'C2': return 'NVARCHAR'; + case 'X2': return 'TEXT'; + + case 'B': return 'BLOB'; + + case 'D': return 'DATE'; + case 'T': return 'DATETIME'; + + case 'L': return 'SMALLINT'; + case 'I': return 'INTEGER'; + case 'I1': return 'SMALLINT'; + case 'I2': return 'SMALLINT'; + case 'I4': return 'INTEGER'; + case 'I8': return 'DECIMAL(20)'; + + case 'F': return 'FLOAT'; + case 'N': return 'DECIMAL'; + default: + return $meta; + } + } +*/ +?> \ No newline at end of file diff --git a/lib/adodb/datadict/datadict-ibase.inc.php b/lib/adodb/datadict/datadict-ibase.inc.php new file mode 100644 index 0000000000..38bb8f4e62 --- /dev/null +++ b/lib/adodb/datadict/datadict-ibase.inc.php @@ -0,0 +1,64 @@ +debug) ADOConnection::outp("AlterColumnSQL not supported"); + return array(); + } + + + function DropColumnSQL($tabname, $flds) + { + if ($this->debug) ADOConnection::outp("DropColumnSQL not supported"); + return array(); + } + +} + + +?> \ No newline at end of file diff --git a/lib/adodb/datadict/datadict-informix.inc.php b/lib/adodb/datadict/datadict-informix.inc.php new file mode 100644 index 0000000000..fbe8ccd3b8 --- /dev/null +++ b/lib/adodb/datadict/datadict-informix.inc.php @@ -0,0 +1,77 @@ +debug) ADOConnection::outp("AlterColumnSQL not supported"); + return array(); + } + + + function DropColumnSQL($tabname, $flds) + { + if ($this->debug) ADOConnection::outp("DropColumnSQL not supported"); + return array(); + } + + /* return string must begin with space */ + function _CreateSuffix($fname, &$ftype, $fnotnull,$fdefault,$fautoinc,$fconstraint) + { + if ($fautoinc) { + $ftype = 'SERIAL'; + return ''; + } + $suffix = ''; + if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault"; + if ($fnotnull) $suffix .= ' NOT NULL'; + if ($fconstraint) $suffix .= ' '.$fconstraint; + return $suffix; + } + +} + +?> \ No newline at end of file diff --git a/lib/adodb/datadict/datadict-mssql.inc.php b/lib/adodb/datadict/datadict-mssql.inc.php index bd5c25b3bc..0b9e85446d 100644 --- a/lib/adodb/datadict/datadict-mssql.inc.php +++ b/lib/adodb/datadict/datadict-mssql.inc.php @@ -1,183 +1,211 @@ -schema) $tabname = $this->schema.'.'.$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) - { - if ($this->schema) $tabname = $this->schema.'.'.$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) - { - if ($this->schema) $tabname = $this->schema.'.'.$tabname; - if (!is_array($flds)) $flds = explode(',',$flds); - $f = array(); - $s = "ALTER TABLE $tabname"; - foreach($flds as $v) { - $f[] = "\n$this->dropCol $v"; - } - $s .= implode(',',$f); - $sql[] = $s; - return $sql; - } - - // return string must begin with space - function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint) - { - $suffix = ''; - if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault"; - if ($fautoinc) $suffix .= ' IDENTITY(1,1)'; - if ($fnotnull) $suffix .= ' NOT 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) - { - if (isset($idxoptions['REPLACE'])) $sql[] = "DROP INDEX $idxname"; - if (isset($idxoptions['UNIQUE'])) $unique = ' UNIQUE'; - else $unique = ''; - if (is_array($flds)) $flds = implode(', ',$flds); - if (isset($idxoptions['CLUSTERED'])) $clustered = ' CLUSTERED'; - else $clustered = ''; - - $s = "CREATE$unique$clustered INDEX $idxname ON $tabname ($flds)"; - if (isset($idxoptions[$this->upperName])) $s .= $idxoptions[$this->upperName]; - $sql[] = $s; - - return $sql; - } -} +type; + $len = $fieldobj->max_length; + } + + $len = -1; /* mysql max_length is not accurate */ + switch (strtoupper($t)) { + + 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': + case 'X': return 'TEXT'; + + 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 '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) + { + if ($this->schema) $tabname = $this->schema.'.'.$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) + { + if ($this->schema) $tabname = $this->schema.'.'.$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) + { + if ($this->schema) $tabname = $this->schema.'.'.$tabname; + if (!is_array($flds)) $flds = explode(',',$flds); + $f = array(); + $s = "ALTER TABLE $tabname"; + foreach($flds as $v) { + $f[] = "\n$this->dropCol $v"; + } + $s .= implode(',',$f); + $sql[] = $s; + return $sql; + } + + /* return string must begin with space */ + function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint) + { + $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) + { + if (isset($idxoptions['REPLACE'])) $sql[] = "DROP INDEX $idxname"; + if (isset($idxoptions['UNIQUE'])) $unique = ' UNIQUE'; + else $unique = ''; + if (is_array($flds)) $flds = implode(', ',$flds); + if (isset($idxoptions['CLUSTERED'])) $clustered = ' CLUSTERED'; + else $clustered = ''; + + $s = "CREATE$unique$clustered INDEX $idxname ON $tabname ($flds)"; + if (isset($idxoptions[$this->upperName])) $s .= $idxoptions[$this->upperName]; + $sql[] = $s; + + return $sql; + } +} ?> \ No newline at end of file diff --git a/lib/adodb/datadict/datadict-mysql.inc.php b/lib/adodb/datadict/datadict-mysql.inc.php index a409d9947e..7153f20fe6 100644 --- a/lib/adodb/datadict/datadict-mysql.inc.php +++ b/lib/adodb/datadict/datadict-mysql.inc.php @@ -1,92 +1,147 @@ -upperName])) $s .= $idxoptions[$this->upperName]; - $sql[] = $s; - - return $sql; - } -} +type; + $len = $fieldobj->max_length; + } + + $len = -1; /* mysql max_length is not accurate */ + switch (strtoupper($t)) { + case 'STRING': + case 'CHAR': + case 'VARCHAR': + case 'TINYBLOB': + case 'TINYTEXT': + case 'ENUM': + case 'SET': + if ($len <= $this->blobSize) return 'C'; + + case 'TEXT': + case 'LONGTEXT': + case 'MEDIUMTEXT': + return 'X'; + + /* php_mysql extension always returns 'blob' even if 'text' */ + /* so we have to check whether binary... */ + case 'IMAGE': + case 'LONGBLOB': + case 'BLOB': + case 'MEDIUMBLOB': + return !empty($fieldobj->binary) ? 'B' : 'X'; + + case 'YEAR': + case 'DATE': return 'D'; + + case 'TIME': + case 'DATETIME': + case 'TIMESTAMP': return 'T'; + + case 'FLOAT': + case 'DOUBLE': + return 'F'; + + case 'INT': + case 'INTEGER': return (!empty($fieldobj->primary_key)) ? 'R' : 'I'; + case 'TINYINT': return (!empty($fieldobj->primary_key)) ? 'R' : 'I1'; + case 'SMALLINT': return (!empty($fieldobj->primary_key)) ? 'R' : 'I2'; + case 'MEDIUMINT': return (!empty($fieldobj->primary_key)) ? 'R' : 'I4'; + case 'BIGINT': return (!empty($fieldobj->primary_key)) ? 'R' : 'I8'; + default: return 'N'; + } + } + + function ActualType($meta) + { + switch(strtoupper($meta)) { + case 'C': return 'VARCHAR'; + case 'XL': + case 'X': return 'LONGTEXT'; + + case 'C2': return 'VARCHAR'; + case 'X2': return 'LONGTEXT'; + + case 'B': return 'LONGBLOB'; + + case 'D': return 'DATE'; + case 'T': return 'DATETIME'; + case 'L': return 'TINYINT'; + + case 'I': return 'INTEGER'; + case 'I1': return 'TINYINT'; + case 'I2': return 'SMALLINT'; + case 'I4': return 'MEDIUMINT'; + case 'I8': return 'BIGINT'; + + case 'F': return 'DOUBLE'; + case 'N': return 'NUMERIC'; + default: + return $meta; + } + } + + /* return string must begin with space */ + function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned) + { + $suffix = ''; + if ($funsigned) $suffix .= ' UNSIGNED'; + if ($fnotnull) $suffix .= ' NOT NULL'; + if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault"; + if ($fautoinc) $suffix .= ' AUTO_INCREMENT'; + if ($fconstraint) $suffix .= ' '.$fconstraint; + return $suffix; + } + + /* + CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name [(create_definition,...)] + [table_options] [select_statement] + create_definition: + col_name type [NOT NULL | NULL] [DEFAULT default_value] [AUTO_INCREMENT] + [PRIMARY KEY] [reference_definition] + or PRIMARY KEY (index_col_name,...) + or KEY [index_name] (index_col_name,...) + or INDEX [index_name] (index_col_name,...) + or UNIQUE [INDEX] [index_name] (index_col_name,...) + or FULLTEXT [INDEX] [index_name] (index_col_name,...) + or [CONSTRAINT symbol] FOREIGN KEY [index_name] (index_col_name,...) + [reference_definition] + or CHECK (expr) + */ + + /* + CREATE [UNIQUE|FULLTEXT] INDEX index_name + ON tbl_name (col_name[(length)],... ) + */ + + function _IndexSQL($idxname, $tabname, $flds, $idxoptions) + { + /* if (isset($idxoptions['REPLACE'])) $sql[] = "DROP INDEX IF EXISTS $idxname"; */ + if (isset($idxoptions['FULLTEXT'])) $unique = ' FULLTEXT'; + else if (isset($idxoptions['UNIQUE'])) $unique = ' UNIQUE'; + else $unique = ''; + + if (is_array($flds)) $flds = implode(', ',$flds); + $s = "CREATE$unique INDEX $idxname ON $tabname ($flds)"; + if (isset($idxoptions[$this->upperName])) $s .= $idxoptions[$this->upperName]; + $sql[] = $s; + + return $sql; + } +} ?> \ No newline at end of file diff --git a/lib/adodb/datadict/datadict-oci8.inc.php b/lib/adodb/datadict/datadict-oci8.inc.php index 8e7e248dd7..1622e1a861 100644 --- a/lib/adodb/datadict/datadict-oci8.inc.php +++ b/lib/adodb/datadict/datadict-oci8.inc.php @@ -1,180 +1,244 @@ -_Options($options); - $password = isset($options['PASSWORD']) ? $options['PASSWORD'] : 'tiger'; - $tablespace = isset($options["TABLESPACE"]) ? " DEFAULT TABLESPACE ".$options["TABLESPACE"] : ''; - $sql[] = "CREATE USER ".$dbname." IDENTIFIED BY ".$password.$tablespace; - $sql[] = "GRANT CREATE SESSION, CREATE TABLE,UNLIMITED TABLESPACE,CREATE SEQUENCE TO $dbname"; - - return $sql; - } - - function AddColumnSQL($tabname, $flds) - { - $f = array(); - list($lines,$pkey) = $this->_GenFields($flds); - $s = "ALTER TABLE $tabname ADD ("; - foreach($lines as $v) { - $f[] = "\n $v"; - } - - $s .= implode(',',$f).')'; - $sql[] = $s; - return $sql; - } - - function AlterColumnSQL($tabname, $flds) - { - $f = array(); - list($lines,$pkey) = $this->_GenFields($flds); - $s = "ALTER TABLE $tabname MODIFY("; - foreach($lines as $v) { - $f[] = "\n $v"; - } - $s .= implode(',',$f).')'; - $sql[] = $s; - return $sql; - } - - function DropColumnSQL($tabname, $flds) - { - if ($this->debug) ADOConnection::outp("DropColumnSQL not supported for Oracle"); - return array(); - } - - // return string must begin with space - function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint) - { - $suffix = ''; - - if ($fdefault == "''" && $fnotnull) {// this is null in oracle - $fnotnull = false; - if ($this->debug) ADOConnection::outp("NOT NULL and DEFAULT='' illegal in Oracle"); - } - - if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault"; - if ($fnotnull) { - $suffix .= ' NOT NULL'; - } - if ($fautoinc) $this->seqField = $fname; - if ($fconstraint) $suffix .= ' '.$fconstraint; - - return $suffix; - } - -/* -CREATE or replace TRIGGER jaddress_insert -before insert on jaddress -for each row -begin -select seqaddress.nextval into :new.A_ID from dual; -end; -*/ - function _Triggers($tabname,$tableoptions) - { - if (!$this->seqField) return array(); - - if ($this->schema) { - $t = strpos($tabname,'.'); - if ($t !== false) $tab = substr($tabname,$t+1); - else $tab = $tabname; - $seqname = $this->schema.'.'.$this->seqPrefix.$tab; - $trigname = $this->schema.'.TRIG_'.$this->seqPrefix.$tab; - } else { - $seqname = $this->seqPrefix.$tabname; - $trigname = "TRIG_$seqname"; - } - if (isset($tableoptions['REPLACE'])) $sql[] = "DROP SEQUENCE $seqname"; - $sql[] = "CREATE SEQUENCE $seqname"; - $sql[] = "CREATE OR REPLACE TRIGGER $trigname BEFORE insert ON $tabname - FOR EACH ROW - BEGIN - select $seqname.nextval into :new.$this->seqField from dual; - END"; - - $this->seqField = false; - return $sql; - } - - /* - CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name [(create_definition,...)] - [table_options] [select_statement] - create_definition: - col_name type [NOT NULL | NULL] [DEFAULT default_value] [AUTO_INCREMENT] - [PRIMARY KEY] [reference_definition] - or PRIMARY KEY (index_col_name,...) - or KEY [index_name] (index_col_name,...) - or INDEX [index_name] (index_col_name,...) - or UNIQUE [INDEX] [index_name] (index_col_name,...) - or FULLTEXT [INDEX] [index_name] (index_col_name,...) - or [CONSTRAINT symbol] FOREIGN KEY [index_name] (index_col_name,...) - [reference_definition] - or CHECK (expr) - */ - - - - function _IndexSQL($idxname, $tabname, $flds,$idxoptions) - { - if (isset($idxoptions['REPLACE'])) $sql[] = "DROP INDEX $idxname"; - if (isset($idxoptions['BITMAP'])) { - $unique = ' BITMAP'; - } else if (isset($idxoptions['UNIQUE'])) - $unique = ' UNIQUE'; - else - $unique = ''; - - if (is_array($flds)) $flds = implode(', ',$flds); - $s = "CREATE$unique INDEX $idxname ON $tabname ($flds)"; - if (isset($idxoptions[$this->upperName])) $s .= $idxoptions[$this->upperName]; - $sql[] = $s; - - return $sql; - } -} +type; + $len = $fieldobj->max_length; + } + switch (strtoupper($t)) { + case 'VARCHAR': + case 'VARCHAR2': + case 'CHAR': + case 'VARBINARY': + case 'BINARY': + if (isset($this) && $len <= $this->blobSize) return 'C'; + return 'X'; + + case 'NCHAR': + case 'NVARCHAR2': + case 'NVARCHAR': + if (isset($this) && $len <= $this->blobSize) return 'C2'; + return 'X2'; + + case 'NCLOB': + case 'CLOB'; + return 'XL'; + + case 'LONG RAW': + case 'LONG VARBINARY': + case 'BLOB': + return 'B'; + + case 'DATE': + return 'T'; + + case 'INT': + case 'SMALLINT': + case 'INTEGER': + return 'I'; + + default: + return 'N'; + } + } + + function ActualType($meta) + { + switch($meta) { + case 'C': return 'VARCHAR'; + case 'X': return 'VARCHAR(4000)'; + case 'XL': return 'CLOB'; + + case 'C2': return 'NVARCHAR'; + case 'X2': return 'NVARCHAR(2000)'; + + case 'B': return 'BLOB'; + + case 'D': + case 'T': return 'DATE'; + case 'L': return 'DECIMAL(1)'; + case 'I1': return 'DECIMAL(3)'; + case 'I2': return 'DECIMAL(5)'; + case 'I': + case 'I4': return 'DECIMAL(10)'; + + case 'I8': return 'DECIMAL(20)'; + case 'F': return 'DECIMAL'; + case 'N': return 'DECIMAL'; + default: + return $meta; + } + } + + function CreateDatabase($dbname, $options=false) + { + $options = $this->_Options($options); + $password = isset($options['PASSWORD']) ? $options['PASSWORD'] : 'tiger'; + $tablespace = isset($options["TABLESPACE"]) ? " DEFAULT TABLESPACE ".$options["TABLESPACE"] : ''; + $sql[] = "CREATE USER ".$dbname." IDENTIFIED BY ".$password.$tablespace; + $sql[] = "GRANT CREATE SESSION, CREATE TABLE,UNLIMITED TABLESPACE,CREATE SEQUENCE TO $dbname"; + + return $sql; + } + + function AddColumnSQL($tabname, $flds) + { + $f = array(); + list($lines,$pkey) = $this->_GenFields($flds); + $s = "ALTER TABLE $tabname ADD ("; + foreach($lines as $v) { + $f[] = "\n $v"; + } + + $s .= implode(',',$f).')'; + $sql[] = $s; + return $sql; + } + + function AlterColumnSQL($tabname, $flds) + { + $f = array(); + list($lines,$pkey) = $this->_GenFields($flds); + $s = "ALTER TABLE $tabname MODIFY("; + foreach($lines as $v) { + $f[] = "\n $v"; + } + $s .= implode(',',$f).')'; + $sql[] = $s; + return $sql; + } + + function DropColumnSQL($tabname, $flds) + { + if ($this->debug) ADOConnection::outp("DropColumnSQL not supported for Oracle"); + return array(); + } + + function _DropAutoIncrement($t) + { + return "drop sequence seq_".$t; + } + + /* return string must begin with space */ + function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned) + { + $suffix = ''; + + if ($fdefault == "''" && $fnotnull) {/* this is null in oracle */ + $fnotnull = false; + if ($this->debug) ADOConnection::outp("NOT NULL and DEFAULT='' illegal in Oracle"); + } + + if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault"; + if ($fnotnull) $suffix .= ' NOT NULL'; + + if ($fautoinc) $this->seqField = $fname; + if ($fconstraint) $suffix .= ' '.$fconstraint; + + return $suffix; + } + +/* +CREATE or replace TRIGGER jaddress_insert +before insert on jaddress +for each row +begin +select seqaddress.nextval into :new.A_ID from dual; +end; +*/ + function _Triggers($tabname,$tableoptions) + { + if (!$this->seqField) return array(); + + if ($this->schema) { + $t = strpos($tabname,'.'); + if ($t !== false) $tab = substr($tabname,$t+1); + else $tab = $tabname; + $seqname = $this->schema.'.'.$this->seqPrefix.$tab; + $trigname = $this->schema.'.TRIG_'.$this->seqPrefix.$tab; + } else { + $seqname = $this->seqPrefix.$tabname; + $trigname = "TRIG_$seqname"; + } + if (isset($tableoptions['REPLACE'])) $sql[] = "DROP SEQUENCE $seqname"; + $sql[] = "CREATE SEQUENCE $seqname"; + $sql[] = "CREATE OR REPLACE TRIGGER $trigname BEFORE insert ON $tabname + FOR EACH ROW + BEGIN + select $seqname.nextval into :new.$this->seqField from dual; + END;"; + + $this->seqField = false; + return $sql; + } + + /* + CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name [(create_definition,...)] + [table_options] [select_statement] + create_definition: + col_name type [NOT NULL | NULL] [DEFAULT default_value] [AUTO_INCREMENT] + [PRIMARY KEY] [reference_definition] + or PRIMARY KEY (index_col_name,...) + or KEY [index_name] (index_col_name,...) + or INDEX [index_name] (index_col_name,...) + or UNIQUE [INDEX] [index_name] (index_col_name,...) + or FULLTEXT [INDEX] [index_name] (index_col_name,...) + or [CONSTRAINT symbol] FOREIGN KEY [index_name] (index_col_name,...) + [reference_definition] + or CHECK (expr) + */ + + + + function _IndexSQL($idxname, $tabname, $flds,$idxoptions) + { + if (isset($idxoptions['REPLACE'])) $sql[] = "DROP INDEX $idxname"; + if (isset($idxoptions['BITMAP'])) { + $unique = ' BITMAP'; + } else if (isset($idxoptions['UNIQUE'])) + $unique = ' UNIQUE'; + else + $unique = ''; + + if (is_array($flds)) $flds = implode(', ',$flds); + $s = "CREATE$unique INDEX $idxname ON $tabname ($flds)"; + if (isset($idxoptions[$this->upperName])) $s .= $idxoptions[$this->upperName]; + if (isset($idxoptions['oci8'])) $s .= $idxoptions['oci8']; + $sql[] = $s; + + return $sql; + } + + function GetCommentSQL($table,$col) + { + $table = $this->connection->qstr($table); + $col = $this->connection->qstr($col); + return "select comments from USER_COL_COMMENTS where TABLE_NAME=$table and COLUMN_NAME=$col"; + } + + function SetCommentSQL($table,$col,$cmt) + { + $cmt = $this->connection->qstr($cmt); + return "COMMENT ON COLUMN $table.$col IS $cmt"; + } +} ?> \ No newline at end of file diff --git a/lib/adodb/datadict/datadict-postgres.inc.php b/lib/adodb/datadict/datadict-postgres.inc.php index 649f804cb0..89d1e04712 100644 --- a/lib/adodb/datadict/datadict-postgres.inc.php +++ b/lib/adodb/datadict/datadict-postgres.inc.php @@ -1,123 +1,191 @@ -debug) ADOConnection::outp("AlterColumnSQL not supported for PostgreSQL"); - return array(); - } - - - function DropColumnSQL($tabname, $flds) - { - if ($this->debug) ADOConnection::outp("DropColumnSQL not supported for PostgreSQL"); - return array(); - } - - // return string must begin with space - function _CreateSuffix($fname, &$ftype, $fnotnull,$fdefault,$fautoinc,$fconstraint) - { - if ($fautoinc) { - $ftype = 'SERIAL'; - return ''; - } - $suffix = ''; - if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault"; - if ($fnotnull) $suffix .= ' NOT NULL'; - if ($fconstraint) $suffix .= ' '.$fconstraint; - return $suffix; - } - - /* - CREATE [ [ LOCAL ] { TEMPORARY | TEMP } ] TABLE table_name ( - { column_name data_type [ DEFAULT default_expr ] [ column_constraint [, ... ] ] - | table_constraint } [, ... ] - ) - [ INHERITS ( parent_table [, ... ] ) ] - [ WITH OIDS | WITHOUT OIDS ] - where column_constraint is: - [ CONSTRAINT constraint_name ] - { NOT NULL | NULL | UNIQUE | PRIMARY KEY | - CHECK (expression) | - REFERENCES reftable [ ( refcolumn ) ] [ MATCH FULL | MATCH PARTIAL ] - [ ON DELETE action ] [ ON UPDATE action ] } - [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] - and table_constraint is: - [ CONSTRAINT constraint_name ] - { UNIQUE ( column_name [, ... ] ) | - PRIMARY KEY ( column_name [, ... ] ) | - CHECK ( expression ) | - FOREIGN KEY ( column_name [, ... ] ) REFERENCES reftable [ ( refcolumn [, ... ] ) ] - [ MATCH FULL | MATCH PARTIAL ] [ ON DELETE action ] [ ON UPDATE action ] } - [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] - */ - - - /* - CREATE [ UNIQUE ] INDEX index_name ON table -[ USING acc_method ] ( column [ ops_name ] [, ...] ) -[ WHERE predicate ] -CREATE [ UNIQUE ] INDEX index_name ON table -[ USING acc_method ] ( func_name( column [, ... ]) [ ops_name ] ) -[ WHERE predicate ] - */ - function _IndexSQL($idxname, $tabname, $flds, $idxoptions) - { - if (isset($idxoptions['REPLACE'])) $sql[] = "DROP INDEX $idxname"; - if (isset($idxoptions['UNIQUE'])) $unique = ' UNIQUE'; - else $unique = ''; - - if (is_array($flds)) $flds = implode(', ',$flds); - $s = "CREATE$unique INDEX $idxname ON $tabname "; - if (isset($idxoptions['HASH'])) $s .= 'USING HASH '; - if (isset($idxoptions[$this->upperName])) $s .= $idxoptions[$this->upperName]; - $s .= "($flds)"; - $sql[] = $s; - - return $sql; - } -} +type; + $len = $fieldobj->max_length; + } + switch (strtoupper($t)) { + case 'INTERVAL': + case 'CHAR': + case 'CHARACTER': + case 'VARCHAR': + case 'NAME': + case 'BPCHAR': + if ($len <= $this->blobSize) return 'C'; + + case 'TEXT': + return 'X'; + + case 'IMAGE': /* user defined type */ + case 'BLOB': /* user defined type */ + case 'BIT': /* This is a bit string, not a single bit, so don't return 'L' */ + case 'VARBIT': + case 'BYTEA': + return 'B'; + + case 'BOOL': + case 'BOOLEAN': + return 'L'; + + case 'DATE': + return 'D'; + + case 'TIME': + case 'DATETIME': + case 'TIMESTAMP': + case 'TIMESTAMPTZ': + return 'T'; + + case 'INTEGER': return (empty($fieldobj->primary_key) && empty($fieldobj->unique))? 'I' : 'R'; + case 'SMALLINT': + case 'INT2': return (empty($fieldobj->primary_key) && empty($fieldobj->unique))? 'I2' : 'R'; + case 'INT4': return (empty($fieldobj->primary_key) && empty($fieldobj->unique))? 'I4' : 'R'; + case 'BIGINT': + case 'INT8': return (empty($fieldobj->primary_key) && empty($fieldobj->unique))? 'I8' : 'R'; + + case 'OID': + case 'SERIAL': + return 'R'; + + case 'FLOAT4': + case 'FLOAT8': + case 'DOUBLE PRECISION': + case 'REAL': + return 'F'; + + default: + return 'N'; + } + } + + function ActualType($meta) + { + switch($meta) { + case 'C': return 'VARCHAR'; + case 'XL': + case 'X': return 'TEXT'; + + case 'C2': return 'VARCHAR'; + case 'X2': return 'TEXT'; + + case 'B': return 'BYTEA'; + + case 'D': return 'DATE'; + case 'T': return 'TIMESTAMP'; + + case 'L': return 'SMALLINT'; + case 'I': return 'INTEGER'; + case 'I1': return 'SMALLINT'; + case 'I2': return 'INT2'; + case 'I4': return 'INT4'; + case 'I8': return 'INT8'; + + case 'F': return 'FLOAT8'; + case 'N': return 'NUMERIC'; + default: + return $meta; + } + } + + function AlterColumnSQL($tabname, $flds) + { + if ($this->debug) ADOConnection::outp("AlterColumnSQL not supported for PostgreSQL"); + return array(); + } + + + function DropColumnSQL($tabname, $flds) + { + if ($this->debug) ADOConnection::outp("DropColumnSQL not supported for PostgreSQL"); + return array(); + } + + /* return string must begin with space */ + function _CreateSuffix($fname, &$ftype, $fnotnull,$fdefault,$fautoinc,$fconstraint) + { + if ($fautoinc) { + $ftype = 'SERIAL'; + return ''; + } + $suffix = ''; + if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault"; + if ($fnotnull) $suffix .= ' NOT NULL'; + if ($fconstraint) $suffix .= ' '.$fconstraint; + return $suffix; + } + + function _DropAutoIncrement($t) + { + return "drop sequence ".$t."_m_id_seq"; + } + + /* + CREATE [ [ LOCAL ] { TEMPORARY | TEMP } ] TABLE table_name ( + { column_name data_type [ DEFAULT default_expr ] [ column_constraint [, ... ] ] + | table_constraint } [, ... ] + ) + [ INHERITS ( parent_table [, ... ] ) ] + [ WITH OIDS | WITHOUT OIDS ] + where column_constraint is: + [ CONSTRAINT constraint_name ] + { NOT NULL | NULL | UNIQUE | PRIMARY KEY | + CHECK (expression) | + REFERENCES reftable [ ( refcolumn ) ] [ MATCH FULL | MATCH PARTIAL ] + [ ON DELETE action ] [ ON UPDATE action ] } + [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] + and table_constraint is: + [ CONSTRAINT constraint_name ] + { UNIQUE ( column_name [, ... ] ) | + PRIMARY KEY ( column_name [, ... ] ) | + CHECK ( expression ) | + FOREIGN KEY ( column_name [, ... ] ) REFERENCES reftable [ ( refcolumn [, ... ] ) ] + [ MATCH FULL | MATCH PARTIAL ] [ ON DELETE action ] [ ON UPDATE action ] } + [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] + */ + + + /* + CREATE [ UNIQUE ] INDEX index_name ON table +[ USING acc_method ] ( column [ ops_name ] [, ...] ) +[ WHERE predicate ] +CREATE [ UNIQUE ] INDEX index_name ON table +[ USING acc_method ] ( func_name( column [, ... ]) [ ops_name ] ) +[ WHERE predicate ] + */ + function _IndexSQL($idxname, $tabname, $flds, $idxoptions) + { + if (isset($idxoptions['REPLACE'])) $sql[] = "DROP INDEX $idxname"; + if (isset($idxoptions['UNIQUE'])) $unique = ' UNIQUE'; + else $unique = ''; + + if (is_array($flds)) $flds = implode(', ',$flds); + $s = "CREATE$unique INDEX $idxname ON $tabname "; + if (isset($idxoptions['HASH'])) $s .= 'USING HASH '; + if (isset($idxoptions[$this->upperName])) $s .= $idxoptions[$this->upperName]; + $s .= "($flds)"; + $sql[] = $s; + + return $sql; + } +} ?> \ No newline at end of file diff --git a/lib/adodb/docs-adodb.htm b/lib/adodb/docs-adodb.htm new file mode 100644 index 0000000000..8e159a1c25 --- /dev/null +++ b/lib/adodb/docs-adodb.htm @@ -0,0 +1,2717 @@ + + + ADODB Manual + ++ + + + ADOdb Library for PHP
+V3.60 16 June 2003 (c) 2000-2003 John Lim (jlim#natsoft.com.my)
+This software is dual licensed using BSD-Style and LGPL. Where + there is any discrepancy, the BSD-Style license will take precedence. This + means you can use it in proprietary and commercial products.
+Introduction
+ Unique Features
+ How People are using ADOdb
+ Feature Requests and Bug Reports
+ Installation
+ Initializing Code ADONewConnection + NewADOConnection
+ Supported Databases
+ Tutorial
+ Example 1: Select
+ Example 2: Advanced Select
+ Example 3: Insert
+ Example 4: Debugging rs2html + example
+ Example 5: MySQL and Menus
+ Example 6: Connecting to Multiple Databases at once
+ Example 7: Generating Update and Insert SQL
+ Example 8: Implementing Scrolling with Next and Previous
+ Example 9: Exporting in CSV or Tab-Delimited Format
+ Example 10: Custom filters
+ Example 11: Smart Transactions
+
+ Using Custom Error Handlers and PEAR_Error
+ Data Source Names
+ Caching
+ Pivot Tables +Variables: $ADODB_COUNTRECS + $ADODB_CACHE_DIR $ADODB_FETCH_MODE $ADODB_LANG
+ Constants: ADODB_ASSOC_CASE +
+ ADOConnection
+ Connections: Connect PConnect + NConnect
+ Executing SQL: Execute CacheExecute + SelectLimit CacheSelectLimit + Prepare PrepareSP Parameter
+ + GetOne CacheGetOne + GetRow CacheGetRow + GetAll CacheGetAll + GetCol CacheGetCol + Replace
+ ExecuteCursor + (oci8 only)
+ Generates SQL: GetUpdateSQL GetInsertSQL
+ Blobs: UpdateBlob UpdateClob + UpdateBlobFile BlobEncode + BlobDecode
+ Paging/Scrolling: PageExecute CachePageExecute
+ Cleanup: CacheFlush Close
+ Transactions: BeginTrans CommitTrans + RollbackTrans StartTrans CompleteTrans
+ Fetching Data: SetFetchMode
+ Strings: concat qstr quote
+ Dates: DBDate DBTimeStamp + UnixDate UnixTimeStamp + OffsetDate SQLDate
+ Rows Management: Affected_Rows Insert_ID + GenID CreateSequence DropSequence +
+ Error Handling: ErrorMsg ErrorNo + MetaError MetaErrorMsg
+ Data Dictionary (metadata): MetaDatabases MetaTables + MetaColumns MetaColumnNames + MetaPrimaryKeys ServerInfo +
+ Statistics and Query-Rewriting: fnExecute and fnCacheExecute
+ Deprecated: Bind BlankRecordSet
+
+ ADORecordSet
+ Returns one row:FetchRow FetchInto + FetchObject FetchNextObject + FetchObj FetchNextObj
+ Returns all rows:GetArray GetRows + GetAssoc
+ Scrolling:Move MoveNext MoveFirst + MoveLast AbsolutePosition CurrentRow + AtFirstPage AtLastPage + AbsolutePage
+ Menu generation:GetMenu GetMenu2
+ Dates:UserDate UserTimeStamp + UnixDate UnixTimeStamp
+ Recordset Info:RecordCount PO_RecordSet + NextRecordSet
+ Field Info:FieldCount FetchField + MetaType
+ Cleanup: Close
+ Deprecated: GetRowAssoc Fields +rs2html example
+
+ Differences between ADOdb and ADO
+ Database Driver Guide
+ Change Log
+Introduction
+PHP's database access functions are not standardised. This creates a need for + a database class library to hide the differences between the different database + API's (encapsulate the differences) so we can easily switch databases. PHP 4.0.5 or later + is now required (because we use array-based str_replace).
+We currently support MySQL, Oracle, Microsoft SQL Server, Sybase, Sybase SQL Anywhere, Informix, + PostgreSQL, FrontBase, Interbase (Firebird and Borland variants), Foxpro, Access, ADO and ODBC. We have had successful reports of connecting + to Progress and DB2 via ODBC. We hope more people + will contribute drivers to support other databases.
+PHP4 supports session variables. You can store your session information using + ADOdb for true portability and scalability. See adodb-session.php for more information.
+Also read http://php.weblogs.com/portable_sql + (also available as tips_portable_sql.htm in the release) for tips on writing + portable SQL.
+Unique Features of ADOdb
++
+- Easy for Windows programmers to adapt to because many of the conventions + are similar to Microsoft's ADO.
+- Unlike other PHP database classes which focus only on select statements, + we provide support code to handle inserts and updates which can be adapted + to multiple databases quickly. Methods are provided for date handling, + string concatenation and string quoting characters for differing databases.
+- A metatype system is built in so that we can figure out that types + such as CHAR, TEXT and STRING are equivalent in different databases.
+- Easy to port because all the database dependant code are stored in + stub functions. You do not need to port the core logic of the classes.
+- PHP4 session support. See adodb-session.php.
+How People are using ADOdb
+Here are some examples of how people are using ADOdb (for a much longer list, +visit http://php.weblogs.com/adodb-cool-applications): ++
- PhpLens is a commercial data grid component that allows both cool Web designers and serious unshaved programmers to develop and maintain databases on the Web easily. Developed by the author of ADOdb.
+ +
- PHAkt: PHP Extension for DreamWeaver Ultradev allows you to script PHP in the popular Web page editor. Database handling provided by ADOdb.
+ +
- Analysis Console for Intrusion Databases (ACID): PHP-based analysis engine to search and process a database of security incidents generated by security-related software such as IDSes and firewalls (e.g. Snort, ipchains). By Roman Danyliw.
+ +
- PostNuke is a very popular free content management + system and weblog system. It offers full CSS support, HTML 4.01 transitional compliance throughout, an advanced blocks system, and is fully multi-lingual enabled.
+ +
- EasyPublish CMS is another free content management system for managing information and integrated modules on your internet, intranet- and extranet-sites. From Norway.
+ +
- NOLA is a full featured accounting, inventory, and job tracking application. It is licensed under the GPL, and developed by Noguska. +
+ +
Feature Requests and Bug Reports
+Feature requests and bug reports can be emailed to jlim#natsoft.com.my + or posted to the ADOdb Help forums at http://phplens.com/lens/lensforum/topics.php?id=4.
+Installation Guide
+Make sure you are running PHP 4.0.4 or later. + Unpack all the files into a directory accessible by your webserver.
+To test, try modifying some of the tutorial examples. Make sure you customize + the connection settings correctly. You can debug using:
+<?php + include('adodb/adodb.inc.php'); + $db = ADONewConnection($dbdriver); # eg 'mysql' or 'postgres' + $db->debug = true; + $db->Connect($server, $user, $password, $database); + $rs = $db->Execute('select * from some_small_table'); + print "<pre>"; + print_r($rs->GetRows()); + print "</pre>"; +?>+Code Initialization
+When running ADOdb, at least two files are loaded. First is adodb/adodb.inc.php, + which contains all functions used by all database classes. The code specific + to a particular database is in the adodb/driver/adodb-????.inc.php file.
+For example, to connect to a mysql database:
++include('/path/to/set/here/adodb.inc.php'); +$conn = &ADONewConnection('mysql'); ++Whenever you need to connect to a database, you create a Connection object + using the ADONewConnection($driver) function. + NewADOConnection($driver) is an alternative name for the same function.
+At this point, you are not connected to the database. +You will use
+$conn->Connect()
or +$conn->PConnect()
to perform the actual connection.See the examples below in the Tutorial.
+ +Databases Supported
++
+ ++ +Name +Tested +Database +RecordCount() usable +Prerequisites +Operating Systems ++ +access +B +Microsoft Access/Jet. You need to create an ODBC DSN. +Y/N +ODBC +Windows only ++ +ado +B ++ Generic ADO, not tuned for specific databases. Allows + DSN-less connections. For best performance, use an OLEDB provider. + This is the base class for all ado drivers.
+You can set $db->codePage before connecting.
? depends on database +ADO or OLEDB provider +Windows only ++ +ado_access +B +Microsoft Access/Jet using ADO. Allows DSN-less connections. + For best performance, use an OLEDB provider. +Y/N +ADO or OLEDB provider +Windows only ++ +ado_mssql +B +Microsoft SQL Server using ADO. Allows DSN-less connections. + For best performance, use an OLEDB provider. +Y/N +ADO or OLEDB provider +Windows only ++ +db2 +A +DB2. Should work reliably as based on ODBC + driver. +Y/N +DB2 CLI/ODBC interface ++ +Unix and Windows. Unix + install hints.
++ +vfp +A +Microsoft Visual FoxPro. You need to create an ODBC DSN. +Y/N +ODBC +Windows only ++ +fbsql +C +FrontBase. +Y +? ++ +Unix and Windows
++ +ibase +B +Interbase 6 or earlier. Some users report you might need + to use this +
+ $db->PConnect('localhost:c:/ibase/employee.gdb', "sysdba", "masterkey") + to connect. Lacks Affected_Rows currently.
+
+ You can set $db->dialect, $db->buffers and $db->charSet before + connecting.Y/N +Interbase client +Unix and Windows ++ +firebird +C +Firebird version of interbase. +Y/N +Interbase client +Unix and Windows ++ +borland_ibase +C +Borland version of Interbase 6.5 or later. Very sad that + the forks differ. +Y/N +Interbase client +Unix and Windows ++ +informix72 +C +Informix databases before Informix 7.3 that do no support + SELECT FIRST. +Y/N +Informix client +Unix and Windows ++ +informix +C +Generic informix driver. +Y/N +Informix client +Unix and Windows ++ +mssql +A ++ +Microsoft SQL Server 7 and later. Works with Microsoft SQL Server + 2000 also. Note that date formating is problematic with this driver. For + example, the PHP mssql extension does not return the seconds for datetime!
+Y/N +Mssql client ++ +Unix and Windows.
+
+ Unix install howto and + another one. ++ +mssqlpo +A ++ +Portable mssql driver. Identical to above mssql driver, + except that '||', the concatenation operator, is converted to '+'. Useful + for porting scripts from most other sql variants that use ||.
+Y/N +Mssql client ++ +Unix and Windows.
+
+ Unix install howto.+ +mysql +A +MySQL without transaction support. You can also set + $db->clientFlags before connecting. +Y/N +MySQL client +Unix and Windows ++ +mysqlt or maxsql +A ++ +MySQL with transaction support. We recommend using || + as the concat operator for best portability. This can be done by running + MySQL using:
+
+ mysqld --ansi or mysqld --sql-mode=PIPES_AS_CONCATY/N +MySQL client +Unix and Windows ++ +oci8 +A +Oracle 8/9. Has more functionality than oracle driver + (eg. Affected_Rows). You might have to putenv('ORACLE_HOME=...') before + Connect/PConnect. + +There are 2 ways of connecting - with server IP + and service name:
+ PConnect('serverip:1521','scott','tiger','service')
+ or using an entry in TNSNAMES.ORA or ONAMES or HOSTNAMES:
+ PConnect(false, 'scott', 'tiger', $oraname). +Since 2.31, we support Oracle REF cursor variables directly + (see ExecuteCursor). +
Y/N +Oracle client +Unix and Windows ++ +oci805 +C +Supports reduced Oracle functionality for Oracle 8.0.5. + SelectLimit is not as efficient as in the oci8 or oci8po drivers. +Y/N +Oracle client +Unix and Windows ++ +oci8po +A +Oracle 8/9 portable driver. This is nearly identical with + the oci8 driver except (a) bind variables in Prepare() use the ? convention, + instead of :bindvar, (b) field names use the more common PHP convention + of lowercase names. + +Use this driver if porting from other databases is important. + Otherwise the oci8 driver offers better performance. +
Y/N +Oracle client +Unix and Windows ++ +odbc +A +Generic ODBC, not tuned for specific databases. To connect, + use +
+ PConnect('DSN','user','pwd'). This is the base class for all odbc + derived drivers.? depends on database +ODBC +Unix and Windows. Unix + hints. ++ +odbc_mssql +C +Uses ODBC to connect to MSSQL +Y/N +ODBC +Unix and Windows. ++ +odbc_oracle +C +Uses ODBC to connect to Oracle +Y/N +ODBC +Unix and Windows. ++ +oracle +C +Implements old Oracle 7 client API. Use oci8 + driver if possible for better performance. +Y/N +Oracle client +Unix and Windows ++ +postgres +A +Generic PostgreSQL driver. Currently identical to postgres7 + driver. +Y +PostgreSQL client +Unix and Windows. ++ +postgres64 +A +For PostgreSQL 6.4 and earlier which does not support LIMIT + internally. +Y +PostgreSQL client +Unix and Windows. ++ +postgres7 +A +PostgreSQL which supports LIMIT and other version 7 functionality. +Y +PostgreSQL client +Unix and Windows. ++ +sqlanywhere +C +Sybase SQL Anywhere. Should work reliably as based on ODBC + driver. +Y/N +SQL Anywhere ODBC client ++ +?
++ +sybase +C +Sybase. +Y/N +Sybase client ++ +Unix and Windows.
++
The "Tested" column indicates how extensively the code has been tested + and used.
+
+ A = well tested and used by many people
+ B = tested and usable, but some features might not be implemented
+ C = user contributed or experimental driver. Might not fully support all of + the latest features of ADOdb.The column "RecordCount() usable" indicates whether RecordCount() + return the number of rows, or returns -1 when a SELECT statement is executed. + If this column displays Y/N then the RecordCount() is emulated when the global + variable $ADODB_COUNTRECS=true (this is the default). Note that for large + recordsets, it might be better to disable RecordCount() emulation because + substantial amounts of memory are required to cache the recordset for counting. Also + there is a speed penalty of 40-50% if emulation is required. This is emulated in + most databases except for PostgreSQL and MySQL. + This variable is checked every time a query is executed, so you can selectively + choose which recordsets to count.
++ +
+Tutorial
+Example 1: Select Statement
+Task: Connect to the Access Northwind DSN, display the first 2 columns + of each row.
+In this example, we create a ADOConnection object, which represents the connection + to the database. The connection is initiated with PConnect, + which is a persistent connection. Whenever we want to query the database, we + call the ADOConnection.Execute() + function. This returns an ADORecordSet object which is actually a cursor that + holds the current row in the array fields[]. + We use MoveNext() + to move from row to row.
+NB: A useful function that is not used in this example is +SelectLimit, which +allows us to limit the number of rows shown. +
+<? +include('adodb.inc.php'); # load code common to ADOdb +$conn = &ADONewConnection('access'); # create a connection +$conn->PConnect('northwind'); # connect to MS-Access, northwind DSN +$recordSet = &$conn->Execute('select * from products'); +if (!$recordSet) + print $conn->ErrorMsg(); +else +while (!$recordSet->EOF) { + print $recordSet->fields[0].' '.$recordSet->fields[1].'<BR>'; + $recordSet->MoveNext(); +} + +$recordSet->Close(); # optional +$conn->Close(); # optional + +?> ++The $recordSet returned stores + the current row in the $recordSet->fields + array, indexed by column number (starting from zero). We use the MoveNext() + function to move to the next row. The EOF + property is set to true when end-of-file is reached. If an error occurs in Execute(), + we return false instead of a recordset.
+The
$recordSet->fields[]
array is generated by the PHP database + extension. Some database extensions only index by number and do not index + the array by field name. To force indexing by name - that is associative arrays + - use the SetFetchMode function. Each recordset saves and uses whatever fetch + mode was set when the recordset was created in Execute() or SelectLimit(). ++ $db->SetFetchMode(ADODB_FETCH_NUM); + $rs1 = $db->Execute('select * from table'); + $db->SetFetchMode(ADODB_FETCH_ASSOC); + $rs2 = $db->Execute('select * from table'); + print_r($rs1->fields); # shows array([0]=>'v0',[1] =>'v1') + print_r($rs2->fields); # shows array(['col1']=>'v0',['col2'] =>'v1') +++
+To get the number of rows in the select statement, you can use $recordSet->RecordCount(). + Note that it can return -1 if the number of rows returned cannot be determined.
+Example 2: Advanced Select with Field Objects
+Select a table, display the first two columns. If the second column is a date or timestamp, reformat the date to US format.
++<? +include('adodb.inc.php'); # load code common to ADOdb +$conn = &ADONewConnection('access'); # create a connection +$conn->PConnect('northwind'); # connect to MS-Access, northwind dsn +$recordSet = &$conn->Execute('select CustomerID,OrderDate from Orders'); +if (!$recordSet) + print $conn->ErrorMsg(); +else +while (!$recordSet->EOF) { + $fld = $recordSet->FetchField(1); + $type = $recordSet->MetaType($fld->type); + + if ( $type == 'D' || $type == 'T') + print $recordSet->fields[0].' '. + $recordSet->UserDate($recordSet->fields[1],'m/d/Y').'<BR>'; + else + print $recordSet->fields[0].' '.$recordSet->fields[1].'<BR>'; + + $recordSet->MoveNext(); +} +$recordSet->Close(); # optional +$conn->Close(); # optional + +?> ++In this example, we check the field type of the second column using FetchField(). + This returns an object with at least 3 fields.
++
+- name: name of column
+- type: native field type of column
+- max_length: maximum length of field. Some databases such as MySQL + do not return the maximum length of the field correctly. In these cases max_length + will be set to -1.
+We then use MetaType() + to translate the native type to a generic type. Currently the following + generic types are defined:
++
+- C: character fields that should be shown in a <input type="text"> + tag.
+- X: TeXt, large text fields that should be shown in a <textarea>
+- B: Blobs, or Binary Large Objects. Typically images. +
- D: Date field
+- T: Timestamp field
+- L: Logical field (boolean or bit-field)
+- I: Integer field
+- N: Numeric field. Includes autoincrement, numeric, floating point, + real and integer.
+- R: Serial field. Includes serial, autoincrement integers. This works + for selected databases.
+If the metatype is of type date or timestamp, then we print it using the user + defined date format with UserDate(), + which converts the PHP SQL date string format to a user defined one. Another + use for MetaType() + is data validation before doing an SQL insert or update.
+Example 3: Inserting
+Insert a row to the Orders table containing dates and strings that need to be quoted before they can be accepted by the database, eg: the single-quote in the word John's.
++<? +include('adodb.inc.php'); # load code common to ADOdb +$conn = &ADONewConnection('access'); # create a connection + +$conn->PConnect('northwind'); # connect to MS-Access, northwind dsn +$shipto = $conn->qstr("John's Old Shoppe"); + +$sql = "insert into orders (customerID,EmployeeID,OrderDate,ShipName) "; +$sql .= "values ('ANATR',2,".$conn->DBDate(time()).",$shipto)"; + +if ($conn->Execute($sql) === false) { + print 'error inserting: '.$conn->ErrorMsg().'<BR>'; +} +?> ++In this example, we see the advanced date and quote handling facilities of + ADOdb. The unix timestamp (which is a long integer) is appropriately formated + for Access with DBDate(), + and the right escape character is used for quoting the John's Old Shoppe, + which is John''s Old Shoppe and not PHP's default John's + Old Shoppe with qstr(). +
+Observe the error-handling of the Execute statement. False is returned by + Execute() if an error occured. The error message + for the last error that occurred is displayed in ErrorMsg(). + Note: php_track_errors might have to be enabled for error messages to + be saved.
+Example 4: Debugging
+<? +include('adodb.inc.php'); # load code common to ADOdb +$conn = &ADONewConnection('access'); # create a connection +$conn->PConnect('northwind'); # connect to MS-Access, northwind dsn +$shipto = $conn->qstr("John's Old Shoppe"); +$sql = "insert into orders (customerID,EmployeeID,OrderDate,ShipName) "; +$sql .= "values ('ANATR',2,".$conn->FormatDate(time()).",$shipto)"; +$conn->debug = true; +if ($conn->Execute($sql) === false) print 'error inserting'; +?> ++In the above example, we have turned on debugging by setting debug = true. + This will display the SQL statement before execution, and also show any error + messages. There is no need to call ErrorMsg() + in this case. For displaying the recordset, see the rs2html() + example.
+Also see the section on Custom Error Handlers.
+Example 5: MySQL and Menus
+Connect to MySQL database agora, and generate a <select> menu + from an SQL statement where the <option> captions are in the 1st column, + and the value to send back to the server is in the 2nd column.
+<? +include('adodb.inc.php'); # load code common to ADOdb +$conn = &ADONewConnection('mysql'); # create a connection +$conn->PConnect('localhost','userid','','agora');# connect to MySQL, agora db +$sql = 'select CustomerName, CustomerID from customers'; +$rs = $conn->Execute($sql); +print $rs->GetMenu('GetCust','Mary Rosli'); +?>+Here we define a menu named GetCust, with the menu option 'Mary Rosli' selected. + See GetMenu(). + We also have functions that return the recordset as an array: GetArray(), + and as an associative array with the key being the first column: GetAssoc().
+Example 6: Connecting to 2 Databases At Once
+<? +include('adodb.inc.php'); # load code common to ADOdb +$conn1 = &ADONewConnection('mysql'); # create a mysql connection +$conn2 = &ADONewConnection('oracle'); # create a oracle connection + +$conn1->PConnect($server, $userid, $password, $database); +$conn2->PConnect(false, $ora_userid, $ora_pwd, $oraname); + +$conn1->Execute('insert ...'); +$conn2->Execute('update ...'); +?>++ +
Example 7: Generating Update and Insert SQL
+ADOdb 1.31 and later supports two new recordset functions: GetUpdateSQL( ) and +GetInsertSQL( ). This allow you to perform a "SELECT * FROM table query WHERE...", +make a copy of the $rs->fields, modify the fields, and then generate the SQL to +update or insert into the table automatically. ++We show how the functions can be used when +accessing a table with the following fields: (ID, FirstName, LastName, Created). +
+Before these functions can be called, you need to initialize the recordset by +performing a select on the table. Idea and code by Jonathan Younger jyounger#unilab.com. +
+
<? +#============================================== +# SAMPLE GetUpdateSQL() and GetInsertSQL() code +#============================================== +include('adodb.inc.php'); +include('tohtml.inc.php'); + +#========================== +# This code tests an insert + +$sql = "SELECT * FROM ADOXYZ WHERE id = -1"; +# Select an empty record from the database + +$conn = &ADONewConnection("mysql"); # create a connection +$conn->debug=1; +$conn->PConnect("localhost", "admin", "", "test"); # connect to MySQL, testdb +$rs = $conn->Execute($sql); # Execute the query and get the empty recordset + +$record = array(); # Initialize an array to hold the record data to insert + +# Set the values for the fields in the record +# Note that field names are case-insensitive +$record["firstname"] = "Bob"; +$record["lastNamE"] = "Smith"; +$record["creaTed"] = time(); + +# Pass the empty recordset and the array containing the data to insert +# into the GetInsertSQL function. The function will process the data and return +# a fully formatted insert sql statement. +$insertSQL = $conn->GetInsertSQL($rs, $record); + +$conn->Execute($insertSQL); # Insert the record into the database + +#========================== +# This code tests an update + +$sql = "SELECT * FROM ADOXYZ WHERE id = 1"; +# Select a record to update + +$rs = $conn->Execute($sql); # Execute the query and get the existing record to update + +$record = array(); # Initialize an array to hold the record data to update + +# Set the values for the fields in the record +# Note that field names are case-insensitive +$record["firstname"] = "Caroline"; +$record["LasTnAme"] = "Smith"; # Update Caroline's lastname from Miranda to Smith + +# Pass the single record recordset and the array containing the data to update +# into the GetUpdateSQL function. The function will process the data and return +# a fully formatted update sql statement with the correct WHERE clause. +# If the data has not changed, no recordset is returned +$updateSQL = $conn->GetUpdateSQL($rs, $record); + +$conn->Execute($updateSQL); # Update the record in the database +$conn->Close(); +?> ++Example 8: Implementing Scrolling with Next and Previous
+The following code creates a very simple recordset pager, where you can scroll + from page to page of a recordset.
+ ++include_once('../adodb.inc.php'); +include_once('../adodb-pager.inc.php'); +session_start(); + +$db = NewADOConnection('mysql'); + +$db->Connect('localhost','root','','xphplens'); + +$sql = "select * from adoxyz "; + +$pager = new ADODB_Pager($db,$sql); +$pager->Render($rows_per_page=5);+This will create a basic record pager that looks like this: +
+
+
+ + |<
<<
+>>
>|
++ +
+ +ID First Name Last Name Date Created + ++ + +36 +Alan +Turing +Sat 06, Oct 2001 ++ + +37 +Serena +Williams +Sat 06, Oct 2001 ++ + +38 +Yat Sun +Sun +Sat 06, Oct 2001 ++ + +39 +Wai Hun +See +Sat 06, Oct 2001 ++ + +40 +Steven +Oey +Sat 06, Oct 2001 +Page 8/10 The number of rows to display at one time is controled by the Render($rows) + method. If you do not pass any value to Render(), ADODB_Pager will default to + 10 records per page. +
You can control the column titles by modifying your SQL (supported by most + databases): +
$sql = 'select id as "ID", firstname as "First Name", + lastname as "Last Name", created as "Date Created"+
from adoxyz';The above code can be found in the adodb/tests/testpaging.php example + included with this release, and the class ADODB_Pager in adodb/adodb-pager.inc.php. + The ADODB_Pager code can be adapted by a programmer so that the text links can + be replaced by images, and the dull white background be replaced with more interesting + colors. +
You can also allow display of html by setting $pager->htmlSpecialChars = false. +
Some of the code used here was contributed by Iván Oliva and Cornel + G.
+Example 9: Exporting in CSV or Tab-Delimited Format
+We provide some helper functions to export in comma-separated-value (CSV) and + tab-delimited formats:
+include_once('/path/to/adodb/toexport.inc.php');+
include_once('/path/to/adodb/adodb.inc.php');
+$db = &NewADOConnection('mysql');
$db->Connect($server, $userid, $password, $database);
$rs = $db->Execute('select fname as "First Name", surname as "Surname" from table'); + +print "<pre>";
print rs2csv($rs); # return a string, CSV formatprint '<hr>'; +
print '<hr>';
$rs->MoveFirst(); # note, some databases do not support MoveFirst
print rs2tab($rs,false); # return a string, tab-delimited + # false == suppress field names in first line
$rs->MoveFirst();
rs2tabout($rs); # send to stdout directly (there is also an rs2csvout function) +print "</pre>"; + +$rs->MoveFirst();
$fp = fopen($path, "w"); +if ($fp) {
rs2csvfile($rs, $fp); # write to file (there is also an rs2tabfile function) + fclose($fp);
} +Carriage-returns or newlines are converted to spaces. Field names are returned + in the first line of text. Strings containing the delimiter character are quoted + with double-quotes. Double-quotes are double-quoted again. This conforms to + Excel import and export guide-lines. +
All the above functions take as an optional last parameter, $addtitles which + defaults to true. When set to false field names in the first line + are suppressed.
+Example 10: Recordset Filters
+Sometimes we want to pre-process all rows in a recordset before we use it. For example, +we want to ucwords all text in recordset. +
+include_once('adodb/rsfilter.inc.php'); +include_once('adodb/adodb.inc.php'); + +// ucwords() every element in the recordset +function do_ucwords(&$arr,$rs) +{ + foreach($arr as $k => $v) { + $arr[$k] = ucwords($v); + } +} + +$db = NewADOConnection('mysql'); +$db->PConnect('server','user','pwd','db'); + +$rs = $db->Execute('select ... from table'); +$rs = RSFilter($rs,'do_ucwords'); ++The RSFilter function takes 2 parameters, the recordset, and the name + of the filter function. It returns the processed recordset scrolled + to the first record. The filter function takes two parameters, the + current row as an array, and the recordset object. For future compatibility, + you should not use the original recordset object.
+Example 11: Smart Transactions
+The old way of doing transactions required you to use ++$conn->BeginTrans(); +$ok = $conn->Execute($sql); +if ($ok) $ok = $conn->Execute($sql2); +if (!$ok) $conn->RollbackTrans(); +else $conn->CommitTrans(); ++This is very complicated for large projects because you have to track the error +status. Smart Transactions is much simpler. You start a smart transaction by calling StartTrans(): ++$conn->StartTrans(); +$conn->Execute($sql); +$conn->Execute($Sql2); +$conn->CompleteTrans(); ++ +CompleteTrans() detects when an SQL error occurs, and +will Rollback/Commit as appropriate. + +To specificly force a rollback even if no error occured, +use FailTrans(). Note that the rollback is done in +CompleteTrans(), and not in FailTrans(). + ++$conn->StartTrans(); +$conn->Execute($sql); +if (!CheckRecords()) $conn->FailTrans(); +$conn->Execute($Sql2); +$conn->CompleteTrans(); ++Lastly, StartTrans/CompleteTrans is nestable, and only the outermost block is +executed. In contrast, BeginTrans/CommitTrans/RollbackTrans is NOT nestable. ++$conn->StartTrans(); +$conn->Execute($sql); + $conn->StartTrans(); # ignored + if (!CheckRecords()) $conn->FailTrans(); + $conn->CompleteTrans(); # ignored +$conn->Execute($Sql2); +$conn->CompleteTrans(); ++Note: Savepoints +are currently not supported. +
Using Custom Error Handlers and PEAR_Error
+Apart from the old $con->debug = true; way of debugging, ADOdb 1.50 onwards provides +another way of handling errors using ADOdb's custom error handlers. ++ADOdb provides two custom handlers which you can modify for your needs. +The first one is in the adodb-errorhandler.inc.php file. This makes +use of the standard PHP functions error_reporting +to control what error messages types to display, +and trigger_error which invokes the default +PHP error handler. +
+Including the above file will cause trigger_error($errorstring,E_USER_ERROR) +to be called when
+(a) Connect() or PConnect() fails, or
+(b) a function that executes SQL statements such as Execute() or SelectLimit() has an error.
+(c) GenID() appears to go into an infinite loop. ++The $errorstring is generated by ADOdb and will contain useful debugging information similar +to the error.log data generated below. +This file adodb-errorhandler.inc.php should be included before you create any ADOConnection objects. +
+ If you define error_reporting(0), no errors will be shown. + If you set error_reporting(E_ALL), all errors will be displayed on the screen. +
+<?php +error_reporting(E_ALL); # show any error messages triggered +include('adodb-errorhandler.inc.php'); +include('adodb.inc.php'); +include('tohtml.inc.php'); +$c = NewADOConnection('mysql'); +$c->PConnect('localhost','root','','northwind'); +$rs=$c->Execute('select * from productsz'); #invalid table productsz'); +if ($rs) $rs2html($rs); +?> +++ If you want to log the error message, you can do so by defining the following optional + constants ADODB_ERROR_LOG_TYPE and ADODB_ERROR_LOG_DEST. ADODB_ERROR_LOG_TYPE is + the error log message type (see error_log + in the PHP manual). In this case we set + it to 3, which means log to the file defined by the constant ADODB_ERROR_LOG_DEST. + +
+<?php +error_reporting(0); # do not echo any errors +define('ADODB_ERROR_LOG_TYPE',3); +define('ADODB_ERROR_LOG_DEST','C:/errors.log'); +include('adodb-errorhandler.inc.php'); +include('adodb.inc.php'); +include('tohtml.inc.php'); + +$c = NewADOConnection('mysql'); +$c->PConnect('localhost','root','','northwind'); +$rs=$c->Execute('select * from productsz'); ## invalid table productsz +if ($rs) $rs2html($rs); +?> ++The following message will be logged in the error.log file: ++(2001-10-28 14:20:38) mysql error: [1146: Table 'northwind.productsz' doesn't exist] in + EXECUTE("select * from productsz") ++The second error handler is adodb-errorpear.inc.php. This will create a +PEAR_Error derived object whenever an error occurs. The last PEAR_Error object +created can be retrieved using ADODB_Pear_Error(). ++<?php +include('adodb-errorpear.inc.php'); +include('adodb.inc.php'); +include('tohtml.inc.php'); +$c = NewADOConnection('mysql'); +$c->PConnect('localhost','root','','northwind'); +$rs=$c->Execute('select * from productsz'); #invalid table productsz'); +if ($rs) $rs2html($rs); +else { + $e = ADODB_Pear_Error(); + echo '<p>',$e->message,'</p>'; +} +?> +++You can use a PEAR_Error derived class by defining the constant ADODB_PEAR_ERROR_CLASS +before the adodb-errorpear.inc.php file is included. For easy debugging, you can +set the default error handler in the beginning of the PHP script to PEAR_ERROR_DIE, +which will cause an error message to be printed, then halt script execution: +
+include('PEAR.php'); +PEAR::setErrorHandling('PEAR_ERROR_DIE'); ++Note that we do not explicitly return a PEAR_Error object to you when an error + occurs. We return false instead. You have to call ADODB_Pear_Error() to get + the last error or use the PEAR_ERROR_DIE technique. +
Error Messages
+Error messages are outputted using the static method ADOConnnection::outp($msg,$newline=true). + By default, it sends the messages to the client. You can override this to + perform error-logging. +
Data Source Names
+We now support connecting using PEAR style DSN's. A DSN is a connection string + of the form:
+$dsn = "$driver://$username:$password@$hostname/$databasename";
+You pass the DSN to the static class function DB::Connect. An example:
+include_once('../adodb/adodb-pear.inc.php'); + $username = 'root'; + $password = ''; + $hostname = 'localhost'; + $databasename = 'xphplens'; + $driver = 'mysql'; + $dsn = "$driver://$username:$password@$hostname/$databasename";+$db = DB::Connect($dsn);+
$rs = $db->Execute('select firstname,lastname from adoxyz'); + $cnt = 0; + while ($arr = $rs->FetchRow()) { + print_r($arr); print "<br>"; + }This requires PEAR to be installed and in the default include path in php.ini.
+Caching of Recordsets
+ADOdb now supports caching of recordsets using the CacheExecute( ), +CachePageExecute( ) and CacheSelectLimit( ) functions. There are similar to the +non-cache functions, except that they take a new first parameter, $secs2cache. +
An example: +
+include('adodb.inc.php'); # load code common to ADOdb +$ADODB_CACHE_DIR = '/usr/ADODB_cache'; +$conn = &ADONewConnection('mysql'); # create a connection +$conn->PConnect('localhost','userid','','agora');# connect to MySQL, agora db +$sql = 'select CustomerName, CustomerID from customers'; +$rs = $conn->CacheExecute(15,$sql);+The first parameter is the number of seconds to cache + the query. Subsequent calls to that query will used the cached version stored + in $ADODB_CACHE_DIR. To force a query to execute and flush the cache, call CacheExecute() + with the first parameter set to zero. Alternatively, use the CacheFlush($sql) + call.
+For the sake of security, we recommend you set register_globals=off + in php.ini if you are using $ADODB_CACHE_DIR.
+In ADOdb 1.80 onwards, the secs2cache parameter is optional in CacheSelectLimit() and +CacheExecute(). If you leave it out, it will use the $connection->cacheSecs parameter, which defaults +to 60 minutes. +
+ $conn->Connect(...); + $conn->cacheSecs = 3600*24; # cache 24 hours + $rs = $conn->CacheExecute('select * from table'); ++Please note that magic_quotes_runtime should be turned off. More info. + +
Pivot Tables
+ +Since ADOdb 2.30, we support the generation of SQL to + create pivot tables, also known as cross-tabulations. For further explanation + read this DevShed Cross-Tabulation + tutorial. We assume that your database supports the SQL case-when expression. +
+ +In this example, we will use the Northwind database from Microsoft. In the + database, we have a products table, and we want to analyze this table by suppliers + versus product categories. We will place the suppliers on each row, and + pivot on categories. So from the table on the left, we generate the pivot-table on the right:
+ ++
+ ++ ++ ++
++ +Supplier +Category ++ +supplier1 +category1 ++ +supplier2 +category1 ++ +supplier2 +category2 +--> ++ ++
++ ++ category1 +category2 +total ++ +supplier1 +1 +0 +1 ++ +supplier2 +1 +1 +2 +The following code will generate the SQL for a cross-tabulation: +
+# Query the main "product" table +# Set the rows to CompanyName +# and the columns to the values of Categories +# and define the joins to link to lookup tables +# "categories" and "suppliers" +# + include "adodb/pivottable.php"; + $sql = PivotTableSQL( + $gDB, # adodb connection + 'products p ,categories c ,suppliers s', # tables + 'CompanyName', # rows (multiple fields allowed) + 'CategoryName', # column to pivot on + 'p.CategoryID = c.CategoryID and s.SupplierID= p.SupplierID' # joins/where +); ++ +This will generate the following SQL:
++
SELECT CompanyName,
+ SUM(CASE WHEN CategoryName='Beverages' THEN 1 ELSE 0 END) AS "Beverages", +
+ SUM(CASE WHEN CategoryName='Condiments' THEN 1 ELSE 0 END) AS "Condiments", +
+ SUM(CASE WHEN CategoryName='Confections' THEN 1 ELSE 0 END) AS "Confections", +
+ SUM(CASE WHEN CategoryName='Dairy Products' THEN 1 ELSE 0 END) AS "Dairy + Products",
+ SUM(CASE WHEN CategoryName='Grains/Cereals' THEN 1 ELSE 0 END) AS "Grains/Cereals", +
+ SUM(CASE WHEN CategoryName='Meat/Poultry' THEN 1 ELSE 0 END) AS "Meat/Poultry", +
+ SUM(CASE WHEN CategoryName='Produce' THEN 1 ELSE 0 END) AS "Produce", +
+ SUM(CASE WHEN CategoryName='Seafood' THEN 1 ELSE 0 END) AS "Seafood", +
+ SUM(1) as Total
+ FROM products p ,categories c ,suppliers s WHERE p.CategoryID = c.CategoryID + and s.SupplierID= p.SupplierID
+ GROUP BY CompanyNameYou can also pivot on numerical columns and generate totals + by using ranges. This code was revised in ADODB 2.41 + and is not backward compatible. The second example shows this:
++ $sql = PivotTableSQL( + $gDB, # adodb connection + 'products p ,categories c ,suppliers s', # tables + 'CompanyName', # rows (multiple fields allowed) + array( # column ranges + ' 0 ' => 'UnitsInStock <= 0', + "1 to 5" => '0 < UnitsInStock and UnitsInStock <= 5', + "6 to 10" => '5 < UnitsInStock and UnitsInStock <= 10', + "11 to 15" => '10 < UnitsInStock and UnitsInStock <= 15', + "16+" => '15 < UnitsInStock' + ), + ' p.CategoryID = c.CategoryID and s.SupplierID= p.SupplierID', # joins/where + 'UnitsInStock', # sum this field + 'Sum ' # sum label prefix +); ++Which generates:
++
SELECT CompanyName,
+ SUM(CASE WHEN UnitsInStock <= 0 THEN UnitsInStock ELSE 0 END) AS "Sum + 0 ",
+ SUM(CASE WHEN 0 < UnitsInStock and UnitsInStock <= 5 THEN UnitsInStock + ELSE 0 END) AS "Sum 1 to 5",
+ SUM(CASE WHEN 5 < UnitsInStock and UnitsInStock <= 10 THEN UnitsInStock + ELSE 0 END) AS "Sum 6 to 10",
+ SUM(CASE WHEN 10 < UnitsInStock and UnitsInStock <= 15 THEN UnitsInStock + ELSE 0 END) AS "Sum 11 to 15",
+ SUM(CASE WHEN 15 < UnitsInStock THEN UnitsInStock ELSE 0 END) AS "Sum + 16+",
+ SUM(UnitsInStock) AS "Sum UnitsInStock",
+ SUM(1) as Total,
+ FROM products p ,categories c ,suppliers s WHERE p.CategoryID = c.CategoryID + and s.SupplierID= p.SupplierID
+ GROUP BY CompanyName
+
+Class Reference
+Function parameters with [ ] around them are optional.
+ +Global Variables
+$ADODB_COUNTRECS
+If the database driver API does not support counting the number of records + returned in a SELECT statement, the function RecordCount() is emulated when + the global variable $ADODB_COUNTRECS is set to true, which is the default. + We emulate this by buffering the records, which can take up large amounts + of memory for big recordsets. Set this variable to false for the best performance. + This variable is checked every time a query is executed, so you can selectively + choose which recordsets to count.
+$ADODB_CACHE_DIR
+ +If you are using recordset caching, this is the directory to save your recordsets + in. Define this before you call any caching functions such as CacheExecute( + ). We recommend setting register_globals=off in php.ini if you use this + feature for security reasons.
+If you are using Unix and apache, you might need to set your cache directory + permissions to something similar to the following:
+ +chown -R apache /path/to/adodb/cache
+ +
+ chgrp -R apache /path/to/adodb/cache$ADODB_LANG
+Determines the language used in MetaErrorMsg(). The default is 'en', for English. +To find out what languages are supported, see the files +in adodb/lang/adodb-$lang.inc.php, where $lang is the supported langauge. +
$ADODB_FETCH_MODE
+This is a global variable that determines how arrays are retrieved by recordsets. + The recordset saves this value on creation (eg. in Execute( ) or SelectLimit( + )), and any subsequent changes to $ADODB_FETCH_MODE have no affect on existing + recordsets, only on recordsets created in the future.
+The following constants are defined:
+ +define('ADODB_FETCH_DEFAULT',0);
+ +
+ define('ADODB_FETCH_NUM',1);
+ define('ADODB_FETCH_ASSOC',2);
+ define('ADODB_FETCH_BOTH',3);An example: +
+ $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + $rs1 = $db->Execute('select * from table'); + $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; + $rs2 = $db->Execute('select * from table'); + print_r($rs1->fields); # shows array([0]=>'v0',[1] =>'v1') + print_r($rs2->fields); # shows array(['col1']=>'v0',['col2'] =>'v1') ++As you can see in the above example, both recordsets store and use different + fetch modes based on the $ADODB_FETCH_MODE setting when the recordset was + created by Execute().
+If no fetch mode is predefined, the fetch mode defaults to ADODB_FETCH_DEFAULT. + The behaviour of this default mode varies from driver to driver, so do not + rely on ADODB_FETCH_DEFAULT. For portability, we recommend sticking to ADODB_FETCH_NUM + or ADODB_FETCH_ASSOC. Many drivers do not support ADODB_FETCH_BOTH.
+SetFetchMode Function
+Some programmers prefer to use a more object-oriented solution, where the fetch + mode is set by a object function, SetFetchMode. + Once this function is called for a connection object, that connection object + will ignore the global variable $ADODB_FETCH_MODE and will use the internal + fetchMode property exclusively.
++ $db->SetFetchMode(ADODB_FETCH_NUM); + $rs1 = $db->Execute('select * from table'); + $db->SetFetchMode(ADODB_FETCH_ASSOC); + $rs2 = $db->Execute('select * from table'); + print_r($rs1->fields); # shows array([0]=>'v0',[1] =>'v1') + print_r($rs2->fields); # shows array(['col1']=>'v0',['col2'] =>'v1')+To retrieve the previous fetch mode, you can use check the $db->fetchMode + property, or use the return value of SetFetchMode( ). +
+You can control the associative fetch case for certain drivers which behave + differently. For the sybase, oci8po, mssql, odbc and ibase drivers and all + drivers derived from them, ADODB_ASSOC_CASE will by default generate recordsets + where the field name keys are lower-cased. Use the constant ADODB_ASSOC_CASE + to change the case of the keys. There are 3 possible values:
+0 = assoc lowercase field names. $rs->fields['orderid']
+
+ 1 = assoc uppercase field names. $rs->fields['ORDERID']
+ 2 = use native-case field names. $rs->fields['OrderID'] -- this is the + default since ADOdb 2.90To use it, declare it before you incldue adodb.inc.php.
+define('ADODB_ASSOC_CASE', 2); # use native-case for ADODB_FETCH_ASSOC
+
+ include('adodb.inc.php');
+ADOConnection
+Object that performs the connection to the database, executes SQL statements + and has a set of utility functions for standardising the format of SQL statements + for issues such as concatenation and date formats.
+ADOConnection Fields
+databaseType: Name of the database system we are connecting to. Eg. + odbc or mssql or mysql.
+dataProvider: The underlying mechanism used to connect to the database. + Normally set to native, unless using odbc or ado.
+host: Name of server or data source name (DSN) to connect to.
+database: Name of the database or to connect to. If ado is used, it + will hold the ado data provider.
+user: Login id to connect to database. Password is not saved for security + reasons.
+raiseErrorFn: Allows you to define an error handling function. See adodb-errorhandler.inc.php + for an example.
+debug: Set to true to make debug statements to appear.
+concat_operator: Set to '+' or '||' normally. The operator used to concatenate + strings in SQL. Used by the Concat function.
+fmtDate: The format used by the DBDate + function to send dates to the database. is '#Y-m-d#' for Microsoft Access, + and ''Y-m-d'' for MySQL.
+fmtTimeStamp: The format used by the DBTimeStamp + function to send timestamps to the database.
+true: The value used to represent true.Eg. '.T.'. for Foxpro, '1' for + Microsoft SQL.
+false: The value used to represent false. Eg. '.F.'. for Foxpro, '0' + for Microsoft SQL.
+replaceQuote: The string used to escape quotes. Eg. double single-quotes + for Microsoft SQL, and backslash-quote for MySQL. Used by qstr.
+autoCommit: indicates whether automatic commit is enabled. Default is + true.
+charSet: set the default charset to use. Currently only interbase supports + this.
+dialect: set the default sql dialect to use. Currently only interbase + supports this.
+metaTablesSQL: SQL statement to return a list of available tables. Eg. + SHOW TABLES in MySQL.
+genID: The latest id generated by GenID() if supported by the database.
+cacheSecs: The number of seconds to cache recordsets if CacheExecute() + or CacheSelectLimit() omit the $secs2cache parameter. Defaults to 60 minutes.
+sysDate: String that holds the name of the database function to call + to get the current date. Useful for inserts and updates.
+sysTimeStamp: String that holds the name of the database function to + call to get the current timestamp/datetime value.
+leftOuter: String that holds operator for left outer join, if known. + Otherwise set to false.
+rightOuter: String that holds operator for left outer join, if known. + Otherwise set to false.
+ansiOuter: Boolean that if true indicates that ANSI style outer joins + are permitted. Eg. select * from table1 left join table2 on p1=p2.
+connectSID: Boolean that indicates whether to treat the $database parameter + in connects as the SID for the oci8 driver. Defaults to false. Useful for + Oracle 8.0.5 and earlier.
+autoRollback: Persistent connections are auto-rollbacked in PConnect( + ) if this is set to true. Default is false.
+
+ADOConnection Main Functions
+ADOConnection( )
+Constructor function. Do not call this directly. Use ADONewConnection( ) instead.
+Connect($host,[$user],[$password],[$database])
+Non-persistent connect to data source or server $host, using userid + $user and password $password. If the server supports multiple + databases, connect to database $database.
+Returns true/false depending on connection.
+ADO Note: If you are using a Microsoft ADO and not OLEDB, you can set the $database + parameter to the OLEDB data provider you are using.
+PostgreSQL: An alternative way of connecting to the database is to pass the + standard PostgreSQL connection string in the first parameter $host, and the + other parameters will be ignored.
+For Oracle and Oci8, there are two ways to connect. First is to use the TNS + name defined in your local tnsnames.ora (or ONAMES or HOSTNAMES). Place the + name in the $database field, and set the $host field to false. Alternatively, + set $host to the server, and $database to the database SID, this bypassed + tnsnames.ora. +
Examples: +
# $oraname in tnsnames.ora/ONAMES/HOSTNAMES + $conn->Connect(false, 'scott', 'tiger', $oraname); + $conn->Connect('server:1521', 'scott', 'tiger', 'ServiceName'); # bypass tnsnames.ora+There are many examples of connecting to a database at php.weblogs.com/ADOdb, + and in the testdatabases.inc.php file included in the release.
+PConnect($host,[$user],[$password],[$database])
+Persistent connect to data source or server $host, using userid $user + and password $password. If the server supports multiple databases, + connect to database $database.
+We now perform a rollback on persistent connection for selected databases since + 2.21, as advised in the PHP manual. See change log or source code for which + databases are affected. +
Returns true/false depending on connection. See Connect( ) above for more info.
+Since ADOdb 2.21, we also support autoRollback. If you set:
+ +$conn = &NewADOConnection('mysql'); + $conn->autoRollback = true; # default is false + $conn->PConnect(...); # rollback here+Then when doing a persistent connection with PConnect( ), ADOdb will + perform a rollback first. This is because it is documented that PHP is + not guaranteed to rollback existing failed transactions when + persistent connections are used. This is implemented in Oracle, + MySQL, PgSQL, MSSQL, ODBC currently. +
Since ADOdb 3.11, you can force non-persistent +connections even if PConnect is called by defining the constant +ADODB_NEVER_PERSIST before you call PConnect. +
NConnect($host,[$user],[$password],[$database])
+Always force a new connection. In contrast, PHP sometimes reuses connections + when you use Connect() or PConnect(). Currently works only on mysql (PHP 4.3.0 + or later) and oci8-derived drivers. For other drivers, NConnect() works like + Connect(). + + +
Execute SQL statement $sql and return derived class of ADORecordSet + if successful. Note that a record set is always returned on success, even + if we are executing an insert or update statement.
+Returns derived class of ADORecordSet. Eg. if connecting via mysql, then ADORecordSet_mysql + would be returned. False is returned if there was an error in executing the + sql.
+The $inputarr parameter can be used for binding variables to parameters. Below + is an Oracle example:
++ $conn->Execute("SELECT * FROM TABLE WHERE COND=:val", array('val'=> $val)); ++Another example, using ODBC,which uses the ? convention:
++ $conn->Execute("SELECT * FROM TABLE WHERE COND=?", array($val)); ++Binding variables
+Variable binding speeds the compilation and caching of SQL statements, leading +to higher performance. Currently Oracle and ODBC support variable binding. ODBC +style ? binding is emulated in databases that do not support binding. +Variable binding in the odbc and oci8po drivers. +
+$rs = $db->Execute('select * from table where val=?', array('10')); ++Variable binding in the oci8 driver. ++$rs = $db->Execute('select name from table where val=:key', + array('key' => 10)); ++CacheExecute([$secs2cache,]$sql,$inputarr=false)
+Similar to Execute, except that the recordset is cached for $secs2cache seconds + in the $ADODB_CACHE_DIR directory. If CacheExecute() is called again with + the same parameters, same database, same userid, same password, and the cached + recordset has not expired, the cached recordset is returned. +
+ include('adodb.inc.php'); + include('tohtml.inc.php'); + $ADODB_CACHE_DIR = '/usr/local/ADOdbcache'; + $conn = &ADONewConnection('mysql'); + $conn->PConnect('localhost','userid','password','database'); + $rs = $conn->CacheExecute(15, 'select * from table'); # cache 15 secs + rs2html($rs); /* recordset to html table */ ++ +Alternatively, since ADOdb 1.80, the $secs2cache parameter is optional:
+$conn->Connect(...); + $conn->cacheSecs = 3600*24; // cache 24 hours + $rs = $conn->CacheExecute('select * from table'); ++Note that the $secs2cache parameter is optional. If omitted, we use the value +in $connection->cacheSecs (default is 3600 seconds, or 1 hour). Use CacheExecute() +only with SELECT statements. +Performance note: I have done some benchmarks and found that they vary so greatly + that it's better to talk about when caching is of benefit. When your database + server is much slower than your Web server or the database is very + overloaded then ADOdb's caching is good because it reduces the load on + your database server. If your database server is lightly loaded or much faster + than your Web server, then caching could actually reduce performance.
+ExecuteCursor($sql,$cursorName='rs',$parameters=false)
+Execute an Oracle stored procedure, and returns an Oracle REF cursor variable as + a regular ADOdb recordset. Does not work with any other database except oci8. + Thanks to Robert Tuttle for the design. +
+ $db = ADONewConnection("oci8"); + $db->Connect("foo.com:1521", "uid", "pwd", "FOO"); + $rs = $db->ExecuteCursor("begin :cursorvar := getdata(:param1); end;", + 'cursorvar', + array('param1'=>10)); + # $rs is now just like any other ADOdb recordset object+
rs2html($rs);ExecuteCursor() is a helper function that does the following internally: +
+ $stmt = $db->Prepare("BEGIN :RS := SP_FOO(); END;"); + $db->Parameter($stmt, $cur, 'RS', false, -1, OCI_B_CURSOR); + $rs = $db->Execute($stmt);+SelectLimit($sql,$numrows=-1,$offset=-1,$inputarr=false)
+Returns a recordset if successful. Returns false otherwise. Performs a select + statement, simulating PostgreSQL's SELECT statement, LIMIT $numrows OFFSET + $offset clause.
+In PostgreSQL, SELECT * FROM TABLE LIMIT 3 will return the first 3 records + only. The equivalent is
+$connection->SelectLimit('SELECT * FROM TABLE',3)
. + This functionality is simulated for databases that do not possess this feature.And SELECT * FROM TABLE LIMIT 3 OFFSET 2 will return records 3, 4 and 5 (eg. + after record 2, return 3 rows). The equivalent in ADOdb is
+$connection->SelectLimit('SELECT + * FROM TABLE',3,2)
.Note that this is the opposite of MySQL's LIMIT clause. You can also + set
+$connection->SelectLimit('SELECT * FROM TABLE',-1,10)
to + get rows 11 to the last row.The last parameter $inputarr is for databases that support variable binding + such as Oracle oci8. This substantially reduces SQL compilation overhead. + Below is an Oracle example:
++ $conn->SelectLimit("SELECT * FROM TABLE WHERE COND=:val", 100,-1,array('val'=> $val)); ++The oci8po driver (oracle portable driver) uses the more standard bind variable + of ?: +
+ $conn->SelectLimit("SELECT * FROM TABLE WHERE COND=?", 100,-1,array('val'=> $val)); +++
Ron Wilson reports that SelectLimit does not work with UNIONs. +
CacheSelectLimit([$secs2cache,] $sql, $numrows=-1,$offset=-1,$inputarr=false)
+Similar to SelectLimit, except that the recordset returned is cached for $secs2cache + seconds in the $ADODB_CACHE_DIR directory.
+Since 1.80, $secs2cache has been optional, and you can define the caching time + in $connection->cacheSecs.
+ +$conn->Connect(...); + $conn->cacheSecs = 3600*24; // cache 24 hours + $rs = $conn->CacheSelectLimit('select * from table',10);+ +CacheFlush($sql=false,$inputarr=false)
+Flush (delete) any cached recordsets for the SQL statement $sql in $ADODB_CACHE_DIR. +
If no parameter is passed in, then all adodb_*.cache files are deleted. +
If you want to flush all cached recordsets manually, execute the following + PHP code (works only under Unix):
+
+system("rm -f `find ".$ADODB_CACHE_DIR." -name + adodb_*.cache`");
For general cleanup of all expired files, you should use crontab + on Unix, or at.exe on Windows, and a shell script similar to the following:
+ +
+ #------------------------------------------------------
+ # This particular example deletes files in the TMPPATH
+ # directory with the string ".cache" in their name that
+ # are more than 7 days old.
+ #------------------------------------------------------
+ AGED=7
+ find ${TMPPATH} -mtime +$AGED | grep "\.cache" | xargs rm -f
+Returns a virtualized error number, based on PEAR DB's error number system. You might +need to include adodb-error.inc.php before you call this function. The parameter $errno +is the native error number you want to convert. If you do not pass any parameter, MetaError +will call ErrorNo() for you and convert it. If the error number cannot be virtualized, MetaError +will return -1 (DB_ERROR).
+ + +Pass the error number returned by MetaError() for the equivalent textual error message.
+ +Returns the last status or error message. This can return a string even if + no error occurs. In general you do not need to call this function unless an + ADOdb function returns false on an error.
+Note: If debug is enabled, the SQL error message is always displayed + when the Execute function is called.
+ +Returns the last error number. Note that old versions of PHP (pre 4.0.6) do + not support error number for ODBC. In general you do not need to call this + function unless an ADOdb function returns false on an error.
+ + +Sets the current fetch mode for the connection and stores + it in $db->fetchMode. Legal modes are ADODB_FETCH_ASSOC and ADODB_FETCH_NUM. + For more info, see $ADODB_FETCH_MODE.
+Returns the previous fetch mode, which could be false + if SetFetchMode( ) has not been called before.
+ +CreateSequence($seqName = 'adodbseq',$startID=1)
+Create a sequence. The next time GenID( ) is called, the value returned will + be $startID. Added in 2.60. +
DropSequenceD($seqName = 'adodbseq')
+Delete a sequence. Added in 2.60. +
GenID($seqName = 'adodbseq',$startID=1)
+Generate a sequence number . Works for interbase, + mysql, postgresql, oci8, oci8po, mssql, ODBC based (access,vfp,db2,etc) drivers + currently. Uses $seqName as the name of the sequence. GenID() will automatically + create the sequence for you if it does not exist (provided the userid has + permission to do so). Otherwise you will have to create the sequence yourself. +
If your database driver emulates sequences, the name of the table is the sequence + name. The table has one column, "id" which should be of type integer, or if + you need something larger - numeric(16). +
For ODBC and databases that do not support sequences natively (eg mssql, mysql), + we create a table for each sequence. If the sequence has not been defined + earlier, it is created with the starting value set in $startID.
+Note that the mssql driver's GenID() before 1.90 used to generate 16 byte GUID's.
+UpdateBlob($table,$column,$val,$where)
+Allows you to store a blob (in $val) into $table into $column in a row at $where. +Usage: +
+
+ # for oracle + $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, empty_blob())'); + $conn->UpdateBlob('blobtable','blobcol',$blobvalue,'id=1'); + + # non oracle databases + $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); + $conn->UpdateBlob('blobtable','blobcol',$blobvalue,'id=1'); ++Returns true if succesful, false otherwise. Supported by MySQL, PostgreSQL, + Oci8, Oci8po and Interbase drivers. Other drivers might work, depending on + the state of development.
+Note that when an Interbase blob is retrieved using SELECT, it still needs + to be decoded using $connection->DecodeBlob($blob); to derive the original + value in versions of PHP before 4.1.0. +
For PostgreSQL, you can store your blob using blob oid's or as a bytea field. + You can use bytea fields but not blob oid's currently with UpdateBlob( ). + Conversely UpdateBlobFile( ) supports oid's, but not bytea data.
+
+ If you do not pass in an oid, then UpdateBlob() assumes that you are storing + in bytea fields. +UpdateClob($table,$column,$val,$where)
+Allows you to store a clob (in $val) into $table into $column in a row at $where. +Similar to UpdateBlob (see above), but for Character Large OBjects. +Usage: +
+ # for oracle + $conn->Execute('INSERT INTO clobtable (id, clobcol) VALUES (1, empty_clob())'); + $conn->UpdateBlob('clobtable','clobcol',$clobvalue,'id=1'); + + # non oracle databases + $conn->Execute('INSERT INTO clobtable (id, clobcol) VALUES (1, null)'); + $conn->UpdateBlob('clobtable','clobcol',$clobvalue,'id=1'); ++UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB')
+Similar to UpdateBlob, except that we pass in a file path to where the blob + resides. +
For PostgreSQL, if you are using blob oid's, use this interface. This interface + does not support bytea fields. +
Returns true if successful, false otherwise. +
Some databases require blob's to be encoded manually before upload. Note if + you use UpdateBlob( ) or UpdateBlobFile( ) the conversion is done automatically + for you and you do not have to call this function. For PostgreSQL, currently, + BlobEncode() can only be used for bytea fields. +
Returns the encoded blob value. +
Note that there is a connection property called blobEncodeType which + has 3 legal values: +
false - no need to perform encoding or decoding.
+ 'I' - blob encoding required, and returned encoded blob is a numeric value + (no need to quote).
+ 'C' - blob encoding required, and returned encoded blob is a character value + (requires quoting). +This is purely for documentation purposes, so that programs that accept multiple + database drivers know what is the right thing to do when processing blobs. +
Some databases require blob's to be decoded manually after doing a select statement. + If the database does not require decoding, then this function will return + the blob unchanged. Currently BlobDecode is only required for one database, + PostgreSQL, and only if you are using blob oid's (if you are using bytea fields, + we auto-decode for you).
+$rs = $db->Execute("select bloboid from postgres_table where id=$key"); +$blob = $db->BlobDecode( reset($rs->fields) );+Replace($table, $arrFields, $keyCols,$autoQuote=false)
+Try to update a record, and if the record is not found, an insert statement + is generated and executed. Returns 0 on failure, 1 if update statement worked, + 2 if no record was found and the insert was executed successfully. This differs + from MySQL's replace which deletes the record and inserts a new record. This + also means you cannot update the primary key. The only exception to this is + Interbase and its derivitives, which uses delete and insert because of some + Interbase API limitations. +
The parameters are $table which is the table name, the $keyCols which is an + associative array where the keys are the field names, and keyCols is the name + of the primary key, or an array of field names if it is a compound key. If + $autoQuote is set to true, then Replace() will quote all values that are non-numeric; + auto-quoting will not quote nulls. Note that auto-quoting will not work if + you use SQL functions or operators. +
Examples: +
+# single field primary key +$ret = $db->Replace('atable', + array('id'=>1000,'firstname'=>'Harun','lastname'=>'Al-Rashid'), + 'id', + 'firstname',$autoquote = true); +# generates UPDATE atable SET firstname='Harun',lastname='Al-Rashid' WHERE id=1000 +# or INSERT INTO atable (id,firstname,lastname) VALUES (1000,'Harun','Al-Rashid') + +# compound key +$ret = $db->Replace('atable2', + array('firstname'=>'Harun','lastname'=>'Al-Rashid', 'age' => 33, 'birthday' => 'null'), + array('lastname','firstname'), + 'firstname',$autoquote = true); + +# no auto-quoting +$ret = $db->Replace('atable2', + array('firstname'=>"'Harun'",'lastname'=>"'Al-Rashid'", 'age' => 'null'), + array('lastname','firstname'), + 'firstname'); + ++GetUpdateSQL(&$rs, $arrFields, $forceUpdate=false,$magicq=false)
+Generate SQL to update a table given a recordset $rs, and the modified fields + of the array $arrFields (which must be an associative array holding the column + names and the new values) are compared with the current recordset. If $forceUpdate + is true, then we also generate the SQL even if $arrFields is identical to + $rs->fields. Requires the recordset to be associative. $magicq is used + to indicate whether magic quotes are enabled (see qstr()). The field names in the array + are case-insensitive.
+GetInsertSQL(&$rs, $arrFields,$magicq=false)
+Generate SQL to insert into a table given a recordset $rs. Requires the query + to be associative. $magicq is used to indicate whether magic quotes are enabled + (for qstr()). The field names in the array are case-insensitive.
+PageExecute($sql, $nrows, $page, $inputarr=false) +Used for pagination of recordset. $page is 1-based. See Example + 8.
+ +CachePageExecute($secs2cache, + $sql, $nrows, $page, $inputarr=false)
+Used for pagination of recordset. $page is 1-based. See + Example 8. Caching version of PageExecute.
+ + + +Close the database connection. PHP4 proudly states that we no longer have to + clean up at the end of the connection because the reference counting mechanism + of PHP4 will automatically clean up for us.
+ +Start a monitored transaction. As SQL statements are executed, ADOdb will monitor + for SQL errors, and if any are detected, when CompleteTrans() is called, we auto-rollback. +
+
To understand why StartTrans() is superior to BeginTrans(), + let us examine a few ways of using BeginTrans(). + The following is the wrong way to use transactions: +
+$DB->BeginTrans(); +$DB->Execute("update table1 set val=$val1 where id=$id"); +$DB->Execute("update table2 set val=$val2 where id=$id"); +$DB->CommitTrans(); ++because you perform no error checking. It is possible to update table1 and + for the update on table2 to fail. Here is a better way: +
+$DB->BeginTrans(); +$ok = $DB->Execute("update table1 set val=$val1 where id=$id"); +if ($ok) $ok = $DB->Execute("update table2 set val=$val2 where id=$id"); +if ($ok) $DB->CommitTrans(); +else $DB->RollbackTrans(); ++Another way is (since ADOdb 2.0): +
+$DB->BeginTrans(); +$ok = $DB->Execute("update table1 set val=$val1 where id=$id"); +if ($ok) $ok = $DB->Execute("update table2 set val=$val2 where id=$id"); +$DB->CommitTrans($ok); ++Now it is a headache monitoring $ok all over the place. StartTrans() is an +improvement because it monitors all SQL errors for you. This is particularly +useful if you are calling black-box functions in which SQL queries might be executed. + Also all BeginTrans, CommitTrans and RollbackTrans calls inside a StartTrans block + will be disabled, so even if the black box function does a commit, it will be ignored. +
+$DB->StartTrans(); +CallBlackBox(); +$DB->Execute("update table1 set val=$val1 where id=$id"); +$DB->Execute("update table2 set val=$val2 where id=$id"); +$DB->CompleteTrans($ok); ++Note that a StartTrans blocks are nestable, the inner blocks are ignored. +
CompleteTrans($autoComplete=true)
+Complete a transaction called with StartTrans(). This function monitors + for SQL errors, and will commit if no errors have occured, otherwise it will rollback. + Returns true on commit, false on rollback. If the parameter $autoComplete is true + monitor sql errors and commit and rollback as appropriate. Set $autoComplete to false + to force rollback even if no SQL error detected. +
+Begin a transaction. Turns off autoCommit. Returns true if successful. Some + databases will always return false if transaction support is not available. + Any open transactions will be rolled back when the connection is closed. Among the + databases that support transactions are Oracle, PostgreSQL, Interbase, MSSQL, certain + versions of MySQL, DB2, Informix, Sybase, etc.
+Note that StartTrans() and CompleteTrans() is a superior method of + handling transactions, available since ADOdb 3.40. For a explanation, see the StartTrans() documentation. + +
You can also use the ADOdb error handler to die + and rollback your transactions for you transparently. Some buggy database extensions + are known to commit all outstanding tranasactions, so you might want to explicitly + do a $DB->RollbackTrans() in your error handler for safety. +
Detecting Transactions
+Since ADOdb 2.50, you are able to detect when you are inside a transaction. Check + that $connection->transCnt > 0. This variable is incremented whenever BeginTrans() is called, + and decremented whenever RollbackTrans() or CommitTrans() is called. +
+End a transaction successfully. Returns true if successful. If the database + does not support transactions, will return true also as data is always committed. +
+If you pass the parameter $ok=false, the data is rolled back. See example in + BeginTrans().
+ +End a transaction, rollback all changes. Returns true if successful. If the + database does not support transactions, will return false as data is never rollbacked. +
+ +Executes the SQL and returns the first field of the first row. The recordset + and remaining rows are discarded for you automatically. If an error occur, false + is returned.
+ +Executes the SQL and returns the first row as an array. The recordset and remaining + rows are discarded for you automatically. If an error occurs, false is returned.
+ + +Executes the SQL and returns the all the rows as a 2-dimensional + array. The recordset is discarded for you automatically. If an error occurs, + false is returned.
+GetCol($sql,$inputarr=false,$trim=false)
+ +Executes the SQL and returns all elements of the first column as a +1-dimensional array. The recordset is discarded for you automatically. If an error occurs, + false is returned.
+CacheGetOne([$secs2cache,] + $sql,$inputarr=false), CacheGetRow([$secs2cache,] $sql,$inputarr=false), CacheGetAll([$secs2cache,] + $sql,$inputarr=false), CacheGetCol([$secs2cache,] + $sql,$inputarr=false,$trim=false)
+ +Similar to above Get* functions, except that the recordset is serialized and + cached in the $ADODB_CACHE_DIR directory for $secs2cache seconds. Good for speeding + up queries on rarely changing data. Note that the $secs2cache parameter is optional. + If omitted, we use the value in $connection->cacheSecs (default is 3600 seconds, + or 1 hour).
+ + +Prepares an SQL query for repeated execution. Only supported + internally by interbase, oci8 and selected ODBC-based drivers, otherwise it + is emulated. There is no performance advantage to using Prepare() with emulation. +
+Returns an array containing the original sql statement + in the first array element; the remaining elements of the array are driver dependent. + If there is an error, or we are emulating Prepare( ), we return the original + $sql string. This is because all error-handling has been centralized in Execute( + ).
+Example:
+$stmt = $DB->Prepare('insert into table (col1,col2) values (?,?)'); +for ($i=0; $i < $max; $i++)+
$DB->Execute($stmt,array((string) rand(), $i)); ++Important: Due to limitations or bugs in PHP, if you are getting errors when you using prepared queries, try +setting $ADODB_COUNTRECS = false before preparing. This behaviour has been observed with ODBC. +
+In the mssql driver, preparing stored procedures requires a special function + call, mssql_init( ), which is called by this function. PrepareSP( ) is available + in all other drivers, and is emulated by calling Prepare( ). For examples of + usage, see Parameter( ) below.
+Returns the same array or $sql string as Prepare( ) above.
+Parameter($stmt, $var, $name, $isOutput=false, + $maxLen = 4000, $type = false )
+Adds a bind parameter in a fashion that is compatible with Microsoft SQL Server + and Oracle oci8. The parameters are:
+
+
+ $stmt Statement returned by Prepare() or PrepareSP().
+ $var PHP variable to bind to. Make sure you pre-initialize it!
+ $name Name of stored procedure variable name to bind to.
+ [$isOutput] Indicates direction of parameter 0/false=IN 1=OUT + 2= IN/OUT. This is ignored in oci8 as this driver auto-detects the direction.
+ [$maxLen] Maximum length of the parameter variable.
+ [$type] Consult mssql_bind and ocibindbyname + docs at php.net for more info on legal values for type.In mssql, $opt can hold the following elements: array('type' => integer, + maxLen =>integer). Example:
+ +# @RETVAL = SP_RUNSOMETHING @myid,@group+
$stmt = $db->PrepareSP('SP_RUNSOMETHING');
# note that the parameter name does not have @ in front!
$db->Parameter($stmt,$id,'myid');
$db->Parameter($stmt,$group,'group',false,64);
# return value in mssql - RETVAL is hard-coded name
$db->Parameter($stmt,$ret,'RETVAL',true);
$db->Execute($stmt);An oci8 example:
+ +# For oracle, Prepare and PrepareSP are identical +$stmt = $db->PrepareSP( + "declare RETVAL integer;+
begin
:RETVAL := SP_RUNSOMETHING(:myid,:group);
end;");
$db->Parameter($stmt,$id,'myid');
$db->Parameter($stmt,$group,'group',false,64); +$db->Parameter($stmt,$ret,'RETVAL',true);
$db->Execute($stmt); +Note that the only difference between the oci8 and mssql implementations is + the syntax of $sql.
+If $type parameter is set to false, in mssql, $type will be dynamicly determined +based on the type of the PHP variable passed (string +=> SQLCHAR, boolean =>SQLINT1, integer =>SQLINT4 or float/double=>SQLFLT8). +In oci8, $type can be set to OCI_B_FILE (Binary-File), OCI_B_CFILE (Character-File), +OCI_B_CLOB (Character-LOB), OCI_B_BLOB (Binary-LOB) and OCI_B_ROWID (ROWID). To +pass in a null, use $db->Parameter($stmt, +$null=null, 'param'). +Lastly, in oci8, bind parameters can be reused without calling PrepareSP( ) + or Parameters again. This is not possible with mssql. An oci8 example:
+$id = 0; $i = 0; +$stmt = $db->PrepareSP( "update table set val=:i where id=:id"); +$db->Parameter($stmt,$id,'id'); +$db->Parameter($stmt,$i, 'i'); +for ($cnt=0; $cnt < 1000; $cnt++) { + $id = $cnt;+
$i = $cnt * $cnt; # works with oci8! + $db->Execute($stmt);
}Bind($stmt, $var, $size=4001, $type=false, $name=false)
+ +This is a low-level function supported only by the oci8 + driver. Avoid using unless you only want to support Oracle. The Parameter( + ) function is the recommended way to go with bind variables.
+Bind( ) allows you to use bind variables in your sql + statement. This binds a PHP variable to a name defined in an Oracle sql statement + that was previously prepared using Prepare(). Oracle named variables begin with + a colon, and ADOdb requires the named variables be called :0, :1, :2, :3, etc. + The first invocation of Bind() will match :0, the second invocation will match + :1, etc. Binding can provide 100% speedups for insert, select and update statements. +
+The other variables, $size sets the buffer size for data storage, $type is the optional +descriptor type OCI_B_FILE (Binary-File), OCI_B_CFILE (Character-File), OCI_B_CLOB (Character-LOB), OCI_B_BLOB (Binary-LOB) and OCI_B_ROWID (ROWID). +Lastly, instead of using the default :0, :1, etc names, you can define your own bind-name using +$name. +
The following example shows 3 bind variables being used: + p1, p2 and p3. These variables are bound to :0, :1 and :2.
+$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); +}+You can also use named variables:
++$stmt = $DB->Prepare("insert into table (col0, col1, col2) values (:name0, :name1, :name2)"); +$DB->Bind($stmt, $p1, "name0"); +$DB->Bind($stmt, $p2, "name1"); +$DB->Bind($stmt, $p3, "name2"); +for ($i = 0; $i < $max; $i++) { + $p1 = ?; $p2 = ?; $p3 = ?; + $DB->Execute($stmt); +}+ +fnExecute and fnCacheExecute properties
+These two properties allow you to define bottleneck functions for all sql statements + processed by ADOdb. This allows you to perform statistical analysis and query-rewriting + of your sql. For example, to count all cached queries and non-cached queries, + you can do this:
+# $db is the connection object +function CountExecs($db, $sql, $inputarray) +{+
global $EXECS; $EXECS++; +} + +# $db is the connection object +function CountCachedExecs($db, $secs2cache, $sql, $inputarray) +{
global $CACHED; $CACHED++; +} +
$db = NewADOConnection('mysql'); +$db->Connect(...); +$db->fnExecute = 'CountExecs'; +$db->fnCacheExecute = 'CountCachedExecs'; + : + :
# After many sql statements:` +printf("<p>Total queries=%d; total cached=%d</p>",$EXECS+$CACHED, $CACHED);The fnExecute function is called before the sql is parsed and executed, so + you can perform a query rewrite. If you are passing in a prepared statement, + then $sql is an array (see Prepare). The fnCacheExecute + function is only called if the recordset returned was cached. + The function parameters match the Execute and CacheExecute functions respectively, + except that $this (the connection object) is passed as the first parameter.
+ +
+ADOConnection Utility Functions
+ +No longer available - removed since 1.99.
+ +Generates the sql string used to concatenate $s1, $s2, etc together. Uses the + string in the concat_operator field to generate the concatenation. Override + this function if a concatenation operator is not used, eg. MySQL.
+Returns the concatenated string.
+ +Format the $date in the format the database accepts; this can be a Unix + integer timestamp or an ISO format Y-m-d. Uses the fmtDate field, which holds + the format to use. If null or false or '' is passed in, it will be converted + to an SQL null.
+Returns the date as a quoted string.
+ +Format the timestamp $ts in the format the database accepts; this can + be a Unix integer timestamp or an ISO format Y-m-d H:i:s. Uses the fmtTimeStamp + field, which holds the format to use. If null or false or '' is passed in, + it will be converted to an SQL null.
+Returns the timestamp as a quoted string.
+qstr($s,[$magic_quotes_enabled=false])
+Quotes a string to be sent to the database. The $magic_quotes_enabled + parameter may look funny, but the idea is if you are quoting a string extracted + from a POST/GET variable, then pass get_magic_quotes_gpc() as the second parameter. + This will ensure that the variable is not quoted twice, once by qstr + and once by the magic_quotes_gpc.
+Eg. $s = $db->qstr(HTTP_GET_VARS['name'],get_magic_quotes_gpc());
+Returns the quoted string.
+ +Quotes the string, automatically checking get_magic_quotes_gpc() first. If + get_magic_quotes_gpc() is set, then we do not quote the string. +
+Returns the number of rows affected by a update or delete statement. Returns + false if function not supported.
+Not supported by interbase/firebird currently.
+ +Returns the last autonumbering ID inserted. Returns false if function not supported. +
+Only supported by databases that support auto-increment or object id's, such + as PostgreSQL, MySQL and MSSQL currently. PostgreSQL returns the OID, which + can change on a database reload.
+ +Returns a list of databases available on the server as an array. You have to + connect to the server first. Only available for ODBC, MySQL and ADO.
+ +Returns an array of tables and views for the current database as an array. + The array should exclude system catalog tables if possible.
+ +Returns an array of ADOFieldObject's, one field object for every column of + $table. Currently Sybase does not recognise date types, and ADO cannot identify + the correct data type (so we default to varchar)..
+ +Returns an array of column names for $table. +
+Returns an array containing column names that are the + primary keys of $table. Only supported by mysql, postgres, oci8 currently. + +
Returns an array of containing two elements 'description' + and 'version'. The 'description' element contains the string description of + the database. The 'version' naturally holds the version number (which is also + a string). +
+ADORecordSet
+When an SQL statement successfully is executed by ADOConnection->Execute($sql),an + ADORecordSet object is returned. This object contains a virtual cursor so + we can move from row to row, functions to obtain information about the columns + and column types, and helper functions to deal with formating the results + to show to the user.
+ADORecordSet Fields
+fields: Array containing the current row. This is not associative, but + is an indexed array from 0 to columns-1. See also the function Fields, + which behaves like an associative array.
+dataProvider: The underlying mechanism used to connect to the database. + Normally set to native, unless using odbc or ado.
+blobSize: Maximum size of a char, string or varchar object before it + is treated as a Blob (Blob's should be shown with textarea's). See the MetaType + function.
+sql: Holds the sql statement used to generate this record set.
+canSeek: Set to true if Move( ) function works.
+EOF: True if we have scrolled the cursor past the last record.
+ADORecordSet Functions
+ADORecordSet( )
+Constructer. Normally you never call this function yourself.
+ +Generates an associative array from the recordset if the number of columns + is greater than 2. The array is generated from the current cursor position + till EOF. The first column of the recordset becomes the key to the rest of + the array. If the columns is equal to two, then the key directly maps to the + value unless $force_array is set to true, when an array is created for each + key. Inspired by PEAR's getAssoc.
+Example:
+We have the following data in a recordset:
+row1: Apple, Fruit, Edible
+
+ row2: Cactus, Plant, Inedible
+ row3: Rose, Flower, EdibleGetAssoc will generate the following associative array:
+Apple => [Fruit, Edible]
+
+ Cactus => [Plant, Inedible]
+ Rose => [Flower,Edible]Returns:
+The associative array, or false if an error occurs.
+ +Generate a 2-dimensional array of records from the current cursor position, + indexed from 0 to $number_of_rows - 1. If $number_of_rows is undefined, till + EOF.
+ +Generate a 2-dimensional array of records from the current cursor position. Synonym +for GetArray() for compatibility with Microsoft ADO. +GetMenu($name, [$default_str=''], [$blank1stItem=true], + [$multiple_select=false], [$size=0], [$moreAttr=''])
+Generate a HTML menu (<select><option><option></select>). + The first column of the recordset (fields[0]) will hold the string to display + in the option tags. If the recordset has more than 1 column, the second column + (fields[1]) is the value to send back to the web server.. The menu will be + given the name $name. +
If $default_str is defined, then if $default_str == fields[0], + that field is selected. If $blank1stItem is true, the first option + is empty. You can also set the first option strings by setting $blank1stItem + = "$value:$text".
+$Default_str can be array for a multiple select listbox.
+To get a listbox, set the $size to a non-zero value (or pass $default_str + as an array). If $multiple_select is true then a listbox will be generated + with $size items (or if $size==0, then 5 items) visible, and we will + return an array to a server. Lastly use $moreAttr to add additional + attributes such as javascript or styles.
+Menu Example 1:
+GetMenu('menu1','A',true)
will generate a menu: + + for the data (A,1), (B,2), (C,3). Also see example 5.Menu Example 2: For the same data,
GetMenu('menu1',array('A','B'),false)
+ will generate a menu with both A and B selected:
+ +GetMenu2($name, [$default_str=''], [$blank1stItem=true], + [$multiple_select=false], [$size=0], [$moreAttr=''])
+This is nearly identical to GetMenu, except that the $default_str is + matched to fields[1] (the option values).
+Menu Example 3: Given the data in menu example 2,
+GetMenu2('menu1',array('1','2'),false)
+ will generate a menu with both A and B selected in menu example 2, but this + time the selection is based on the 2nd column, which holds the values to return + to the Web server. +Converts the date string $str to another format.UserDate calls UnixDate + to parse $str, and $fmt defaults to Y-m-d if not defined.
+ +Converts the timestamp string $str to another format. The timestamp + format is Y-m-d H:i:s, as in '2002-02-28 23:00:12'. UserTimeStamp calls UnixTimeStamp + to parse $str, and $fmt defaults to Y-m-d H:i:s if not defined. +
+ +Parses the date string $str and returns it in unix mktime format (eg. + a number indicating the seconds after January 1st, 1970). Expects the date + to be in Y-m-d H:i:s format, except for Sybase and Microsoft SQL Server, where + M d Y is also accepted (the 3 letter month strings are controlled by a global + array, which might need localisation).
+This function is available in both ADORecordSet and ADOConnection since 1.91.
+ +Parses the timestamp string $str and returns it in unix mktime format + (eg. a number indicating the seconds after January 1st, 1970). Expects the + date to be in Y-m-d H:i:s format, except for Sybase and Microsoft SQL Server, + where M d Y h:i:sA is also accepted (the 3 letter month strings are controlled + by a global array, which might need localisation).
+ +This function is available in both ADORecordSet and ADOConnection + since 1.91.
+OffsetDate($dayFraction, + $basedate=false)
+Allows you to calculate future and past dates based on + $basedate in a portable fashion. If $basedate is not defined, then the current + date (at 12 midnight) is used. Returns the SQL string that performs the calculation + when passed to Execute().
+For example, in Oracle, to find the date and time that + is 2.5 days from today, you can use:
+# get date one week from now +$fld = $conn->OffsetDate(7); // returns "(trunc(sysdate)+7")+# get date and time that is 60 hours from current date and time +$fld = $conn->OffsetDate(2.5, $conn->sysTimeStamp); // returns "(sysdate+2.5)" + +$conn->Execute("UPDATE TABLE SET dodate=$fld WHERE ID=$id");+This function is available for mysql, mssql, oracle, oci8 and postgresql drivers + since 2.13. It might work with other drivers provided + they allow performing numeric day arithmetic on dates.
+ +SQLDate($dateFormat, + $basedate=false)
+ Use the native SQL functions to format a date or date column $basedate, + using a case-sensitive $dateFormat, which supports: ++ Y: 4-digit Year + Q: Quarter (1-4) + m: Month (01-12) + d: Day (01-31) + H: Hour (00-23) + h: Hour (1-12) + i: Minute (00-59) + s: Second (00-60) + A: AM/PM indicator+All other characters are treated as strings. You can also use \ to escape characters. Available + on selected databases, including mysql, postgresql, mssql, oci8 and DB2. +
This is useful in writing portable sql statements that GROUP BY on dates. For example to display + total cost of goods sold broken by quarter (dates are stored in a field called postdate): +
+ $sqlfn = $db->SQLDate('Y-\QQ','postdate'); # get sql that formats postdate to output 2002-Q1 + $sql = "SELECT $sqlfn,SUM(cogs) FROM table GROUP BY $sqlfn ORDER BY 1 desc"; ++ +Move the internal cursor to the next row. The $this->fields array is automatically + updated. Return false if unable to do so (normally because EOF has been reached), otherwise true. + If EOF is reached, then the $this->fields array is set to false (this was only implemented consistently + in ADOdb 3.30). + Note that if false is returned, then the previous array in $this->fields is preserved.
+Example:
+$rs = $db->Execute($sql); +if ($rs) + while (!$rs->EOF) { + ProcessArray($rs->fields); + $rs->MoveNext(); + }+ +Moves the internal cursor to a specific row $to. Rows are zero-based + eg. 0 is the first row. The fields array is automatically updated. For + databases that do not support scrolling internally, ADOdb will simulate forward + scrolling. Some databases do not support backward scrolling. If the $to + position is after the EOF, $to will move to the end of the RecordSet + for most databases. Some obscure databases using odbc might not behave this + way.
+Note: This function uses absolute positioning, unlike Microsoft's ADO.
+Returns true or false. If false, the internal cursor is not moved in most implementations, + so AbsolutePosition( ) will return the last cursor position before the Move( + ).
+ +Internally calls Move(0). Note that some databases do not support this function.
+ +Internally calls Move(RecordCount()-1). Note that some databases do not support + this function.
+ +The above function is no longer the prefered way of getting associative arrays. + Use the $ADODB_FETCH_MODE variable instead.
+Returns an associative array containing the current row. The keys to the array + are the column names. The column names are upper-cased for easy access. To get + the next row, you will still need to call MoveNext().
+For example:
+
+ Array ( [ID] => 1 [FIRSTNAME] => Caroline [LASTNAME] => Miranda [CREATED] => + 2001-07-05 )Note: do not use GetRowAssoc() with $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC. + Because they have the same functionality, they will interfere with each other.
+ + +Returns the current page. Requires PageExecute()/CachePageExecute() to be called. See Example 8.
+ +AtFirstPage($status='')
+Returns true if at first page (1-based). Requires PageExecute()/CachePageExecute() + to be called. See Example 8.
+AtLastPage($status='')
+Returns true if at last page (1-based). Requires PageExecute()/CachePageExecute() + to be called. See Example 8.
+ +This function is deprecated. Use $ADODB_FETCH_MODE + instead.
+Some database extensions (eg. MySQL) return arrays that are both associative + and indexed if you use the native extensions. GetRowAssoc() does not return + arrays that combine associative and indexed elements. Returns the value of the + associated column $colname for the current row. The column name is case-insensitive.
+ + +Returns array containing current row, or false if EOF. + FetchRow( ) internally moves to the next record after returning the current + row.
+Warning: Do not mix using FetchRow() with MoveNext().
+Usage:
+$rs = $db->Execute($sql); +if ($rs) + while ($arr = $rs->FetchRow()) { + # process $arr + }+ +Sets $array to the current row. Returns PEAR_Error object + if EOF, 1 if ok (DB_OK constant). If PEAR is undefined, false is returned when + EOF. FetchInto( ) internally moves to the next + record after returning the current row.
+FetchRow() is easier to use. See above.
+ + +Returns an object containing the name, type and max_length + of the associated field. If the max_length cannot be determined reliably, + it will be set to -1. The column numbers are zero-based. See example + 2.
+ +Returns the number of fields (columns) in the record set.
+ +Returns the number of rows in the record set. If the number of records returned + cannot be determined from the database driver API, we will buffer all rows + and return a count of the rows after all the records have been retrieved. + This buffering can be disabled (for performance reasons) by setting the global + variable $ADODB_COUNTRECS = false. When disabled, RecordCount( ) will return + -1 for certain databases. See the supported databases list above for more + details.
+RowCount is a synonym for RecordCount.
+PO_RecordCount($table, $where)
+Returns the number of rows in the record set. If the database does not support + this, it will perform a SELECT COUNT(*) on the table $table, with the given + $where condition to return an estimate of the recordset size.
+$numrows = $rs->PO_RecordCount("articles_table", "group=$group");
+ NextRecordSet() +For databases that allow multiple recordsets to be returned in one query, this + function allows you to switch to the next recordset. Currently only supported + by mssql driver.
++$rs = $db->Execute('execute return_multiple_rs'); +$arr1 = $rs->GetArray(); +$rs->NextRecordSet(); +$arr2 = $rs->GetArray();+ +Returns the current row as an object. If you set $toupper to true, then the + object fields are set to upper-case. Note: The newer FetchNextObject() is + the recommended way of accessing rows as objects. See below.
+FetchNextObject($toupper=true)
+Gets the current row as an object and moves to the next row automatically. + Returns false if at end-of-file. If you set $toupper to true, then the object + fields are set to upper-case.
++$rs = $db->Execute('select firstname,lastname from table'); +if ($rs) { + while ($o = $rs->FetchNextObject()) { + print "$o->FIRSTNAME, $o->LASTNAME<BR>"; + } +} ++There is some trade-off in speed in using FetchNextObject(). If performance + is important, you should access rows with the
fields[]
array. +FetchObj() +Returns the current record as an object. Fields are not upper-cased, unlike + FetchObject. +
+ +Returns the current record as an object and moves to + the next record. If EOF, false is returned. Fields are not upper-cased, unlike + FetctNextObject.
+ + +Returns the current row of the record set. 0 is the first row.
+ +Synonym for CurrentRow for compatibility with ADO. Returns the current + row of the record set. 0 is the first row.
+MetaType($nativeDBType[,$field_max_length],[$fieldobj])
+Determine what generic meta type a database field type is given its + native type $nativeDBType as a string and the length of the field $field_max_length. + Note that field_max_length can be -1 if it is not known. The field object + returned by the database driver can be passed in $fieldobj. This is + useful for databases such as mysql which has additional properties + in the field object such as primary_key.
+Uses the field blobSize and compares it with $field_max_length + to determine whether the character field is actually a blob.
+For example, $db->MetaType('char') will return 'C'. +Returns:
++
+ +- C: Character fields that should be shown in a <input type="text"> + tag.
+- X: Clob (character large objects), or large text fields that should + be shown in a <textarea>
+- D: Date field
+- T: Timestamp field
+- L: Logical field (boolean or bit-field)
+- N: Numeric field. Includes decimal, numeric, floating point, and + real.
+- I: Integer field.
+- R: Counter or Autoincrement field. Must be numeric.
+- B: Blob, or binary large objects.
+Since ADOdb 3.0, MetaType accepts $fieldobj as the first + parameter, instead of $nativeDBType.
+ + +Close the recordset.
+
+function rs2html($adorecordset,[$tableheader_attributes], + [$col_titles])
+This is a standalone function (rs2html = recordset to html) that is similar + to PHP's odbc_result_all function, it prints a ADORecordSet, $adorecordset + as a HTML table. $tableheader_attributes allow you to control the table + cellpadding, cellspacing and border attributes. Lastly + you can replace the database column names with your own column titles with + the array $col_titles. This is designed more as a quick debugging mechanism, + not a production table recordset viewer.
+You will need to include the file tohtml.inc.php.
+ +<? +include('tohtml.inc.php'); # load code common to ADOdb +include('adodb.inc.php'); # load code common to ADOdb +$conn = &ADONewConnection('mysql'); # create a connection +$conn->PConnect('localhost','userid','','agora');# connect to MySQL, agora db +$sql = 'select CustomerName, CustomerID from customers'; +$rs = $conn->Execute($sql); +rs2html($rs,'border=2 cellpadding=3',array('Customer Name','Customer ID')); +?>+
+Differences between this ADOdb library and Microsoft ADO
++
+- ADOdb only supports recordsets created by a connection object. Recordsets + cannot be created independently.
+- ADO properties are implemented as functions in ADOdb. This makes it easier + to implement any enhanced ADO functionality in the future.
+- ADOdb's ADORecordSet->Move() + uses absolute positioning, not relative. Bookmarks are not supported.
+- ADORecordSet->AbsolutePosition() + cannot be used to move the record cursor.
+- ADO Parameter objects are not supported. Instead we have the ADOConnection::Parameter( + ) function, which provides a simpler interface for calling preparing parameters + and calling stored procedures.
+- Recordset properties for paging records are available, but implemented + as in Example 8.
+
+Database Driver Guide
+This describes how to create a class to connect to a new database. To ensure + there is no duplication of work, kindly email me at jlim#natsoft.com.my if + you decide to create such a class.
+First decide on a name in lower case to call the database type. Let's say we + call it xbase.
+Then we need to create two classes ADODB_xbase and ADORecordSet_xbase + in the file adodb-xbase.inc.php.
+The simplest form of database driver is an adaptation of an existing ODBC driver. + Then we just need to create the class ADODB_xbase extends ADODB_odbc + to support the new date and timestamp formats, the concatenation + operator used, true and false. For the ADORecordSet_xbase + extends ADORecordSet_odbc we need to change the MetaType function. + See adodb-vfp.inc.php as an example.
+More complicated is a totally new database driver that connects to a new PHP + extension. Then you will need to implement several functions. Fortunately, + you do not have to modify most of the complex code. You only need to override + a few stub functions. See adodb-mysql.inc.php for example.
+The default date format of ADOdb internally is YYYY-MM-DD (Ansi-92). All dates + should be converted to that format when passing to an ADOdb date function. + See Oracle for an example how we use ALTER SESSION to change the default date + format in _pconnect _connect.
+ADOConnection Functions to Override
+Defining a constructor for your ADOConnection derived function is optional. + There is no need to call the base class constructor.
+_connect: Low level implementation of Connect. Returns true or false. + Should set the _connectionID.
+_pconnect: Low level implemention of PConnect. Returns true or false. + Should set the _connectionID.
+_query: Execute a query. Returns the queryID, or false.
+_close: Close the connection -- PHP should clean up all recordsets. +
+ErrorMsg: Stores the error message in the private variable _errorMsg. +
+ADOConnection Fields to Set
+_bindInputArray: Set to true if binding of parameters for SQL inserts + and updates is allowed using ?, eg. as with ODBC.
+fmtDate
+fmtTimeStamp
+true
+false
+concat_operator
+replaceQuote
+hasLimit support SELECT * FROM TABLE LIMIT 10 of MySQL.
+hasTop support Microsoft style SELECT TOP 10 * FROM TABLE.
+ADORecordSet Functions to Override
+You will need to define a constructor for your ADORecordSet derived class that + calls the parent class constructor.
+FetchField: as documented above in ADORecordSet
+_initrs: low level initialization of the recordset: setup the _numOfRows + and _numOfFields fields -- called by the constructor.
+_seek: seek to a particular row. Do not load the data into the fields + array. This is done by _fetch. Returns true or false. Note that some implementations + such as Interbase do not support seek. Set canSeek to false.
+_fetch: fetch a row using the database extension function and then move + to the next row. Sets the fields array. If the parameter $ignore_fields + is true then there is no need to populate the fields array, just move + to the next row. then Returns true or false.
+_close: close the recordset
+Fields: If the array row returned by the PHP extension is not an associative + one, you will have to override this. See adodb-odbc.inc.php for an example. + For databases such as MySQL and MSSQL where an associative array is returned, + there is no need to override this function.
+ADOConnection Fields to Set
+canSeek: Set to true if the _seek function works.
+ToDo:
+See the RoadMap article.
+Also see the ADOdb proxy article + for bridging Windows and Unix databases using http remote procedure calls. + For your education, visit palslib.com for + database info, and read this article on Optimizing + PHP.
+ +Change Log
+ +3.60 16 June 2003 + +
We now SET CONCAT_NULL_YIELDS_NULL OFF for odbc_mssql driver to be compat with mssql driver. + +
The property $emptyDate missing from connection class. Also changed 1903 to constant (TIMESTAMP_FIRST_YEAR=100). +Thx to Sebastiaan van Stijn. +
ADOdb speedup optimization - we now return all arrays by reference. +
Now DBDate() and DBTimeStamp() now accepts the string 'null' as a parameter. Suggested by vincent. +
Added GetArray() to connection class. +
Added not_null check in informix metacolumns(). +
Connection parameters for postgresql did not work correctly when port was defined. +
DB2 is now a tested driver, making adodb 100% compatible. +Extensive changes to odbc driver for DB2, including implementing serverinfo() and SQLDate(), +switching to SQL_CUR_USE_ODBC as the cursor mode, +and lastAffectedRows and SelectLimit() fixes. +
The odbc driver's FetchField() field names did not obey ADODB_ASSOC_CASE. Fixed. +
Some bugs in adodb_backtrace() fixed. +
Added "INT IDENTITY" type to adorecordset::MetaType() to support odbc_mssql properly. +
MetaColumns() for oci8, mssql, odbc revised to support scale. Also minor revisions to odbc +MetaColumns() for vfp and db2 compat. +
Added unsigned support to mysql datadict class. Thx to iamsure. +
Infinite loop in mssql MoveNext() fixed when ADODB_FETCH_ASSOC used. Thx to Josh R, Night_Wulfe#hotmail.com. +
ChangeTableSQL contributed by Florian Buzin. +
The odbc_mssql driver now sets CONCAT_NULL_YIELDS_NULL OFF for compat with mssql driver. +
3.50 19 May 2003
+Fixed mssql compat with FreeTDS. FreeTDS does not implement mssql_fetch_assoc(). +
Merged back connection and recordset code into adodb.inc.php. +
ADOdb sessions using oracle clobs contributed by achim.gosse#ddd.de. See adodb-session-clob.php. +
Added /s modifier to preg_match everywhere, which ensures that regex does not stop at /n. Thx Pao-Hsi Huang. +
Fixed error in metacolumns() for mssql. +
Added time format support for SQLDate. +
Image => B added to metatype. +
MetaType now checks empty($this->blobSize) instead of empty($this). +
Datadict has beta support for informix, sybase (mapped to mssql), db2 and generic (which is a fudge). +
BlobEncode for postgresql uses pg_escape_bytea, if available. Needed for compat with 7.3. +
Added $ADODB_LANG, to support multiple languages in MetaErrorMsg(). +
Datadict can now parse table definition as declarative text. +
For DataDict, oci8 autoincrement trigger missing semi-colon. Fixed. +
For DataDict, when REPLACE flag enabled, drop sequence in datadict for autoincrement field in postgres and oci8.s +
Postgresql defaults to template1 database if no database defined in connect/pconnect. +
We now clear _resultid in postgresql if query fails. +
3.40 19 May 2003
+Added insert_id for odbc_mssql. +
Modified postgresql UpdateBlobFile() because it did not work in safe mode. +
Now connection object is passed to raiseErrorFn as last parameter. Needed by StartTrans(). +
Added StartTrans() and CompleteTrans(). It is recommended that you do not modify transOff, but +use the above functions. +
oci8po now obeys ADODB_ASSOC_CASE settings. +
Added virtualized error codes, using PEAR DB equivalents. Requires you to manually include + adodb-error.inc.php yourself, with MetaError() and MetaErrorMsg($errno). +
GetRowAssoc for mysql and pgsql were flawed. Fix by Ross Smith. +
Added to datadict types I1, I2, I4 and I8. Changed datadict type 'T' to map to +timestamp instead of datetime for postgresql. +
Error handling in ExecuteSQLArray(), adodb-datadict.inc.php did not work. +
We now auto-quote postgresql connection parameters when building connection string. +
Added session expiry notification. +
We now test with odbc mysql - made some changes to odbc recordset constructor. +
MetaColumns now special cases access and other databases for odbc. +
3.31 17 March 2003
+Added row checking for _fetch in postgres. +
Added Interval type to MetaType for postgres. +
Remapped postgres driver to call postgres7 driver internally. +
Adorecordset_array::getarray() did not return array when nRows >= 0. +
Postgresql: at times, no error message returned by pg_result_error() + but error message returned in pg_last_error(). Recoded again. +
Interbase blob's now use chunking for updateblob. +
Move() did not set EOF correctly. Reported by Jorma T. +
We properly support mysql timestamp fields when we are creating mysql + tables using the data-dict interface. +
Table regex includes backticks character now. +
3.30 3 March 2003
+Added $ADODB_EXTENSION and $ADODB_COMPAT_FETCH constant. +
Made blank1stItem configurable using syntax "value:text" in GetMenu/GetMenu2. Thx to Gabriel Birke. +
Previously ADOdb differed from the Microsoft standard because it did not define + what to set $this->fields when EOF was reached. Now at EOF, ADOdb sets $this->fields + to false for all databases, which is consist with Microsoft's implementation. + Postgresql and mysql have always worked this way (in 3.11 and earlier). If + you are experiencing compatibility problems (and you are not using postgresql + nor mysql) on upgrading to 3.30, try setting the global variables $ADODB_COUNTRECS + = true (which is the default) and $ADODB_FETCH_COMPAT = true (this is a new + global variable). +
We now check both pg_result_error and pg_last_error as sometimes pg_result_error does not display anything. + Iman Mayes +
+We no longer check for magic quotes gpc in Quote(). +
+Misc fixes for table creation in adodb-datadict.inc.php. Thx to iamsure. +
+Time calculations use adodb_time library for all negative timestamps +due to problems in Red Hat 7.3 or later. Formerly, only did this for +Windows. +
+In mssqlpo, we now check if $sql in _query is a string before we change || to +. This is +to support prepared stmts. +
+Move() and MoveLast() internals changed to support to support EOF and $this->fields change. +
+Added ADODB_FETCH_BOTH support to mssql. Thx to Angel Fradejas afradejas#mediafusion.es +
+We now check if link resource exists before we run mysql_escape_string in qstr(). +
+Before we flock in csv code, we check that it is not a http url. +
3.20 17 Feb 2003
+Added new Data Dictionary classes for creating tables and indexes. Warning - this is very much alpha quality code. +The API can still change. See adodb/tests/test-datadict.php for more info. +
We now ignore $ADODB_COUNTRECS for mysql, because PHP truncates incomplete recordsets + when mysql_unbuffered_query() is called a second time. +
Now postgresql works correctly when $ADODB_COUNTRECS = false. +
Changed _adodb_getcount to properly support SELECT DISTINCT. +
Discovered that $ADODB_COUNTRECS=true has some problems with prepared queries - suspect +PHP bug. +
Now GetOne and GetRow run in $ADODB_COUNTRECS=false mode for better performance. +
Added support for mysql_real_escape_string() and pg_escape_string() in qstr(). +
Added an intermediate variable for mysql _fetch() and MoveNext() to store fields, to prevent +overwriting field array with boolean when mysql_fetch_array() returns false. +
Made arrays for getinsertsql and getupdatesql case-insensitive. Suggested by Tim Uckun" tim#diligence.com +
3.11 11 Feb 2003
+Added check for ADODB_NEVER_PERSIST constant in PConnect(). +If defined, then PConnect() will actually call non-persistent Connect(). +
Modified interbase to properly work with Prepare(). +
Added $this->ibase_timefmt to allow you to change the date and time format. +
Added support for $input_array parameter in CacheFlush(). +
Added experimental support for dbx, which was then removed when i found that +it was slower than using native calls. +
Added MetaPrimaryKeys for mssql and ibase/firebird. +
Added new $trim parameter to GetCol and CacheGetCol +
Uses updated adodb-time.inc.php 0.06. +
3.10 27 Jan 2003 +
Added adodb_date(), adodb_getdate(), adodb_mktime() and adodb-time.inc.php. +
For interbase, added code to handle unlimited number of bind parameters. +From Daniel Hasan daniel#hasan.cl. +
Added BlobDecode and UpdateBlob for informix. Thx to Fernando Ortiz. +
Added constant ADODB_WINDOWS. If defined, means that running on Windows. +
Added constant ADODB_PHPVER which stores php version as a hex num. Removed $ADODB_PHPVER variable. +
Felho Bacsi reported a minor white-space regular expression problem in GetInsertSQL. +
Modified ADO to use variant to store _affectedRows +
Changed ibase to use base class Replace(). Modified base class Replace() to support ibase. +
Changed odbc to auto-detect when 0 records returned is wrong due to bad odbc drivers. +
Changed mssql to use datetimeconvert ini setting only when 4.30 or later (does not work in 4.23). +
ExecuteCursor($stmt, $cursorname, $params) now accepts a new $params array of additional bind +parameters -- William Lovaton walovaton#yahoo.com.mx. +
Added support for sybase_unbuffered_query if ADODB_COUNTRECS == false. Thx to chuck may. +
Fixed FetchNextObj() bug. Thx to Jorma Tuomainen. +
We now use SCOPE_IDENTITY() instead of @@IDENTITY for mssql - thx to marchesini#eside.it +
Changed postgresql movenext logic to prevent illegal row number from being passed to pg_fetch_array(). +
Postgresql initrs bug found by "Bogdan RIPA" bripa#interakt.ro $f1 accidentally named $f +
3.00 6 Jan 2003 +
Fixed adodb-pear.inc.php syntax error. +
Improved _adodb_getcount() to use SELECT COUNT(*) FROM ($sql) for languages that +accept it. +
Fixed _adodb_getcount() caching error. +
Added sql to retrive table and column info for odbc_mssql. +
2.91 3 Jan 2003 +
Revised PHP version checking to use $ADODB_PHPVER with legal values 0x4000, 0x4050, 0x4200, 0x4300. +
Added support for bytea fields and oid blobs in postgres by allowing BlobDecode() + to detect and convert non-oid fields. Also added BlobEncode to postgres when + you want to encode oid blobs. +
Added blobEncodeType property for connections to inform phpLens what encoding +method to use for blobs. +
Added BlobDecode() and BlobEncode() to base ADOConnection class. +
Added umask() to _gencachename() when creating directories. +
Added charPage for ado drivers, so you can set the code page. +
+$conn->charPage = CP_UTF8; +$conn->Connect($dsn); ++Modified _seek in mysql to check for num rows=0. +
Added to metatypes new informix types for IDS 9.30. Thx Fernando Ortiz. +
_maxrecordcount returned in CachePageExecute $rsreturn +
Fixed sybase cacheselectlimit( ) problems +
MetaColumns() max_length should use precision for types X and C for ms access. Fixed. +
Speedup of odbc non-SELECT sql statements. +
Added support in MetaColumns for Wide Char types for ODBC. We halve max_length + if unicode/wide char. +
Added 'B' to types handled by GetUpdateSQL/GetInsertSQL. +
Fixed warning message in oci8 driver with $persist variable when using PConnect. +
2.90 11 Dec 2002 +
Mssql and mssqlpo and oci8po now support ADODB_ASSOC_CASE. +
Now MetaType() can accept a field object as the first parameter. +
New $arr = $db->ServerInfo( ) function. Returns $arr['description'] which + is the string description, and $arr['version']. +
PostgreSQL and MSSQL speedups for insert/updates. +
Implemented new SetFetchMode() that removes the need to use $ADODB_FETCH_MODE. + Each connection has independant fetchMode. +
ADODB_ASSOC_CASE now defaults to 2, use native defaults. This is because we would +break backward compat for too many applications otherwise. +
Patched encrypted sessions to use replace() +
The qstr function supports quoting of nulls when escape character is \ +
Rewrote bits and pieces of session code to check for time synch and improve reliability. +
Added property ADOConnection::hasTransactions = true/false; +
Added CreateSequence and DropSequence functions +
Found misplaced MoveNext() in adodb-postgres.inc.php. Fixed. +
Sybase SelectLimit not reliable because 'set rowcount' not cached - fixed. +
Moved ADOConnection to adodb-connection.inc.php and ADORecordSet to adodb-recordset.inc.php. +This allows us to use doxygen to generate documentation. Doxygen doesn't like the classes +in the main adodb.inc.php file for some mysterious reason. +
2.50, 14 Nov 2002 +
Added transOff and transCnt properties for disabling (transOff = true) +and tracking transaction status (transCnt>0). +
Added inputarray handling into _adodb_pageexecute_all_rows - "Ross Smith" RossSmith#bnw.com. +
Fixed postgresql inconsistencies in date handling. +
Added support for mssql_fetch_assoc. +
Fixed $ADODB_FETCH_MODE bug in odbc MetaTables() and MetaPrimaryKeys(). +
Accidentally declared UnixDate() twice, making adodb incompatible with php 4.3.0. Fixed. +
Fixed pager problems with some databases that returned -1 for _currentRow on MoveLast() by +switching to MoveNext() in adodb-lib.inc.php. +
Also fixed uninited $discard in adodb-lib.inc.php. +
2.43, 25 Oct 2002
+Added ADODB_ASSOC_CASE constant to better support ibase and odbc field names. +Added support for NConnect() for oracle OCINLogin. +
Fixed NumCols() bug. +
Changed session handler to use Replace() on write. +
Fixed oci8 SelectLimit aggregate function bug again. +
Rewrote pivoting code. +
2.42, 4 Oct 2002
+Fixed ibase_fetch() problem with nulls. Also interbase now does automatic blob decoding, +and is backward compatible. Suggested by Heinz Hombergs heinz#hhombergs.de. +
Fixed postgresql MoveNext() problems when called repeatedly after EOF. +Also suggested by Heinz Hombergs. +
PageExecute() does not rewrite queries if SELECT DISTINCT is used. Requested by hans#velum.net +
Added additional fixes to oci8 SelectLimit handling with aggregate functions - thx to Christian Bugge +for reporting the problem. +
2.41, 2 Oct 2002
+Fixed ADODB_COUNTRECS bug in odbc. Thx to Joshua Zoshi jzoshi#hotmail.com. +
Increased buffers for adodb-csvlib.inc.php for extremely long sql from 8192 to 32000. +
Revised pivottable.inc.php code. Added better support for aggregate fields. +
Fixed mysql text/blob types problem in MetaTypes base class - thx to horacio degiorgi. +
Added SQLDate($fmt,$date) function, which allows an sql date format string to be generated - +useful for group by's. +
Fixed bug in oci8 SelectLimit when offset>100. +
2.40 4 Sept 2002
+Added new NLS_DATE_FORMAT property to oci8. Suggested by Laurent NAVARRO ln#altidev.com +
Now use bind parameters in oci8 selectlimit for better performance. +
Fixed interbase replaceQuote for dialect != 1. Thx to +"BEGUIN Pierre-Henri - INFOCOB" phb#infocob.com. +
Added white-space check to QA. +
Changed unixtimestamp to support fractional seconds (we always round down/floor the seconds). + Thanks to beezly#beezly.org.uk. +
Now you can set the trigger_error type your own user-defined type in adodb-errorhandler.inc.php. +Suggested by Claudio Bustos clbustos#entelchile.net. +
Added recordset filters with rsfilter.inc.php. +
$conn->_rs2rs does not create a new recordset when it detects it is of type array. Some +trickery there as there seems to be a bug in Zend Engine +
Added render_pagelinks to adodb-pager.inc.php. Code by "Pablo Costa" pablo#cbsp.com.br. +
MetaType() speedup in adodb.inc.php by using hashing instead of switch. Best performance +if constant arrays are supported, as they are in PHP5. +
adodb-session.php now updates only the expiry date if the crc32 check indicates + that the data has not been modified.
+0.10 Sept 9 2000 First release +
Old changelog history moved to old-changelog.htm.
++
+ + diff --git a/lib/adodb/docs-datadict.htm b/lib/adodb/docs-datadict.htm new file mode 100644 index 0000000000..fe0a45c1fb --- /dev/null +++ b/lib/adodb/docs-datadict.htm @@ -0,0 +1,293 @@ + +
+ADODB Data Dictionary Manual + ++ + + + ADOdb Data Dictionary Library for PHP
+V3.60 16 June 2003 (c) 2000-2003 John Lim (jlim#natsoft.com.my)
+This software is dual licensed using BSD-Style and LGPL. +Where there is any discrepancy, the BSD-Style license will take precedence. +This means you can use it in proprietary and commercial products.
+ +This documentation describes a class library to automate the creation of tables, + indexes and foreign key constraints portably for multiple databases. Download + from http://php.weblogs.com/adodb +
Currently the following databases are supported: +
Well-tested: PostgreSQL, MySQL, Oracle, MSSQL.
+
+Beta-quality: DB2, Informix, Sybase, Interbase, Firebird.
+Alpha-quality: MS Access (does not support DEFAULT values) and generic ODBC.Example Usage
+include_once('adodb.inc.php'); + +# First create a normal connection +$db->NewADOConnection('mysql'); +$db->Connect(...); ++
+# Then create a data dictionary object, using this connection +$dict = NewDataDictionary($db); + +# We have a portable declarative data dictionary format in ADOdb 3.50, similar to SQL. +# Field types use 1 character codes, and fields are separated by commas. +# The following example creates three fields: "col1", "col2" and "col3": +$flds = " + col1 C(32) NOTNULL DEFAULT 'abc', + col2 I DEFAULT 0, + col3 N(12.2) +";
+# We demonstrate creating tables and indexes +$sqlarray = $dict->CreateTableSQL($tabname, $flds, $taboptarray); +$dict->ExecuteSQLArray($sqlarray);
+$idxflds = 'co11, col2'; +$sqlarray = $dict->CreateIndexSQL($idxname, $tabname, $idxflds); +$dict->ExecuteSQLArray($sqlarray); +Functions
+function CreateDatabase($dbname, $optionsarray=false) +
Create a database with the name $dbname; +
function CreateTableSQL($tabname, $fldarray, $taboptarray=false) +
+ RETURNS: an array of strings, the sql to be executed, or false + $tabname: name of table + $fldarray: string (or array) containing field info + $taboptarray: array containing table options +++ The new format of $fldarray uses a free text format, where each field is comma-delimited. + The first token for each field is the field name, followed by the type and optional + field size. Then optional keywords in $otheroptions: +
"$fieldname $type $colsize $otheroptions"+The older (and still supported) format of $fldarray is a 2-dimensional array, where each row in the + 1st dimension represents one field. Each row has this format: +
array($fieldname, $type, [,$colsize] [,$otheroptions]*)+ The first 2 fields must be the field name and the field type. The field type + can be a portable type codes or the actual type for that database. ++ Legal portable type codes include: +
+C: varchar +X: Largest varchar size +XL: For Oracle, returns CLOB, otherwise same as 'X' above + +C2: Multibyte varchar +X2: Multibyte varchar (largest size) + +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 +The $colsize field represents the size of the field. If a decimal number is + used, then it is assumed that the number following the dot is the precision, + so 6.2 means a number of size 6 digits and 2 decimal places. It is + recommended that the default for number types be represented as a string to + avoid any rounding errors. +
+ The $otheroptions include the following keywords (case-insensitive): +
+AUTO For autoincrement number. Emulated with triggers if not available. + Sets NOTNULL also. +AUTOINCREMENT Same as auto. +KEY Primary key field. Sets NOTNULL also. Compound keys are supported. +PRIMARY Same as KEY. +DEF Synonym for DEFAULT for lazy typists. +DEFAULT The default value. Character strings are auto-quoted unless + the string begins and ends with spaces, eg ' SYSDATE '. +NOTNULL If field is not null. +DEFDATE Set default value to call function to get today's date. +DEFTIMESTAMP Set default to call function to get today's datetime. +NOQUOTE Prevents autoquoting of default string values. +CONSTRAINTS Additional constraints defined at the end of the field + definition. ++The Data Dictonary accepts two formats, the older array specification:
++$flds = array( + array('COLNAME', 'DECIMAL', '8.4', 'DEFAULT' => 0, 'NotNull'), + array('ID', 'I' , 'AUTO'), + array('MYDATE', 'D' , 'DEFDATE'), + array('NAME', 'C' ,'32', + 'CONSTRAINTS' => 'FOREIGN KEY REFERENCES reftable') +);+Or the simpler declarative format: +$flds = " + COLNAME DECIMAL(8.4) DEFAULT 0 NotNull, + ID I AUTO, + MYDATE D DEFDATE, + NAME C(32) CONSTRAINTS 'FOREIGN KEY REFERENCES reftable' +"; +++ The $taboptarray is the 3rd parameter of the CreateTableSQL function. + This contains table specific settings. Legal keywords include: + +
+
+- REPLACE
+
+ Indicates that the previous table definition should be removed (dropped)together + with ALL data. See first example below.
+- CONSTRAINTS
+
+ Define this as the key, with the constraint as the value. See the postgresql + example below. Additional constraints defined for the whole table. You + will probably need to prefix this with a comma.Database specific table options can be defined also using the name of the + database type as the array key. In the following example, create the table + as ISAM with MySQL, and store the table in the "users" tablespace + if using Oracle. And if the table already exists, drop the table first. +
$taboptarray = array('mysql' => 'TYPE=ISAM', 'oci8' => 'tablespace users', 'REPLACE');++ You can also define foreignkey constraints. The following is syntax for + postgresql:
+ $taboptarray = array('constraints' => + ', FOREIGN KEY (col1) REFERENCES reftable (refcol)'); ++function ChangeTableSQL($tabname, $flds) +
Checks to see if table exists, if table does not exist, behaves like CreateTableSQL. + If table exists, generates appropriate ALTER TABLE MODIFY COLUMN commands if + field already exists, or ALTER TABLE ADD $column if field does not exist. +
The class must be connected to the database for ChangeTableSQL to detect the + existance of the table. Idea and code contributed by Florian Buzin. +
function CreateIndexSQL($idxname, $tabname, $flds, $idxoptarray=false) +
+ RETURNS: an array of strings, the sql to be executed, or false +
+ $idxname: name of index + $tabname: name of table + $flds: list of fields as a comma delimited string or an array of strings + $idxoptarray: array of index creation options ++$idxoptarray is similar to $taboptarray in that index specific information can + be embedded in the array. Other options include: +
+ CLUSTERED Create clustered index (only mssql) + BITMAP Create bitmap index (only oci8) + UNIQUE Make unique index + FULLTEXT Make fulltext index (only mysql) + HASH Create hash index (only postgres) ++function AddColumnSQL($tabname, $flds) +
Add one or more columns. Not guaranteed to work under all situations. +
function AlterColumnSQL($tabname, $flds) +
Warning, not all databases support this feature. +
function DropColumnSQL($tabname, $flds) +
Drop 1 or more columns. +
function ExecuteSQLArray($sqlarray, $contOnError = true) +
+ RETURNS: 0 if failed, 1 if executed all but with errors, 2 if executed successfully + $sqlarray: an array of strings with sql code (no semicolon at the end of string) + $contOnError: if true, then continue executing even if error occurs ++Executes an array of SQL strings returned by CreateTableSQL or CreateIndexSQL. +
+XML Schema
+This is a class contributed by Richard Tango-Lowy that allows the user to quickly + and easily build a database using the excellent +ADODB database library and a simple XML formatted file. + + +Quick Start
+First, create an XML database schema. Let's call it "schema.xml:"
+<?xml version="1.0"?> +<schema> + <table name="mytable"> + <field name="row1" type="I"> + <descr>An integer row that's a primary key and autoincrements</descr> + <KEY/> + <AUTOINCREMENT/> + </field> + <field name="row2" type="C" size="16"> + <descr>A 16 character varchar row that can't be null</descr> + <NOTNULL/> + </field> + </table> + <index name="myindex" table="mytable"> + <col>row1</col> + <col>row2</col> + </index> + <sql> + <descr>SQL to be executed only on specific platforms</descr> + <query platform="postgres|postgres7"> + insert into mytable ( row1, row2 ) values ( 12, 'stuff' ) + </query> + <query platform="mysql"> + insert into mytable ( row1, row2 ) values ( 12, 'different stuff' ) + </query> + </sql> +</schema> +Create a new database using the appropriate tool for your platform. +Executing the following PHP code will create the a mytable and myindex +in the database and insert one row into mytable if the platform is postgres or mysql.
+include_once('/path/to/adodb.inc.php'); +include_once('/path/to/adodb-xmlschema.inc.php'); + +// To build the schema, start by creating a normal ADOdb connection: +$db->NewADOConnection( 'mysql' ); +$db->Connect( ... ); + +// Create the schema object and build the query array. +$schema = new adoSchema( $db ); + +// Build the SQL array +$sql = $schema->ParseSchema( "schema.xml" ); + +// Execute the SQL on the database +$result = $schema->ExecuteSchema( $sql ); + +// Finally, clean up after the XML parser +// (PHP won't do this for you!) +$schema->Destroy(); ++ +XML Schema Format:
+(See ADOdb_schema.dtd for the full specification)
++<?xml version="1.0"?> +<schema> + <table name="tablename" platform="platform1|platform2|..."> + <descr>Optional description</descr> + <field name="fieldname" type="datadict_type" size="size"> + <KEY/> + <NOTNULL/> + <AUTOINCREMENT/> + <DEFAULT value="value"/> + </field> + ... more fields + </table> + ... more tables + + <index name="indexname" platform="platform1|platform2|..."> + <descr>Optional description</descr> + <col>fieldname</col> + ... more columns + </index> + ... more indices + + <sql platform="platform1|platform2|..."> + <descr>Optional description</descr> + <query platform="platform1|platform2|...">SQL query</query> + ... more queries + </sql> + ... more SQL + </schema> ++
+ +If you have any questions or comments, please email them to me at +richtl#arscognita.com. + + + diff --git a/lib/adodb/docs-session.htm b/lib/adodb/docs-session.htm new file mode 100644 index 0000000000..7ec9eaad68 --- /dev/null +++ b/lib/adodb/docs-session.htm @@ -0,0 +1,130 @@ + + +ADODB Session Management Manual + ++ + + ADODB Session Management Manual
++V3.60 16 June 2003 (c) 2000-2003 John Lim (jlim#natsoft.com.my) +
+This software is dual licensed using BSD-Style and LGPL. Where there is any discrepancy, the BSD-Style license will take precedence. This means you can use it in proprietary and commercial products. + +
Introduction
+PHP is packed with good features. One of the most popular is session variables. +These are variables that persist throughout a session, as the user moves from page to page. Session variables are great holders of state information and other useful stuff. +
+To use session variables, call session_start() at the beginning of your web page, +before your HTTP headers are sent. Then for every variable you want to keep alive +for the duration of the session, call session_register($variable_name). By default, +the session handler will keep track of the session by using a cookie. You can save objects + or arrays in session variables also. +
The default method of storing sessions is to store it in a file. However if you have multiple web servers, +or need to do special processing of each session, or require notification when a session expires, you +need to override the default session storage behaviour. +
The ADOdb session handler provides you with the above additional capabilities by storing +the session information as records in a database table that can be shared by multiple servers. +
Setup
+There are 3 session management files that you can use: +
+adodb-session.inc.php : The default +adodb-session-clob.inc.php : Use this if you are storing DATA in clobs +adodb-cryptsession.inc.php : Use this if you want to store encrypted session data in the database + +Examples + + GLOBAL $HTTP_SESSION_VARS; + include('adodb.inc.php'); + include('adodb-session.php'); + session_start(); + session_register('AVAR'); + $HTTP_SESSION_VARS['AVAR'] += 1; + print "+\$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}
"; + +To force non-persistent connections, call adodb_session_open first before session_start(): + + GLOBAL $HTTP_SESSION_VARS; + include('adodb.inc.php'); + include('adodb-session.php'); + adodb_sess_open(false,false,false); + session_start(); + session_register('AVAR'); + $HTTP_SESSION_VARS['AVAR'] += 1; + print "\$HTTP_SESSION_VARS['AVAR']={$HTTP_SESSION_VARS['AVAR']}
"; + +To use a encrypted sessions, simply replace the file: + + GLOBAL $HTTP_SESSION_VARS; + include('adodb.inc.php'); + include('adodb-cryptsession.php'); + session_start(); + +And the same technique for adodb-session-clob.inc.php: + + GLOBAL $HTTP_SESSION_VARS; + include('adodb.inc.php'); + include('adodb-session-clob.php'); + session_start(); + + Installation + 1. Create this table in your database (syntax might vary depending on your db): + + create table sessions ( + SESSKEY char(32) not null, + EXPIRY int(11) unsigned not null, + EXPIREREF varchar(64), + DATA text not null, + primary key (sesskey) + ); + + For the adodb-session-clob.inc.php version, create this: + + create table sessions ( + SESSKEY char(32) not null, + EXPIRY int(11) unsigned not null, + EXPIREREF varchar(64), + DATA CLOB, + primary key (sesskey) + ); + + 2. Then define the following parameters in this file: + $ADODB_SESSION_DRIVER='database driver, eg. mysql or ibase'; + $ADODB_SESSION_CONNECT='server to connect to'; + $ADODB_SESSION_USER ='user'; + $ADODB_SESSION_PWD ='password'; + $ADODB_SESSION_DB ='database'; + $ADODB_SESSION_TBL = 'sessions' + + 3. Recommended is PHP 4.0.6 or later. There are documented + session bugs in earlier versions of PHP. + + 4. If you want to receive notifications when a session expires, then + you can tag a session with an EXPIREREF, and before the session + record is deleted, we can call a function that will pass the EXPIREREF + as the first parameter, and the session key as the second parameter. + + To do this, define a notification function, say NotifyFn: + + function NotifyFn($expireref, $sesskey) + { + } + + Then define a global variable, with the first parameter being the + global variable you would like to store in the EXPIREREF field, and + the second is the function name. + + In this example, we want to be notified when a user's session + has expired, so we store the user id in $USERID, and make this + the value stored in the EXPIREREF field: + + $ADODB_SESSION_EXPIRE_NOTIFY = array('USERID','NotifyFn'); ++Also see the core ADOdb documentation. + + \ No newline at end of file diff --git a/lib/adodb/drivers/adodb-access.inc.php b/lib/adodb/drivers/adodb-access.inc.php index fa4aa4ae07..e154ba57e4 100644 --- a/lib/adodb/drivers/adodb-access.inc.php +++ b/lib/adodb/drivers/adodb-access.inc.php @@ -1,74 +1,74 @@ -ADODB_odbc(); - } - - function BeginTrans() { return false;} - - function &MetaTables() - { - global $ADODB_FETCH_MODE; - - $savem = $ADODB_FETCH_MODE; - $ADODB_FETCH_MODE = ADODB_FETCH_NUM; - $qid = odbc_tables($this->_connectionID); - $rs = new ADORecordSet_odbc($qid); - $ADODB_FETCH_MODE = $savem; - if (!$rs) return false; - - $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change; - - $arr = &$rs->GetArray(); - //print_pre($arr); - $arr2 = array(); - for ($i=0; $i < sizeof($arr); $i++) { - if ($arr[$i][2] && $arr[$i][3] != 'SYSTEM TABLE') - $arr2[] = $arr[$i][2]; - } - return $arr2; - } -} - - -class ADORecordSet_access extends ADORecordSet_odbc { - - var $databaseType = "access"; - - function ADORecordSet_access($id,$mode=false) - { - return $this->ADORecordSet_odbc($id,$mode); - } -}// class -} +ADODB_odbc(); + } + + function BeginTrans() { return false;} + + function &MetaTables() + { + global $ADODB_FETCH_MODE; + + $savem = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + $qid = odbc_tables($this->_connectionID); + $rs = new ADORecordSet_odbc($qid); + $ADODB_FETCH_MODE = $savem; + if (!$rs) return false; + + $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change; + + $arr = &$rs->GetArray(); + /* print_pre($arr); */ + $arr2 = array(); + for ($i=0; $i < sizeof($arr); $i++) { + if ($arr[$i][2] && $arr[$i][3] != 'SYSTEM TABLE') + $arr2[] = $arr[$i][2]; + } + return $arr2; + } +} + + +class ADORecordSet_access extends ADORecordSet_odbc { + + var $databaseType = "access"; + + function ADORecordSet_access($id,$mode=false) + { + return $this->ADORecordSet_odbc($id,$mode); + } +}/* class */ +} ?> \ No newline at end of file diff --git a/lib/adodb/drivers/adodb-ado.inc.php b/lib/adodb/drivers/adodb-ado.inc.php index 9d195d3170..98271168e3 100644 --- a/lib/adodb/drivers/adodb-ado.inc.php +++ b/lib/adodb/drivers/adodb-ado.inc.php @@ -1,589 +1,589 @@ -_affectedRows = new VARIANT; - } - - function ServerInfo() - { - if (!empty($this->_connectionID)) $desc = $this->_connectionID->provider; - return array('description' => $desc, 'version' => ''); - } - - function _affectedrows() - { - return $this->_affectedRows->value; - } - - // you can also pass a connection string like this: - // - // $DB->Connect('USER ID=sa;PASSWORD=pwd;SERVER=mangrove;DATABASE=ai',false,false,'SQLOLEDB'); - function _connect($argHostname, $argUsername, $argPassword, $argProvider= 'MSDASQL') - { - $u = 'UID'; - $p = 'PWD'; - - if (!empty($this->charPage)) - $dbc = new COM('ADODB.Connection',null,$this->charPage); - else - $dbc = new COM('ADODB.Connection'); - - if (! $dbc) return false; - - /* special support if provider is mssql or access */ - if ($argProvider=='mssql') { - $u = 'User Id'; //User parameter name for OLEDB - $p = 'Password'; - $argProvider = "SQLOLEDB"; // SQL Server Provider - - // not yet - //if ($argDatabasename) $argHostname .= ";Initial Catalog=$argDatabasename"; - - //use trusted conection for SQL if username not specified - if (!$argUsername) $argHostname .= ";Trusted_Connection=Yes"; - } else if ($argProvider=='access') - $argProvider = "Microsoft.Jet.OLEDB.4.0"; // Microsoft Jet Provider - - if ($argProvider) $dbc->Provider = $argProvider; - - if ($argUsername) $argHostname .= ";$u=$argUsername"; - if ($argPassword)$argHostname .= ";$p=$argPassword"; - - if ($this->debug) ADOConnection::outp( "Host=".$argHostname."
\n version=$dbc->version"); - // @ added below for php 4.0.1 and earlier - @$dbc->Open((string) $argHostname); - - $this->_connectionID = $dbc; - - $dbc->CursorLocation = $this->_cursor_location; - return $dbc->State > 0; - } - - // returns true or false - function _pconnect($argHostname, $argUsername, $argPassword, $argProvider='MSDASQL') - { - return $this->_connect($argHostname,$argUsername,$argPassword,$argProvider); - } - -/* - adSchemaCatalogs = 1, - adSchemaCharacterSets = 2, - adSchemaCollations = 3, - adSchemaColumns = 4, - adSchemaCheckConstraints = 5, - adSchemaConstraintColumnUsage = 6, - adSchemaConstraintTableUsage = 7, - adSchemaKeyColumnUsage = 8, - adSchemaReferentialContraints = 9, - adSchemaTableConstraints = 10, - adSchemaColumnsDomainUsage = 11, - adSchemaIndexes = 12, - adSchemaColumnPrivileges = 13, - adSchemaTablePrivileges = 14, - adSchemaUsagePrivileges = 15, - adSchemaProcedures = 16, - adSchemaSchemata = 17, - adSchemaSQLLanguages = 18, - adSchemaStatistics = 19, - adSchemaTables = 20, - adSchemaTranslations = 21, - adSchemaProviderTypes = 22, - adSchemaViews = 23, - adSchemaViewColumnUsage = 24, - adSchemaViewTableUsage = 25, - adSchemaProcedureParameters = 26, - adSchemaForeignKeys = 27, - adSchemaPrimaryKeys = 28, - adSchemaProcedureColumns = 29, - adSchemaDBInfoKeywords = 30, - adSchemaDBInfoLiterals = 31, - adSchemaCubes = 32, - adSchemaDimensions = 33, - adSchemaHierarchies = 34, - adSchemaLevels = 35, - adSchemaMeasures = 36, - adSchemaProperties = 37, - adSchemaMembers = 38 - -*/ - - function MetaTables() - { - $arr= array(); - $dbc = $this->_connectionID; - - $adors=@$dbc->OpenSchema(20);//tables - if ($adors){ - $f = $adors->Fields(2);//table/view name - $t = $adors->Fields(3);//table type - while (!$adors->EOF){ - $tt=substr($t->value,0,6); - if ($tt!='SYSTEM' && $tt !='ACCESS') - $arr[]=$f->value; - //print $f->value . ' ' . $t->value.'
'; - $adors->MoveNext(); - } - $adors->Close(); - } - - return $arr; - } - - function MetaColumns($table) - { - $table = strtoupper($table); - $arr= array(); - $dbc = $this->_connectionID; - - $adors=@$dbc->OpenSchema(4);//tables - - if ($adors){ - $t = $adors->Fields(2);//table/view name - while (!$adors->EOF){ - - - if (strtoupper($t->Value) == $table) { - - $fld = new ADOFieldObject(); - $c = $adors->Fields(3); - $fld->name = $c->Value; - $fld->type = 'CHAR'; // cannot discover type in ADO! - $fld->max_length = -1; - $arr[strtoupper($fld->name)]=$fld; - } - - $adors->MoveNext(); - } - $adors->Close(); - } - - return $arr; - } - - /* returns queryID or false */ - function &_query($sql,$inputarr=false) - { - - $dbc = $this->_connectionID; - - // return rs - if ($inputarr) { - - if (!empty($this->charPage)) - $oCmd = new COM('ADODB.Command',null,$this->charPage); - else - $oCmd = new COM('ADODB.Command'); - $oCmd->ActiveConnection = $dbc; - $oCmd->CommandText = $sql; - $oCmd->CommandType = 1; - - foreach($inputarr as $val) { - // name, type, direction 1 = input, len, - $this->adoParameterType = 130; - $p = $oCmd->CreateParameter('name',$this->adoParameterType,1,strlen($val),$val); - //print $p->Type.' '.$p->value; - $oCmd->Parameters->Append($p); - } - $p = false; - $rs = $oCmd->Execute(); - $e = $dbc->Errors; - if ($dbc->Errors->Count > 0) return false; - return $rs; - } - - $rs = @$dbc->Execute($sql,$this->_affectedRows, $this->_execute_option); - /* - $rs = new COM('ADODB.Recordset'); - if ($rs) { - $rs->Open ($sql, $dbc, $this->_cursor_type,$this->_lock_type, $this->_execute_option); - } - */ - if ($dbc->Errors->Count > 0) return false; - if (! $rs) return false; - - if ($rs->State == 0) return true; // 0 = adStateClosed means no records returned - return $rs; - } - - - function BeginTrans() - { - if ($this->transOff) return true; - - if (isset($this->_thisTransactions)) - if (!$this->_thisTransactions) return false; - else { - $o = $this->_connectionID->Properties("Transaction DDL"); - $this->_thisTransactions = $o ? true : false; - if (!$o) return false; - } - @$this->_connectionID->BeginTrans(); - $this->transCnt += 1; - return true; - } - function CommitTrans($ok=true) - { - if (!$ok) return $this->RollbackTrans(); - if ($this->transOff) return true; - - @$this->_connectionID->CommitTrans(); - if ($this->transCnt) @$this->transCnt -= 1; - return true; - } - function RollbackTrans() { - if ($this->transOff) return true; - @$this->_connectionID->RollbackTrans(); - if ($this->transCnt) @$this->transCnt -= 1; - return true; - } - - /* Returns: the last error message from previous database operation */ - - function ErrorMsg() - { - $errc = $this->_connectionID->Errors; - if ($errc->Count == 0) return ''; - $err = $errc->Item($errc->Count-1); - return $err->Description; - } - - function ErrorNo() - { - $errc = $this->_connectionID->Errors; - if ($errc->Count == 0) return 0; - $err = $errc->Item($errc->Count-1); - return $err->NativeError; - } - - // returns true or false - function _close() - { - if ($this->_connectionID) $this->_connectionID->Close(); - $this->_connectionID = false; - return true; - } - - -} - -/*-------------------------------------------------------------------------------------- - Class Name: Recordset ---------------------------------------------------------------------------------------*/ - -class ADORecordSet_ado extends ADORecordSet { - - var $bind = false; - var $databaseType = "ado"; - var $dataProvider = "ado"; - var $_tarr = false; // caches the types - var $_flds; // and field objects - var $canSeek = true; - var $hideErrors = true; - - function ADORecordSet_ado($id,$mode=false) - { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - $this->fetchMode = $mode; - return $this->ADORecordSet($id,$mode); - } - - - // returns the field object - function FetchField($fieldOffset = -1) { - $off=$fieldOffset+1; // offsets begin at 1 - - $o= new ADOFieldObject(); - $rs = $this->_queryID; - $f = $rs->Fields($fieldOffset); - $o->name = $f->Name; - $t = $f->Type; - $o->type = $this->MetaType($t); - $o->max_length = $f->DefinedSize; - $o->ado_type = $t; - - - //print "off=$off name=$o->name type=$o->type len=$o->max_length
"; - return $o; - } - - /* Use associative array to get fields array */ - function Fields($colname) - { - if ($this->fetchMode & ADODB_FETCH_ASSOC) 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)]]; - } - - - function _initrs() - { - $rs = $this->_queryID; - $this->_numOfRows = $rs->RecordCount; - - $f = $rs->Fields; - $this->_numOfFields = $f->Count; - } - - - // should only be used to move forward as we normally use forward-only cursors - function _seek($row) - { - $rs = $this->_queryID; - // absoluteposition doesn't work -- my maths is wrong ? - // $rs->AbsolutePosition->$row-2; - // return true; - if ($this->_currentRow > $row) return false; - @$rs->Move((integer)$row - $this->_currentRow-1); //adBookmarkFirst - return true; - } - -/* - OLEDB types - - enum DBTYPEENUM - { DBTYPE_EMPTY = 0, - DBTYPE_NULL = 1, - DBTYPE_I2 = 2, - DBTYPE_I4 = 3, - DBTYPE_R4 = 4, - DBTYPE_R8 = 5, - DBTYPE_CY = 6, - DBTYPE_DATE = 7, - DBTYPE_BSTR = 8, - DBTYPE_IDISPATCH = 9, - DBTYPE_ERROR = 10, - DBTYPE_BOOL = 11, - DBTYPE_VARIANT = 12, - DBTYPE_IUNKNOWN = 13, - DBTYPE_DECIMAL = 14, - DBTYPE_UI1 = 17, - DBTYPE_ARRAY = 0x2000, - DBTYPE_BYREF = 0x4000, - DBTYPE_I1 = 16, - DBTYPE_UI2 = 18, - DBTYPE_UI4 = 19, - DBTYPE_I8 = 20, - DBTYPE_UI8 = 21, - DBTYPE_GUID = 72, - DBTYPE_VECTOR = 0x1000, - DBTYPE_RESERVED = 0x8000, - DBTYPE_BYTES = 128, - DBTYPE_STR = 129, - DBTYPE_WSTR = 130, - DBTYPE_NUMERIC = 131, - DBTYPE_UDT = 132, - DBTYPE_DBDATE = 133, - DBTYPE_DBTIME = 134, - DBTYPE_DBTIMESTAMP = 135 - - ADO Types - - adEmpty = 0, - adTinyInt = 16, - adSmallInt = 2, - adInteger = 3, - adBigInt = 20, - adUnsignedTinyInt = 17, - adUnsignedSmallInt = 18, - adUnsignedInt = 19, - adUnsignedBigInt = 21, - adSingle = 4, - adDouble = 5, - adCurrency = 6, - adDecimal = 14, - adNumeric = 131, - adBoolean = 11, - adError = 10, - adUserDefined = 132, - adVariant = 12, - adIDispatch = 9, - adIUnknown = 13, - adGUID = 72, - adDate = 7, - adDBDate = 133, - adDBTime = 134, - adDBTimeStamp = 135, - adBSTR = 8, - adChar = 129, - adVarChar = 200, - adLongVarChar = 201, - adWChar = 130, - adVarWChar = 202, - adLongVarWChar = 203, - adBinary = 128, - adVarBinary = 204, - adLongVarBinary = 205, - adChapter = 136, - adFileTime = 64, - adDBFileTime = 137, - adPropVariant = 138, - adVarNumeric = 139 -*/ - function MetaType($t,$len=-1,$fieldobj=false) - { - if (is_object($t)) { - $fieldobj = $t; - $t = $fieldobj->type; - $len = $fieldobj->max_length; - } - - if (!is_numeric($t)) return $t; - - switch ($t) { - case 0: - case 12: // variant - case 8: // bstr - case 129: //char - case 130: //wc - case 200: // varc - case 202:// varWC - case 128: // bin - case 204: // varBin - case 72: // guid - if ($len <= $this->blobSize) return 'C'; - - case 201: - case 203: - return 'X'; - case 128: - case 204: - case 205: - return 'B'; - case 7: - case 133: return 'D'; - - case 134: - case 135: return 'T'; - - case 11: return 'L'; - - case 16:// adTinyInt = 16, - case 2://adSmallInt = 2, - case 3://adInteger = 3, - case 4://adBigInt = 20, - case 17://adUnsignedTinyInt = 17, - case 18://adUnsignedSmallInt = 18, - case 19://adUnsignedInt = 19, - case 20://adUnsignedBigInt = 21, - return 'I'; - default: return 'N'; - } - } - - // time stamp not supported yet - function _fetch() - { - $rs = $this->_queryID; - if (!$rs or $rs->EOF) { - $this->fields = false; - return false; - } - $this->fields = array(); - - if (!$this->_tarr) { - $tarr = array(); - $flds = array(); - for ($i=0,$max = $this->_numOfFields; $i < $max; $i++) { - $f = $rs->Fields($i); - $flds[] = $f; - $tarr[] = $f->Type; - } - // bind types and flds only once - $this->_tarr = $tarr; - $this->_flds = $flds; - } - $t = reset($this->_tarr); - $f = reset($this->_flds); - - if ($this->hideErrors) $olde = error_reporting(E_ERROR|E_CORE_ERROR);// sometimes $f->value be null - for ($i=0,$max = $this->_numOfFields; $i < $max; $i++) { - - switch($t) { - case 135: // timestamp - $this->fields[] = date('Y-m-d H:i:s',(integer)$f->value); - break; - - case 133:// A date value (yyyymmdd) - $val = $f->value; - $this->fields[] = substr($val,0,4).'-'.substr($val,4,2).'-'.substr($val,6,2); - break; - case 7: // adDate - $this->fields[] = date('Y-m-d',(integer)$f->value); - break; - case 1: // null - $this->fields[] = false; - break; - case 6: // currency is not supported properly; - ADOConnection::outp( ''.$f->Name.': currency type not supported by PHP'); - $this->fields[] = (float) $f->value; - break; - default: - $this->fields[] = $f->value; - break; - } - //print " $f->value $t, "; - $f = next($this->_flds); - $t = next($this->_tarr); - } // for - if ($this->hideErrors) error_reporting($olde); - @$rs->MoveNext(); // @ needed for some versions of PHP! - - if ($this->fetchMode & ADODB_FETCH_ASSOC) { - $this->fields = $this->GetRowAssoc(ADODB_ASSOC_CASE); - } - return true; - } - - - function _close() { - $this->_flds = false; - @$this->_queryID->Close();// by Pete Dishman (peterd@telephonetics.co.uk) - $this->_queryID = false; - } - -} - +_affectedRows = new VARIANT; + } + + function ServerInfo() + { + if (!empty($this->_connectionID)) $desc = $this->_connectionID->provider; + return array('description' => $desc, 'version' => ''); + } + + function _affectedrows() + { + return $this->_affectedRows->value; + } + + /* you can also pass a connection string like this: */ + /* */ + /* $DB->Connect('USER ID=sa;PASSWORD=pwd;SERVER=mangrove;DATABASE=ai',false,false,'SQLOLEDB'); */ + function _connect($argHostname, $argUsername, $argPassword, $argProvider= 'MSDASQL') + { + $u = 'UID'; + $p = 'PWD'; + + if (!empty($this->charPage)) + $dbc = new COM('ADODB.Connection',null,$this->charPage); + else + $dbc = new COM('ADODB.Connection'); + + if (! $dbc) return false; + + /* special support if provider is mssql or access */ + if ($argProvider=='mssql') { + $u = 'User Id'; /* User parameter name for OLEDB */ + $p = 'Password'; + $argProvider = "SQLOLEDB"; /* SQL Server Provider */ + + /* not yet */ + /* if ($argDatabasename) $argHostname .= ";Initial Catalog=$argDatabasename"; */ + + /* use trusted conection for SQL if username not specified */ + if (!$argUsername) $argHostname .= ";Trusted_Connection=Yes"; + } else if ($argProvider=='access') + $argProvider = "Microsoft.Jet.OLEDB.4.0"; /* Microsoft Jet Provider */ + + if ($argProvider) $dbc->Provider = $argProvider; + + if ($argUsername) $argHostname .= ";$u=$argUsername"; + if ($argPassword)$argHostname .= ";$p=$argPassword"; + + if ($this->debug) ADOConnection::outp( "Host=".$argHostname."
\n version=$dbc->version"); + /* @ added below for php 4.0.1 and earlier */ + @$dbc->Open((string) $argHostname); + + $this->_connectionID = $dbc; + + $dbc->CursorLocation = $this->_cursor_location; + return $dbc->State > 0; + } + + /* returns true or false */ + function _pconnect($argHostname, $argUsername, $argPassword, $argProvider='MSDASQL') + { + return $this->_connect($argHostname,$argUsername,$argPassword,$argProvider); + } + +/* + adSchemaCatalogs = 1, + adSchemaCharacterSets = 2, + adSchemaCollations = 3, + adSchemaColumns = 4, + adSchemaCheckConstraints = 5, + adSchemaConstraintColumnUsage = 6, + adSchemaConstraintTableUsage = 7, + adSchemaKeyColumnUsage = 8, + adSchemaReferentialContraints = 9, + adSchemaTableConstraints = 10, + adSchemaColumnsDomainUsage = 11, + adSchemaIndexes = 12, + adSchemaColumnPrivileges = 13, + adSchemaTablePrivileges = 14, + adSchemaUsagePrivileges = 15, + adSchemaProcedures = 16, + adSchemaSchemata = 17, + adSchemaSQLLanguages = 18, + adSchemaStatistics = 19, + adSchemaTables = 20, + adSchemaTranslations = 21, + adSchemaProviderTypes = 22, + adSchemaViews = 23, + adSchemaViewColumnUsage = 24, + adSchemaViewTableUsage = 25, + adSchemaProcedureParameters = 26, + adSchemaForeignKeys = 27, + adSchemaPrimaryKeys = 28, + adSchemaProcedureColumns = 29, + adSchemaDBInfoKeywords = 30, + adSchemaDBInfoLiterals = 31, + adSchemaCubes = 32, + adSchemaDimensions = 33, + adSchemaHierarchies = 34, + adSchemaLevels = 35, + adSchemaMeasures = 36, + adSchemaProperties = 37, + adSchemaMembers = 38 + +*/ + + function &MetaTables() + { + $arr= array(); + $dbc = $this->_connectionID; + + $adors=@$dbc->OpenSchema(20);/* tables */ + if ($adors){ + $f = $adors->Fields(2);/* table/view name */ + $t = $adors->Fields(3);/* table type */ + while (!$adors->EOF){ + $tt=substr($t->value,0,6); + if ($tt!='SYSTEM' && $tt !='ACCESS') + $arr[]=$f->value; + /* print $f->value . ' ' . $t->value.'
'; */ + $adors->MoveNext(); + } + $adors->Close(); + } + + return $arr; + } + + function &MetaColumns($table) + { + $table = strtoupper($table); + $arr= array(); + $dbc = $this->_connectionID; + + $adors=@$dbc->OpenSchema(4);/* tables */ + + if ($adors){ + $t = $adors->Fields(2);/* table/view name */ + while (!$adors->EOF){ + + + if (strtoupper($t->Value) == $table) { + + $fld = new ADOFieldObject(); + $c = $adors->Fields(3); + $fld->name = $c->Value; + $fld->type = 'CHAR'; /* cannot discover type in ADO! */ + $fld->max_length = -1; + $arr[strtoupper($fld->name)]=$fld; + } + + $adors->MoveNext(); + } + $adors->Close(); + } + + return $arr; + } + + /* returns queryID or false */ + function &_query($sql,$inputarr=false) + { + + $dbc = $this->_connectionID; + + /* return rs */ + if ($inputarr) { + + if (!empty($this->charPage)) + $oCmd = new COM('ADODB.Command',null,$this->charPage); + else + $oCmd = new COM('ADODB.Command'); + $oCmd->ActiveConnection = $dbc; + $oCmd->CommandText = $sql; + $oCmd->CommandType = 1; + + foreach($inputarr as $val) { + /* name, type, direction 1 = input, len, */ + $this->adoParameterType = 130; + $p = $oCmd->CreateParameter('name',$this->adoParameterType,1,strlen($val),$val); + /* print $p->Type.' '.$p->value; */ + $oCmd->Parameters->Append($p); + } + $p = false; + $rs = $oCmd->Execute(); + $e = $dbc->Errors; + if ($dbc->Errors->Count > 0) return false; + return $rs; + } + + $rs = @$dbc->Execute($sql,$this->_affectedRows, $this->_execute_option); + /* + $rs = new COM('ADODB.Recordset'); + if ($rs) { + $rs->Open ($sql, $dbc, $this->_cursor_type,$this->_lock_type, $this->_execute_option); + } + */ + if ($dbc->Errors->Count > 0) return false; + if (! $rs) return false; + + if ($rs->State == 0) return true; /* 0 = adStateClosed means no records returned */ + return $rs; + } + + + function BeginTrans() + { + if ($this->transOff) return true; + + if (isset($this->_thisTransactions)) + if (!$this->_thisTransactions) return false; + else { + $o = $this->_connectionID->Properties("Transaction DDL"); + $this->_thisTransactions = $o ? true : false; + if (!$o) return false; + } + @$this->_connectionID->BeginTrans(); + $this->transCnt += 1; + return true; + } + function CommitTrans($ok=true) + { + if (!$ok) return $this->RollbackTrans(); + if ($this->transOff) return true; + + @$this->_connectionID->CommitTrans(); + if ($this->transCnt) @$this->transCnt -= 1; + return true; + } + function RollbackTrans() { + if ($this->transOff) return true; + @$this->_connectionID->RollbackTrans(); + if ($this->transCnt) @$this->transCnt -= 1; + return true; + } + + /* Returns: the last error message from previous database operation */ + + function ErrorMsg() + { + $errc = $this->_connectionID->Errors; + if ($errc->Count == 0) return ''; + $err = $errc->Item($errc->Count-1); + return $err->Description; + } + + function ErrorNo() + { + $errc = $this->_connectionID->Errors; + if ($errc->Count == 0) return 0; + $err = $errc->Item($errc->Count-1); + return $err->NativeError; + } + + /* returns true or false */ + function _close() + { + if ($this->_connectionID) $this->_connectionID->Close(); + $this->_connectionID = false; + return true; + } + + +} + +/*-------------------------------------------------------------------------------------- + Class Name: Recordset +--------------------------------------------------------------------------------------*/ + +class ADORecordSet_ado extends ADORecordSet { + + var $bind = false; + var $databaseType = "ado"; + var $dataProvider = "ado"; + var $_tarr = false; /* caches the types */ + var $_flds; /* and field objects */ + var $canSeek = true; + var $hideErrors = true; + + function ADORecordSet_ado($id,$mode=false) + { + if ($mode === false) { + global $ADODB_FETCH_MODE; + $mode = $ADODB_FETCH_MODE; + } + $this->fetchMode = $mode; + return $this->ADORecordSet($id,$mode); + } + + + /* returns the field object */ + function FetchField($fieldOffset = -1) { + $off=$fieldOffset+1; /* offsets begin at 1 */ + + $o= new ADOFieldObject(); + $rs = $this->_queryID; + $f = $rs->Fields($fieldOffset); + $o->name = $f->Name; + $t = $f->Type; + $o->type = $this->MetaType($t); + $o->max_length = $f->DefinedSize; + $o->ado_type = $t; + + + /* print "off=$off name=$o->name type=$o->type len=$o->max_length
"; */ + return $o; + } + + /* Use associative array to get fields array */ + function Fields($colname) + { + if ($this->fetchMode & ADODB_FETCH_ASSOC) 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)]]; + } + + + function _initrs() + { + $rs = $this->_queryID; + $this->_numOfRows = $rs->RecordCount; + + $f = $rs->Fields; + $this->_numOfFields = $f->Count; + } + + + /* should only be used to move forward as we normally use forward-only cursors */ + function _seek($row) + { + $rs = $this->_queryID; + /* absoluteposition doesn't work -- my maths is wrong ? */ + /* $rs->AbsolutePosition->$row-2; */ + /* return true; */ + if ($this->_currentRow > $row) return false; + @$rs->Move((integer)$row - $this->_currentRow-1); /* adBookmarkFirst */ + return true; + } + +/* + OLEDB types + + enum DBTYPEENUM + { DBTYPE_EMPTY = 0, + DBTYPE_NULL = 1, + DBTYPE_I2 = 2, + DBTYPE_I4 = 3, + DBTYPE_R4 = 4, + DBTYPE_R8 = 5, + DBTYPE_CY = 6, + DBTYPE_DATE = 7, + DBTYPE_BSTR = 8, + DBTYPE_IDISPATCH = 9, + DBTYPE_ERROR = 10, + DBTYPE_BOOL = 11, + DBTYPE_VARIANT = 12, + DBTYPE_IUNKNOWN = 13, + DBTYPE_DECIMAL = 14, + DBTYPE_UI1 = 17, + DBTYPE_ARRAY = 0x2000, + DBTYPE_BYREF = 0x4000, + DBTYPE_I1 = 16, + DBTYPE_UI2 = 18, + DBTYPE_UI4 = 19, + DBTYPE_I8 = 20, + DBTYPE_UI8 = 21, + DBTYPE_GUID = 72, + DBTYPE_VECTOR = 0x1000, + DBTYPE_RESERVED = 0x8000, + DBTYPE_BYTES = 128, + DBTYPE_STR = 129, + DBTYPE_WSTR = 130, + DBTYPE_NUMERIC = 131, + DBTYPE_UDT = 132, + DBTYPE_DBDATE = 133, + DBTYPE_DBTIME = 134, + DBTYPE_DBTIMESTAMP = 135 + + ADO Types + + adEmpty = 0, + adTinyInt = 16, + adSmallInt = 2, + adInteger = 3, + adBigInt = 20, + adUnsignedTinyInt = 17, + adUnsignedSmallInt = 18, + adUnsignedInt = 19, + adUnsignedBigInt = 21, + adSingle = 4, + adDouble = 5, + adCurrency = 6, + adDecimal = 14, + adNumeric = 131, + adBoolean = 11, + adError = 10, + adUserDefined = 132, + adVariant = 12, + adIDispatch = 9, + adIUnknown = 13, + adGUID = 72, + adDate = 7, + adDBDate = 133, + adDBTime = 134, + adDBTimeStamp = 135, + adBSTR = 8, + adChar = 129, + adVarChar = 200, + adLongVarChar = 201, + adWChar = 130, + adVarWChar = 202, + adLongVarWChar = 203, + adBinary = 128, + adVarBinary = 204, + adLongVarBinary = 205, + adChapter = 136, + adFileTime = 64, + adDBFileTime = 137, + adPropVariant = 138, + adVarNumeric = 139 +*/ + function MetaType($t,$len=-1,$fieldobj=false) + { + if (is_object($t)) { + $fieldobj = $t; + $t = $fieldobj->type; + $len = $fieldobj->max_length; + } + + if (!is_numeric($t)) return $t; + + switch ($t) { + case 0: + case 12: /* variant */ + case 8: /* bstr */ + case 129: /* char */ + case 130: /* wc */ + case 200: /* varc */ + case 202:/* varWC */ + case 128: /* bin */ + case 204: /* varBin */ + case 72: /* guid */ + if ($len <= $this->blobSize) return 'C'; + + case 201: + case 203: + return 'X'; + case 128: + case 204: + case 205: + return 'B'; + case 7: + case 133: return 'D'; + + case 134: + case 135: return 'T'; + + case 11: return 'L'; + + case 16:/* adTinyInt = 16, */ + case 2:/* adSmallInt = 2, */ + case 3:/* adInteger = 3, */ + case 4:/* adBigInt = 20, */ + case 17:/* adUnsignedTinyInt = 17, */ + case 18:/* adUnsignedSmallInt = 18, */ + case 19:/* adUnsignedInt = 19, */ + case 20:/* adUnsignedBigInt = 21, */ + return 'I'; + default: return 'N'; + } + } + + /* time stamp not supported yet */ + function _fetch() + { + $rs = $this->_queryID; + if (!$rs or $rs->EOF) { + $this->fields = false; + return false; + } + $this->fields = array(); + + if (!$this->_tarr) { + $tarr = array(); + $flds = array(); + for ($i=0,$max = $this->_numOfFields; $i < $max; $i++) { + $f = $rs->Fields($i); + $flds[] = $f; + $tarr[] = $f->Type; + } + /* bind types and flds only once */ + $this->_tarr = $tarr; + $this->_flds = $flds; + } + $t = reset($this->_tarr); + $f = reset($this->_flds); + + if ($this->hideErrors) $olde = error_reporting(E_ERROR|E_CORE_ERROR);/* sometimes $f->value be null */ + for ($i=0,$max = $this->_numOfFields; $i < $max; $i++) { + + switch($t) { + case 135: /* timestamp */ + $this->fields[] = date('Y-m-d H:i:s',(integer)$f->value); + break; + + case 133:/* A date value (yyyymmdd) */ + $val = $f->value; + $this->fields[] = substr($val,0,4).'-'.substr($val,4,2).'-'.substr($val,6,2); + break; + case 7: /* adDate */ + $this->fields[] = date('Y-m-d',(integer)$f->value); + break; + case 1: /* null */ + $this->fields[] = false; + break; + case 6: /* currency is not supported properly; */ + ADOConnection::outp( ''.$f->Name.': currency type not supported by PHP'); + $this->fields[] = (float) $f->value; + break; + default: + $this->fields[] = $f->value; + break; + } + /* print " $f->value $t, "; */ + $f = next($this->_flds); + $t = next($this->_tarr); + } /* for */ + if ($this->hideErrors) error_reporting($olde); + @$rs->MoveNext(); /* @ needed for some versions of PHP! */ + + if ($this->fetchMode & ADODB_FETCH_ASSOC) { + $this->fields = $this->GetRowAssoc(ADODB_ASSOC_CASE); + } + return true; + } + + + function _close() { + $this->_flds = false; + @$this->_queryID->Close();/* by Pete Dishman (peterd@telephonetics.co.uk) */ + $this->_queryID = false; + } + +} + ?> \ No newline at end of file diff --git a/lib/adodb/drivers/adodb-ado_access.inc.php b/lib/adodb/drivers/adodb-ado_access.inc.php index c3748e5e99..53bb5c295a 100644 --- a/lib/adodb/drivers/adodb-ado_access.inc.php +++ b/lib/adodb/drivers/adodb-ado_access.inc.php @@ -1,46 +1,46 @@ -ADODB_ado(); - } - - function BeginTrans() { return false;} - -} - - -class ADORecordSet_ado_access extends ADORecordSet_ado { - - var $databaseType = "ado_access"; - - function ADORecordSet_ado_access($id,$mode=false) - { - return $this->ADORecordSet_ado($id,$mode); - } -} +ADODB_ado(); + } + + function BeginTrans() { return false;} + +} + + +class ADORecordSet_ado_access extends ADORecordSet_ado { + + var $databaseType = "ado_access"; + + function ADORecordSet_ado_access($id,$mode=false) + { + return $this->ADORecordSet_ado($id,$mode); + } +} ?> \ No newline at end of file diff --git a/lib/adodb/drivers/adodb-ado_mssql.inc.php b/lib/adodb/drivers/adodb-ado_mssql.inc.php index bf295892ee..ae6fa20d3e 100644 --- a/lib/adodb/drivers/adodb-ado_mssql.inc.php +++ b/lib/adodb/drivers/adodb-ado_mssql.inc.php @@ -1,59 +1,59 @@ -ADODB_ado(); - } - - function _insertid() - { - return $this->GetOne('select @@identity'); - } - - function _affectedrows() - { - return $this->GetOne('select @@rowcount'); - } - -} - -class ADORecordSet_ado_mssql extends ADORecordSet_ado { - - var $databaseType = 'ado_mssql'; - - function ADORecordSet_ado_mssql($id,$mode=false) - { - return $this->ADORecordSet_ado($id,$mode); - } -} +ADODB_ado(); + } + + function _insertid() + { + return $this->GetOne('select @@identity'); + } + + function _affectedrows() + { + return $this->GetOne('select @@rowcount'); + } + +} + +class ADORecordSet_ado_mssql extends ADORecordSet_ado { + + var $databaseType = 'ado_mssql'; + + function ADORecordSet_ado_mssql($id,$mode=false) + { + return $this->ADORecordSet_ado($id,$mode); + } +} ?> \ No newline at end of file diff --git a/lib/adodb/drivers/adodb-borland_ibase.inc.php b/lib/adodb/drivers/adodb-borland_ibase.inc.php index 05a4915873..da95a55af6 100644 --- a/lib/adodb/drivers/adodb-borland_ibase.inc.php +++ b/lib/adodb/drivers/adodb-borland_ibase.inc.php @@ -1,79 +1,79 @@ -ADODB_ibase(); - } - - function ServerInfo() - { - $arr['dialect'] = $this->dialect; - switch($arr['dialect']) { - case '': - case '1': $s = 'Interbase 6.5, Dialect 1'; break; - case '2': $s = 'Interbase 6.5, Dialect 2'; break; - default: - case '3': $s = 'Interbase 6.5, Dialect 3'; break; - } - $arr['version'] = '6.5'; - $arr['description'] = $s; - return $arr; - } - - // Note that Interbase 6.5 uses ROWS instead - don't you love forking wars! - // SELECT col1, col2 FROM table ROWS 5 -- get 5 rows - // SELECT col1, col2 FROM TABLE ORDER BY col1 ROWS 3 TO 7 -- first 5 skip 2 - // Firebird uses - // SELECT FIRST 5 SKIP 2 col1, col2 FROM TABLE - function &SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false, $arg3=false,$secs2cache=0) - { - if ($nrows > 0) { - if ($offset <= 0) $str = " ROWS $nrows "; - else { - $a = $offset+1; - $b = $offset+$nrows; - $str = " ROWS $a TO $b"; - } - } else { - // ok, skip - $a = $offset + 1; - $str = " ROWS $a TO 999999999"; // 999 million - } - $sql .= $str; - - return ($secs2cache) ? - $this->CacheExecute($secs2cache,$sql,$inputarr,$arg3) - : - $this->Execute($sql,$inputarr,$arg3); - } - -}; - - -class ADORecordSet_borland_ibase extends ADORecordSet_ibase { - - var $databaseType = "borland_ibase"; - - function ADORecordSet_borland_ibase($id,$mode=false) - { - $this->ADORecordSet_ibase($id,$mode); - } -} +ADODB_ibase(); + } + + function ServerInfo() + { + $arr['dialect'] = $this->dialect; + switch($arr['dialect']) { + case '': + case '1': $s = 'Interbase 6.5, Dialect 1'; break; + case '2': $s = 'Interbase 6.5, Dialect 2'; break; + default: + case '3': $s = 'Interbase 6.5, Dialect 3'; break; + } + $arr['version'] = '6.5'; + $arr['description'] = $s; + return $arr; + } + + /* Note that Interbase 6.5 uses ROWS instead - don't you love forking wars! */ + /* SELECT col1, col2 FROM table ROWS 5 -- get 5 rows */ + /* SELECT col1, col2 FROM TABLE ORDER BY col1 ROWS 3 TO 7 -- first 5 skip 2 */ + /* Firebird uses */ + /* SELECT FIRST 5 SKIP 2 col1, col2 FROM TABLE */ + function &SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false, $arg3=false,$secs2cache=0) + { + if ($nrows > 0) { + if ($offset <= 0) $str = " ROWS $nrows "; + else { + $a = $offset+1; + $b = $offset+$nrows; + $str = " ROWS $a TO $b"; + } + } else { + /* ok, skip */ + $a = $offset + 1; + $str = " ROWS $a TO 999999999"; /* 999 million */ + } + $sql .= $str; + + return ($secs2cache) ? + $this->CacheExecute($secs2cache,$sql,$inputarr,$arg3) + : + $this->Execute($sql,$inputarr,$arg3); + } + +}; + + +class ADORecordSet_borland_ibase extends ADORecordSet_ibase { + + var $databaseType = "borland_ibase"; + + function ADORecordSet_borland_ibase($id,$mode=false) + { + $this->ADORecordSet_ibase($id,$mode); + } +} ?> \ No newline at end of file diff --git a/lib/adodb/drivers/adodb-csv.inc.php b/lib/adodb/drivers/adodb-csv.inc.php index 7924084527..921be0db5c 100644 --- a/lib/adodb/drivers/adodb-csv.inc.php +++ b/lib/adodb/drivers/adodb-csv.inc.php @@ -1,202 +1,202 @@ -_insertid; - } - - function _affectedrows() - { - return $this->_affectedrows; - } - - function &MetaDatabases() - { - return false; - } - - - // returns true or false - function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) - { - if (strtolower(substr($argHostname,0,7)) !== 'http://') return false; - $this->_url = $argHostname; - return true; - } - - // returns true or false - function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) - { - if (strtolower(substr($argHostname,0,7)) !== 'http://') return false; - $this->_url = $argHostname; - return true; - } - - function &MetaColumns($table) - { - return false; - } - - - // parameters use PostgreSQL convention, not MySQL - function &SelectLimit($sql,$nrows=-1,$offset=-1,$arg3=false) - { - global $ADODB_FETCH_MODE; - - $url = $this->_url.'?sql='.urlencode($sql)."&nrows=$nrows&fetch=". - (($this->fetchMode !== false)?$this->fetchMode : $ADODB_FETCH_MODE). - "&offset=$offset&arg3=".urlencode($arg3); - $err = false; - $rs = csv2rs($url,$err,false); - - if ($this->debug) print "$url
$err
"; - - $at = strpos($err,'::::'); - if ($at === false) { - $this->_errorMsg = $err; - $this->_errorNo = (integer)$err; - } else { - $this->_errorMsg = substr($err,$at+4,1024); - $this->_errorNo = -9999; - } - if ($this->_errorNo) - if ($fn = $this->raiseErrorFn) { - $fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,''); - } - - if (is_object($rs)) { - - $rs->databaseType='csv'; - $rs->fetchMode = ($this->fetchMode !== false) ? $this->fetchMode : $ADODB_FETCH_MODE; - $rs->connection = &$this; - } - return $rs; - } - - // returns queryID or false - function &Execute($sql,$inputarr=false,$arg3=false) - { - global $ADODB_FETCH_MODE; - - if (!$this->_bindInputArray && $inputarr) { - $sqlarr = explode('?',$sql); - $sql = ''; - $i = 0; - foreach($inputarr as $v) { - - $sql .= $sqlarr[$i]; - // from Ron Baldwin- // Only quote string types - if (gettype($v) == 'string') - $sql .= $this->qstr($v); - else if ($v === null) - $sql .= 'NULL'; - else - $sql .= $v; - $i += 1; - - } - $sql .= $sqlarr[$i]; - if ($i+1 != sizeof($sqlarr)) - print "Input Array does not match ?: ".htmlspecialchars($sql); - $inputarr = false; - } - - $url = $this->_url.'?sql='.urlencode($sql)."&fetch=". - (($this->fetchMode !== false)?$this->fetchMode : $ADODB_FETCH_MODE); - if ($arg3) $url .= "&arg3=".urlencode($arg3); - $err = false; - - $rs = csv2rs($url,$err,false); - if ($this->debug) print urldecode($url)."
$err
"; - $at = strpos($err,'::::'); - if ($at === false) { - $this->_errorMsg = $err; - $this->_errorNo = (integer)$err; - } else { - $this->_errorMsg = substr($err,$at+4,1024); - $this->_errorNo = -9999; - } - - if ($this->_errorNo) - if ($fn = $this->raiseErrorFn) { - $fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,$inputarr); - } - if (is_object($rs)) { - $rs->fetchMode = ($this->fetchMode !== false) ? $this->fetchMode : $ADODB_FETCH_MODE; - - $this->_affectedrows = $rs->affectedrows; - $this->_insertid = $rs->insertid; - $rs->databaseType='csv'; - $rs->connection = &$this; - } - return $rs; - } - - /* Returns: the last error message from previous database operation */ - function ErrorMsg() - { - return $this->_errorMsg; - } - - /* Returns: the last error number from previous database operation */ - function ErrorNo() - { - return $this->_errorNo; - } - - // returns true or false - function _close() - { - return true; - } -} // class - -class ADORecordset_csv extends ADORecordset { - function ADORecordset_csv($id,$mode=false) - { - $this->ADORecordset($id,$mode); - } - - function _close() - { - return true; - } -} - -} // define - +_insertid; + } + + function _affectedrows() + { + return $this->_affectedrows; + } + + function &MetaDatabases() + { + return false; + } + + + /* returns true or false */ + function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + if (strtolower(substr($argHostname,0,7)) !== 'http:/* ') return false; */ + $this->_url = $argHostname; + return true; + } + + /* returns true or false */ + function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + if (strtolower(substr($argHostname,0,7)) !== 'http:/* ') return false; */ + $this->_url = $argHostname; + return true; + } + + function &MetaColumns($table) + { + return false; + } + + + /* parameters use PostgreSQL convention, not MySQL */ + function &SelectLimit($sql,$nrows=-1,$offset=-1,$arg3=false) + { + global $ADODB_FETCH_MODE; + + $url = $this->_url.'?sql='.urlencode($sql)."&nrows=$nrows&fetch=". + (($this->fetchMode !== false)?$this->fetchMode : $ADODB_FETCH_MODE). + "&offset=$offset&arg3=".urlencode($arg3); + $err = false; + $rs = csv2rs($url,$err,false); + + if ($this->debug) print "$url
$err
"; + + $at = strpos($err,'::::'); + if ($at === false) { + $this->_errorMsg = $err; + $this->_errorNo = (integer)$err; + } else { + $this->_errorMsg = substr($err,$at+4,1024); + $this->_errorNo = -9999; + } + if ($this->_errorNo) + if ($fn = $this->raiseErrorFn) { + $fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,''); + } + + if (is_object($rs)) { + + $rs->databaseType='csv'; + $rs->fetchMode = ($this->fetchMode !== false) ? $this->fetchMode : $ADODB_FETCH_MODE; + $rs->connection = &$this; + } + return $rs; + } + + /* returns queryID or false */ + function &Execute($sql,$inputarr=false,$arg3=false) + { + global $ADODB_FETCH_MODE; + + if (!$this->_bindInputArray && $inputarr) { + $sqlarr = explode('?',$sql); + $sql = ''; + $i = 0; + foreach($inputarr as $v) { + + $sql .= $sqlarr[$i]; + /* from Ron Baldwin*/ + /* Only quote string types */ + if (gettype($v) == 'string') + $sql .= $this->qstr($v); + else if ($v === null) + $sql .= 'NULL'; + else + $sql .= $v; + $i += 1; + + } + $sql .= $sqlarr[$i]; + if ($i+1 != sizeof($sqlarr)) + print "Input Array does not match ?: ".htmlspecialchars($sql); + $inputarr = false; + } + + $url = $this->_url.'?sql='.urlencode($sql)."&fetch=". + (($this->fetchMode !== false)?$this->fetchMode : $ADODB_FETCH_MODE); + if ($arg3) $url .= "&arg3=".urlencode($arg3); + $err = false; + + $rs = csv2rs($url,$err,false); + if ($this->debug) print urldecode($url)."
$err
"; + $at = strpos($err,'::::'); + if ($at === false) { + $this->_errorMsg = $err; + $this->_errorNo = (integer)$err; + } else { + $this->_errorMsg = substr($err,$at+4,1024); + $this->_errorNo = -9999; + } + + if ($this->_errorNo) + if ($fn = $this->raiseErrorFn) { + $fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,$inputarr); + } + if (is_object($rs)) { + $rs->fetchMode = ($this->fetchMode !== false) ? $this->fetchMode : $ADODB_FETCH_MODE; + + $this->_affectedrows = $rs->affectedrows; + $this->_insertid = $rs->insertid; + $rs->databaseType='csv'; + $rs->connection = &$this; + } + return $rs; + } + + /* Returns: the last error message from previous database operation */ + function ErrorMsg() + { + return $this->_errorMsg; + } + + /* Returns: the last error number from previous database operation */ + function ErrorNo() + { + return $this->_errorNo; + } + + /* returns true or false */ + function _close() + { + return true; + } +} /* class */ + +class ADORecordset_csv extends ADORecordset { + function ADORecordset_csv($id,$mode=false) + { + $this->ADORecordset($id,$mode); + } + + function _close() + { + return true; + } +} + +} /* define */ + ?> \ No newline at end of file diff --git a/lib/adodb/drivers/adodb-db2.inc.php b/lib/adodb/drivers/adodb-db2.inc.php index 2ba16955af..4d000f2fe9 100644 --- a/lib/adodb/drivers/adodb-db2.inc.php +++ b/lib/adodb/drivers/adodb-db2.inc.php @@ -1,196 +1,265 @@ -curMode = SQL_CUR_USE_ODBC; -$db->Connect($dsn, $userid, $pwd); - -*/ - -if (!defined('_ADODB_ODBC_LAYER')) { - include(ADODB_DIR."/drivers/adodb-odbc.inc.php"); -} -if (!defined('ADODB_DB2')){ -define('ADODB_DB2',1); - -class ADODB_DB2 extends ADODB_odbc { - var $databaseType = "db2"; - var $concat_operator = 'CONCAT'; - var $sysDate = 'CURRENT DATE'; - var $sysTimeStamp = 'CURRENT TIMESTAMP'; - var $ansiOuter = true; - //var $curmode = SQL_CUR_USE_ODBC; - - function ADODB_DB2() - { - $this->ADODB_odbc(); - } - - function RowLock($tables,$where) - { - if ($this->_autocommit) $this->BeginTrans(); - return $this->GetOne("select 1 as ignore from $tables where $where for update"); - } - - // Format date column in sql string given an input format that understands Y M D - function SQLDate($fmt, $col=false) - { - // use right() and replace() ? - if (!$col) $col = $this->sysDate; - $s = ''; - - $len = strlen($fmt); - for ($i=0; $i < $len; $i++) { - if ($s) $s .= '+'; - $ch = $fmt[$i]; - switch($ch) { - case 'Y': - case 'y': - $s .= "char(year($col))"; - break; - case 'M': - case 'm': - $s .= "right(digits(month($col)),2)"; - break; - case 'D': - case 'd': - $s .= "right(digits(day($col)),2)"; - break; - default: - $s .= $this->qstr($ch); - } - } - return $s; - } - - - function &SelectLimit($sql,$nrows=-1,$offset=-1,$arg3=false) - { - if ($offset <= 0) { - // could also use " OPTIMIZE FOR $nrows ROWS " - $sql .= " FETCH FIRST $nrows ROWS ONLY "; - return $this->Execute($sql,false,$arg3); - } else { - $nrows += $offset; - $sql .= " FETCH FIRST $nrows ROWS ONLY "; - return ADOConnection::SelectLimit($sql,-1,$offset,$arg3); - } - } - -}; - - -class ADORecordSet_db2 extends ADORecordSet_odbc { - - var $databaseType = "db2"; - - function ADORecordSet_db2($id,$mode=false) - { - $this->ADORecordSet_odbc($id,$mode); - } - - function MetaType($t,$len=-1,$fieldobj=false) - { - switch (strtoupper($t)) { - case 'VARCHAR': - case 'CHAR': - case 'CHARACTER': - if ($len <= $this->blobSize) return 'C'; - - case 'LONGCHAR': - case 'TEXT': - case 'CLOB': - case 'DBCLOB': // double-byte - return 'X'; - - case 'BLOB': - case 'GRAPHIC': - case 'VARGRAPHIC': - return 'B'; - - case 'DATE': - return 'D'; - - case 'TIME': - case 'TIMESTAMP': - return 'T'; - - //case 'BOOLEAN': - //case 'BIT': - // return 'L'; - - //case 'COUNTER': - // return 'R'; - - case 'INT': - case 'INTEGER': - case 'BIGINT': - case 'SMALLINT': - return 'I'; - - default: return 'N'; - } - } -} - -} //define +curMode = SQL_CUR_USE_ODBC; +$db->Connect($dsn, $userid, $pwd); + +*/ + +if (!defined('_ADODB_ODBC_LAYER')) { + include(ADODB_DIR."/drivers/adodb-odbc.inc.php"); +} +if (!defined('ADODB_DB2')){ +define('ADODB_DB2',1); + +class ADODB_DB2 extends ADODB_odbc { + var $databaseType = "db2"; + var $concat_operator = '||'; + var $sysDate = 'CURRENT_DATE'; + var $sysTimeStamp = 'CURRENT TIMESTAMP'; + var $ansiOuter = true; + var $identitySQL = 'values IDENTITY_VAL_LOCAL()'; + + function ADODB_DB2() + { + if (strpos(PHP_OS,'WIN') !== false) $this->curmode = SQL_CUR_USE_ODBC; + $this->ADODB_odbc(); + } + + function ServerInfo() + { + /* odbc_setoption($this->_connectionID,1,101 /*SQL_ATTR_ACCESS_MODE*/, 1 /*SQL_MODE_READ_ONLY*/); */ + $vers = $this->GetOne('select versionnumber from sysibm.sysversions'); + /* odbc_setoption($this->_connectionID,1,101, 0 /*SQL_MODE_READ_WRITE*/); */ + return array('description'=>'DB2 ODBC driver', 'version'=>$vers); + } + + function _insertid() + { + return $this->GetOne($this->identitySQL); + } + + function RowLock($tables,$where) + { + if ($this->_autocommit) $this->BeginTrans(); + return $this->GetOne("select 1 as ignore from $tables where $where for update"); + } + + function &MetaTables($showSchema=false) + { + global $ADODB_FETCH_MODE; + + $savem = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + $qid = odbc_tables($this->_connectionID); + + $rs = new ADORecordSet_odbc($qid); + + $ADODB_FETCH_MODE = $savem; + if (!$rs) return false; + + $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change; + + /* print_r($rs); */ + $arr =& $rs->GetArray(); + $rs->Close(); + $arr2 = array(); + /* print_r($arr); */ + for ($i=0; $i < sizeof($arr); $i++) { + $row = $arr[$i]; + if ($row[2] && strncmp($row[1],'SYS',3) != 0) + if ($showSchema) $arr2[] = $row[1].'.'.$row[2]; + else $arr2[] = $row[2]; + } + return $arr2; + } + + /* Format date column in sql string given an input format that understands Y M D */ + function SQLDate($fmt, $col=false) + { + /* use right() and replace() ? */ + if (!$col) $col = $this->sysDate; + $s = ''; + + $len = strlen($fmt); + for ($i=0; $i < $len; $i++) { + if ($s) $s .= '||'; + $ch = $fmt[$i]; + switch($ch) { + case 'Y': + case 'y': + $s .= "char(year($col))"; + break; + case 'M': + $s .= "substr(monthname($col),1,3)"; + break; + case 'm': + $s .= "right(digits(month($col)),2)"; + break; + case 'D': + case 'd': + $s .= "right(digits(day($col)),2)"; + break; + case 'H': + case 'h': + if ($col != $this->sysDate) $s .= "right(digits(hour($col)),2)"; + else $s .= "''"; + break; + case 'i': + case 'I': + if ($col != $this->sysDate) + $s .= "right(digits(minute($col)),2)"; + else $s .= "''"; + break; + case 'S': + case 's': + if ($col != $this->sysDate) + $s .= "right(digits(second($col)),2)"; + else $s .= "''"; + break; + default: + if ($ch == '\\') { + $i++; + $ch = substr($fmt,$i,1); + } + $s .= $this->qstr($ch); + } + } + return $s; + } + + + function &SelectLimit($sql,$nrows=-1,$offset=-1,$arg3=false) + { + if ($offset <= 0) { + /* could also use " OPTIMIZE FOR $nrows ROWS " */ + if ($nrows >= 0) $sql .= " FETCH FIRST $nrows ROWS ONLY "; + return $this->Execute($sql,false,$arg3); + } else { + if ($offset > 0 && $nrows < 0); + else { + $nrows += $offset; + $sql .= " FETCH FIRST $nrows ROWS ONLY "; + } + return ADOConnection::SelectLimit($sql,-1,$offset,$arg3); + } + } + +}; + + +class ADORecordSet_db2 extends ADORecordSet_odbc { + + var $databaseType = "db2"; + + function ADORecordSet_db2($id,$mode=false) + { + $this->ADORecordSet_odbc($id,$mode); + } + + function MetaType($t,$len=-1,$fieldobj=false) + { + switch (strtoupper($t)) { + case 'VARCHAR': + case 'CHAR': + case 'CHARACTER': + if ($len <= $this->blobSize) return 'C'; + + case 'LONGCHAR': + case 'TEXT': + case 'CLOB': + case 'DBCLOB': /* double-byte */ + return 'X'; + + case 'BLOB': + case 'GRAPHIC': + case 'VARGRAPHIC': + return 'B'; + + case 'DATE': + return 'D'; + + case 'TIME': + case 'TIMESTAMP': + return 'T'; + + /* case 'BOOLEAN': */ + /* case 'BIT': */ + /* return 'L'; */ + + /* case 'COUNTER': */ + /* return 'R'; */ + + case 'INT': + case 'INTEGER': + case 'BIGINT': + case 'SMALLINT': + return 'I'; + + default: return 'N'; + } + } +} + +} /* define */ ?> \ No newline at end of file diff --git a/lib/adodb/drivers/adodb-fbsql.inc.php b/lib/adodb/drivers/adodb-fbsql.inc.php index 9affce5671..b4a30b469e 100644 --- a/lib/adodb/drivers/adodb-fbsql.inc.php +++ b/lib/adodb/drivers/adodb-fbsql.inc.php @@ -1,262 +1,262 @@ -. - Set tabs to 8. -*/ - -if (! defined("_ADODB_FBSQL_LAYER")) { - define("_ADODB_FBSQL_LAYER", 1 ); - -class ADODB_fbsql extends ADOConnection { - var $databaseType = 'fbsql'; - var $hasInsertID = true; - var $hasAffectedRows = true; - var $metaTablesSQL = "SHOW TABLES"; - var $metaColumnsSQL = "SHOW COLUMNS FROM %s"; - var $fmtTimeStamp = "'Y-m-d H:i:s'"; - var $hasLimit = false; - - function ADODB_fbsql() - { - } - - function _insertid() - { - return fbsql_insert_id($this->_connectionID); - } - - function _affectedrows() - { - return fbsql_affected_rows($this->_connectionID); - } - - function &MetaDatabases() - { - $qid = fbsql_list_dbs($this->_connectionID); - $arr = array(); - $i = 0; - $max = fbsql_num_rows($qid); - while ($i < $max) { - $arr[] = fbsql_tablename($qid,$i); - $i += 1; - } - return $arr; - } - - // returns concatenated string - function Concat() - { - $s = ""; - $arr = func_get_args(); - $first = true; - - $s = implode(',',$arr); - if (sizeof($arr) > 0) return "CONCAT($s)"; - else return ''; - } - - // returns true or false - function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) - { - $this->_connectionID = fbsql_connect($argHostname,$argUsername,$argPassword); - if ($this->_connectionID === false) return false; - if ($argDatabasename) return $this->SelectDB($argDatabasename); - return true; - } - - // returns true or false - function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) - { - $this->_connectionID = fbsql_pconnect($argHostname,$argUsername,$argPassword); - if ($this->_connectionID === false) return false; - if ($argDatabasename) return $this->SelectDB($argDatabasename); - return true; - } - - function &MetaColumns($table) - { - if ($this->metaColumnsSQL) { - - $rs = $this->Execute(sprintf($this->metaColumnsSQL,$table)); - - if ($rs === false) return false; - - $retarr = array(); - while (!$rs->EOF){ - $fld = new ADOFieldObject(); - $fld->name = $rs->fields[0]; - $fld->type = $rs->fields[1]; - - // split type into type(length): - if (preg_match("/^(.+)\((\d+)\)$/", $fld->type, $query_array)) { - $fld->type = $query_array[1]; - $fld->max_length = $query_array[2]; - } else { - $fld->max_length = -1; - } - $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($fld->type,'blob') !== false); - - $retarr[strtoupper($fld->name)] = $fld; - $rs->MoveNext(); - } - $rs->Close(); - return $retarr; - } - return false; - } - - // returns true or false - function SelectDB($dbName) - { - $this->databaseName = $dbName; - if ($this->_connectionID) { - return @fbsql_select_db($dbName,$this->_connectionID); - } - else return false; - } - - - // returns queryID or false - function _query($sql,$inputarr) - { - return fbsql_query("$sql;",$this->_connectionID); - } - - /* Returns: the last error message from previous database operation */ - function ErrorMsg() - { - $this->_errorMsg = @fbsql_error($this->_connectionID); - return $this->_errorMsg; - } - - /* Returns: the last error number from previous database operation */ - function ErrorNo() - { - return @fbsql_errno($this->_connectionID); - } - - // returns true or false - function _close() - { - return @fbsql_close($this->_connectionID); - } - -} - -/*-------------------------------------------------------------------------------------- - Class Name: Recordset ---------------------------------------------------------------------------------------*/ - -class ADORecordSet_fbsql extends ADORecordSet{ - - var $databaseType = "fbsql"; - var $canSeek = true; - - function ADORecordSet_fbsql($queryID,$mode=false) - { - if (!$mode) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - switch ($mode) { - case ADODB_FETCH_NUM: $this->fetchMode = FBSQL_NUM; break; - default: - case ADODB_FETCH_BOTH: $this->fetchMode = FBSQL_BOTH; break; - case ADODB_FETCH_ASSOC: $this->fetchMode = FBSQL_ASSOC; break; - } - return $this->ADORecordSet($queryID); - } - - function _initrs() - { - GLOBAL $ADODB_COUNTRECS; - $this->_numOfRows = ($ADODB_COUNTRECS) ? @fbsql_num_rows($this->_queryID):-1; - $this->_numOfFields = @fbsql_num_fields($this->_queryID); - } - - - - function &FetchField($fieldOffset = -1) { - if ($fieldOffset != -1) { - $o = @fbsql_fetch_field($this->_queryID, $fieldOffset); - //$o->max_length = -1; // fbsql returns the max length less spaces -- so it is unrealiable - $f = @fbsql_field_flags($this->_queryID,$fieldOffset); - $o->binary = (strpos($f,'binary')!== false); - } - else if ($fieldOffset == -1) { /* The $fieldOffset argument is not provided thus its -1 */ - $o = @fbsql_fetch_field($this->_queryID);// fbsql returns the max length less spaces -- so it is unrealiable - //$o->max_length = -1; - } - - return $o; - } - - function _seek($row) - { - return @fbsql_data_seek($this->_queryID,$row); - } - - function _fetch($ignore_fields=false) - { - $this->fields = @fbsql_fetch_array($this->_queryID,$this->fetchMode); - return ($this->fields == true); - } - - function _close() { - return @fbsql_free_result($this->_queryID); - } - - function MetaType($t,$len=-1,$fieldobj=false) - { - if (is_object($t)) { - $fieldobj = $t; - $t = $fieldobj->type; - $len = $fieldobj->max_length; - } - $len = -1; // fbsql max_length is not accurate - switch (strtoupper($t)) { - case 'CHARACTER': - case 'CHARACTER VARYING': - case 'BLOB': - case 'CLOB': - case 'BIT': - case 'BIT VARYING': - if ($len <= $this->blobSize) return 'C'; - - // so we have to check whether binary... - case 'IMAGE': - case 'LONGBLOB': - case 'BLOB': - case 'MEDIUMBLOB': - return !empty($fieldobj->binary) ? 'B' : 'X'; - - case 'DATE': return 'D'; - - case 'TIME': - case 'TIME WITH TIME ZONE': - case 'TIMESTAMP': - case 'TIMESTAMP WITH TIME ZONE': return 'T'; - - case 'PRIMARY_KEY': - return 'R'; - case 'INTEGER': - case 'SMALLINT': - case 'BOOLEAN': - - if (!empty($fieldobj->primary_key)) return 'R'; - else return 'I'; - - default: return 'N'; - } - } - -} //class -} // defined +. + Set tabs to 8. +*/ + +if (! defined("_ADODB_FBSQL_LAYER")) { + define("_ADODB_FBSQL_LAYER", 1 ); + +class ADODB_fbsql extends ADOConnection { + var $databaseType = 'fbsql'; + var $hasInsertID = true; + var $hasAffectedRows = true; + var $metaTablesSQL = "SHOW TABLES"; + var $metaColumnsSQL = "SHOW COLUMNS FROM %s"; + var $fmtTimeStamp = "'Y-m-d H:i:s'"; + var $hasLimit = false; + + function ADODB_fbsql() + { + } + + function _insertid() + { + return fbsql_insert_id($this->_connectionID); + } + + function _affectedrows() + { + return fbsql_affected_rows($this->_connectionID); + } + + function &MetaDatabases() + { + $qid = fbsql_list_dbs($this->_connectionID); + $arr = array(); + $i = 0; + $max = fbsql_num_rows($qid); + while ($i < $max) { + $arr[] = fbsql_tablename($qid,$i); + $i += 1; + } + return $arr; + } + + /* returns concatenated string */ + function Concat() + { + $s = ""; + $arr = func_get_args(); + $first = true; + + $s = implode(',',$arr); + if (sizeof($arr) > 0) return "CONCAT($s)"; + else return ''; + } + + /* returns true or false */ + function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + $this->_connectionID = fbsql_connect($argHostname,$argUsername,$argPassword); + if ($this->_connectionID === false) return false; + if ($argDatabasename) return $this->SelectDB($argDatabasename); + return true; + } + + /* returns true or false */ + function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + $this->_connectionID = fbsql_pconnect($argHostname,$argUsername,$argPassword); + if ($this->_connectionID === false) return false; + if ($argDatabasename) return $this->SelectDB($argDatabasename); + return true; + } + + function &MetaColumns($table) + { + if ($this->metaColumnsSQL) { + + $rs = $this->Execute(sprintf($this->metaColumnsSQL,$table)); + + if ($rs === false) return false; + + $retarr = array(); + while (!$rs->EOF){ + $fld = new ADOFieldObject(); + $fld->name = $rs->fields[0]; + $fld->type = $rs->fields[1]; + + /* split type into type(length): */ + if (preg_match("/^(.+)\((\d+)\)$/", $fld->type, $query_array)) { + $fld->type = $query_array[1]; + $fld->max_length = $query_array[2]; + } else { + $fld->max_length = -1; + } + $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($fld->type,'blob') !== false); + + $retarr[strtoupper($fld->name)] = $fld; + $rs->MoveNext(); + } + $rs->Close(); + return $retarr; + } + return false; + } + + /* returns true or false */ + function SelectDB($dbName) + { + $this->databaseName = $dbName; + if ($this->_connectionID) { + return @fbsql_select_db($dbName,$this->_connectionID); + } + else return false; + } + + + /* returns queryID or false */ + function _query($sql,$inputarr) + { + return fbsql_query("$sql;",$this->_connectionID); + } + + /* Returns: the last error message from previous database operation */ + function ErrorMsg() + { + $this->_errorMsg = @fbsql_error($this->_connectionID); + return $this->_errorMsg; + } + + /* Returns: the last error number from previous database operation */ + function ErrorNo() + { + return @fbsql_errno($this->_connectionID); + } + + /* returns true or false */ + function _close() + { + return @fbsql_close($this->_connectionID); + } + +} + +/*-------------------------------------------------------------------------------------- + Class Name: Recordset +--------------------------------------------------------------------------------------*/ + +class ADORecordSet_fbsql extends ADORecordSet{ + + var $databaseType = "fbsql"; + var $canSeek = true; + + function ADORecordSet_fbsql($queryID,$mode=false) + { + if (!$mode) { + global $ADODB_FETCH_MODE; + $mode = $ADODB_FETCH_MODE; + } + switch ($mode) { + case ADODB_FETCH_NUM: $this->fetchMode = FBSQL_NUM; break; + default: + case ADODB_FETCH_BOTH: $this->fetchMode = FBSQL_BOTH; break; + case ADODB_FETCH_ASSOC: $this->fetchMode = FBSQL_ASSOC; break; + } + return $this->ADORecordSet($queryID); + } + + function _initrs() + { + GLOBAL $ADODB_COUNTRECS; + $this->_numOfRows = ($ADODB_COUNTRECS) ? @fbsql_num_rows($this->_queryID):-1; + $this->_numOfFields = @fbsql_num_fields($this->_queryID); + } + + + + function &FetchField($fieldOffset = -1) { + if ($fieldOffset != -1) { + $o = @fbsql_fetch_field($this->_queryID, $fieldOffset); + /* $o->max_length = -1; // fbsql returns the max length less spaces -- so it is unrealiable */ + $f = @fbsql_field_flags($this->_queryID,$fieldOffset); + $o->binary = (strpos($f,'binary')!== false); + } + else if ($fieldOffset == -1) { /* The $fieldOffset argument is not provided thus its -1 */ + $o = @fbsql_fetch_field($this->_queryID);/* fbsql returns the max length less spaces -- so it is unrealiable */ + /* $o->max_length = -1; */ + } + + return $o; + } + + function _seek($row) + { + return @fbsql_data_seek($this->_queryID,$row); + } + + function _fetch($ignore_fields=false) + { + $this->fields = @fbsql_fetch_array($this->_queryID,$this->fetchMode); + return ($this->fields == true); + } + + function _close() { + return @fbsql_free_result($this->_queryID); + } + + function MetaType($t,$len=-1,$fieldobj=false) + { + if (is_object($t)) { + $fieldobj = $t; + $t = $fieldobj->type; + $len = $fieldobj->max_length; + } + $len = -1; /* fbsql max_length is not accurate */ + switch (strtoupper($t)) { + case 'CHARACTER': + case 'CHARACTER VARYING': + case 'BLOB': + case 'CLOB': + case 'BIT': + case 'BIT VARYING': + if ($len <= $this->blobSize) return 'C'; + + /* so we have to check whether binary... */ + case 'IMAGE': + case 'LONGBLOB': + case 'BLOB': + case 'MEDIUMBLOB': + return !empty($fieldobj->binary) ? 'B' : 'X'; + + case 'DATE': return 'D'; + + case 'TIME': + case 'TIME WITH TIME ZONE': + case 'TIMESTAMP': + case 'TIMESTAMP WITH TIME ZONE': return 'T'; + + case 'PRIMARY_KEY': + return 'R'; + case 'INTEGER': + case 'SMALLINT': + case 'BOOLEAN': + + if (!empty($fieldobj->primary_key)) return 'R'; + else return 'I'; + + default: return 'N'; + } + } + +} /* class */ +} /* defined */ ?> \ No newline at end of file diff --git a/lib/adodb/drivers/adodb-firebird.inc.php b/lib/adodb/drivers/adodb-firebird.inc.php index 5789618601..01a0924cef 100644 --- a/lib/adodb/drivers/adodb-firebird.inc.php +++ b/lib/adodb/drivers/adodb-firebird.inc.php @@ -1,68 +1,67 @@ -ADODB_ibase(); - } - - function ServerInfo() - { - $arr['dialect'] = $this->dialect; - switch($arr['dialect']) { - case '': - case '1': $s = 'Firebird Dialect 1'; break; - case '2': $s = 'Firebird Dialect 2'; break; - default: - case '3': $s = 'Firebird Dialect 3'; break; - } - $arr['version'] = ADOConnection::_findvers($s); - $arr['description'] = $s; - return $arr; - } - - // Note that Interbase 6.5 uses this ROWS instead - don't you love forking wars! - // SELECT col1, col2 FROM table ROWS 5 -- get 5 rows - // SELECT col1, col2 FROM TABLE ORDER BY col1 ROWS 3 TO 7 -- first 5 skip 2 - function &SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false, $arg3=false,$secs=0) - { - $str = 'SELECT '; - if ($nrows >= 0) $str .= "FIRST $nrows "; - $str .=($offset>=0) ? "SKIP $offset " : ''; - - $sql = preg_replace('/^[ \t]*select/i',$str,$sql); - return ($secs) ? - $this->CacheExecute($secs,$sql,$inputarr,$arg3) - : - $this->Execute($sql,$inputarr,$arg3); - } - - -}; - - -class ADORecordSet_firebird extends ADORecordSet_ibase { - - var $databaseType = "firebird"; - - function ADORecordSet_firebird($id,$mode=false) - { - $this->ADORecordSet_ibase($id,$mode); - } -} +ADODB_ibase(); + } + + function ServerInfo() + { + $arr['dialect'] = $this->dialect; + switch($arr['dialect']) { + case '': + case '1': $s = 'Firebird Dialect 1'; break; + case '2': $s = 'Firebird Dialect 2'; break; + default: + case '3': $s = 'Firebird Dialect 3'; break; + } + $arr['version'] = ADOConnection::_findvers($s); + $arr['description'] = $s; + return $arr; + } + + /* Note that Interbase 6.5 uses this ROWS instead - don't you love forking wars! */ + /* SELECT col1, col2 FROM table ROWS 5 -- get 5 rows */ + /* SELECT col1, col2 FROM TABLE ORDER BY col1 ROWS 3 TO 7 -- first 5 skip 2 */ + function &SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false, $arg3=false,$secs=0) + { + $str = 'SELECT '; + if ($nrows >= 0) $str .= "FIRST $nrows "; + $str .=($offset>=0) ? "SKIP $offset " : ''; + + $sql = preg_replace('/^[ \t]*select/i',$str,$sql); + return ($secs) ? + $this->CacheExecute($secs,$sql,$inputarr,$arg3) + : + $this->Execute($sql,$inputarr,$arg3); + } + + +}; + + +class ADORecordSet_firebird extends ADORecordSet_ibase { + + var $databaseType = "firebird"; + + function ADORecordSet_firebird($id,$mode=false) + { + $this->ADORecordSet_ibase($id,$mode); + } +} ?> \ No newline at end of file diff --git a/lib/adodb/drivers/adodb-ibase.inc.php b/lib/adodb/drivers/adodb-ibase.inc.php index 5a1870c215..b20cc5933f 100644 --- a/lib/adodb/drivers/adodb-ibase.inc.php +++ b/lib/adodb/drivers/adodb-ibase.inc.php @@ -1,665 +1,665 @@ - - changed transaction handling and added experimental blob stuff - - Docs to interbase at the website - http://www.synectics.co.za/php3/tutorial/IB_PHP3_API.html - - To use gen_id(), see - http://www.volny.cz/iprenosil/interbase/ip_ib_code.htm#_code_creategen - - $rs = $conn->Execute('select gen_id(adodb,1) from rdb$database'); - $id = $rs->fields[0]; - $conn->Execute("insert into table (id, col1,...) values ($id, $val1,...)"); -*/ - - -class ADODB_ibase extends ADOConnection { - var $databaseType = "ibase"; - var $dataProvider = "ibase"; - var $replaceQuote = "''"; // string to use to replace quotes - var $ibase_timefmt = '%Y-%m-%d'; - var $fmtDate = "'Y-m-d'"; - var $fmtTimeStamp = "'Y-m-d, H:i:s'"; - var $concat_operator='||'; - var $_transactionID; - var $metaTablesSQL = "select rdb\$relation_name from rdb\$relations where rdb\$relation_name not like 'RDB\$%'"; - var $metaColumnsSQL = "select a.rdb\$field_name,b.rdb\$field_type,b.rdb\$field_length from rdb\$relation_fields a join rdb\$fields b on a.rdb\$field_source=b.rdb\$field_name where rdb\$relation_name ='%s'"; - var $ibasetrans = IBASE_DEFAULT; - var $hasGenID = true; - var $_bindInputArray = true; - var $buffers = 0; - var $dialect = 1; - var $sysDate = "cast('TODAY' as date)"; - var $sysTimeStamp = "cast('NOW' as timestamp)"; - var $ansiOuter = true; - var $hasAffectedRows = false; - var $poorAffectedRows = true; - var $blobEncodeType = 'C'; - - function ADODB_ibase() - { - } - - function MetaPrimaryKeys($table,$owner_notused=false,$internalKey=false) - { - if ($internalKey) return array('RDB$DB_KEY'); - - $table = strtoupper($table); - - $sql = 'SELECT S.RDB$FIELD_NAME AFIELDNAME - FROM RDB$INDICES I JOIN RDB$INDEX_SEGMENTS S ON I.RDB$INDEX_NAME=S.RDB$INDEX_NAME - WHERE I.RDB$RELATION_NAME=\''.$table.'\' and I.RDB$INDEX_NAME like \'RDB$PRIMARY%\' - ORDER BY I.RDB$INDEX_NAME,S.RDB$FIELD_POSITION'; - - $a = $this->GetCol($sql,false,true); - if ($a && sizeof($a)>0) return $a; - return false; - } - - function ServerInfo() - { - $arr['dialect'] = $this->dialect; - switch($arr['dialect']) { - case '': - case '1': $s = 'Interbase 5.5 or earlier'; break; - case '2': $s = 'Interbase 5.6'; break; - default: - case '3': $s = 'Interbase 6.0'; break; - } - $arr['version'] = ADOConnection::_findvers($s); - $arr['description'] = $s; - return $arr; - } - - function BeginTrans() - { - if ($this->transOff) return true; - $this->transCnt += 1; - $this->autoCommit = false; - $this->_transactionID = $this->_connectionID;//ibase_trans($this->ibasetrans, $this->_connectionID); - return $this->_transactionID; - } - - function CommitTrans($ok=true) - { - if (!$ok) return $this->RollbackTrans(); - if ($this->transOff) return true; - if ($this->transCnt) $this->transCnt -= 1; - $ret = false; - $this->autoCommit = true; - if ($this->_transactionID) { - //print ' commit '; - $ret = ibase_commit($this->_transactionID); - } - $this->_transactionID = false; - return $ret; - } - - function RollbackTrans() - { - if ($this->transOff) return true; - if ($this->transCnt) $this->transCnt -= 1; - $ret = false; - $this->autoCommit = true; - if ($this->_transactionID) - $ret = ibase_rollback($this->_transactionID); - $this->_transactionID = false; - - return $ret; - } - - // See http://community.borland.com/article/0,1410,25844,00.html - function RowLock($tables,$where,$col) - { - if ($this->autoCommit) $this->BeginTrans(); - $this->Execute("UPDATE $table SET $col=$col WHERE $where "); // is this correct - jlim? - return 1; - } - - /*// use delete and insert instead - function Replace($table, $fieldArray, $keyCol,$autoQuote=false) - { - if (count($fieldArray) == 0) return 0; - - if (!is_array($keyCol)) { - $keyCol = array($keyCol); - } - - if ($autoQuote) - foreach($fieldArray as $k => $v) { - if (!is_numeric($v) and $v[0] != "'" and strcasecmp($v,'null')!=0) { - $v = $this->qstr($v); - $fieldArray[$k] = $v; - } - } - - $first = true; - foreach ($keyCol as $v) { - if ($first) { - $first = false; - $where = "$v=$fieldArray[$v]"; - } else { - $where .= " and $v=$fieldArray[$v]"; - } - } - - $first = true; - foreach($fieldArray as $k => $v) { - if ($first) { - $first = false; - $iCols = "$k"; - $iVals = "$v"; - } else { - $iCols .= ",$k"; - $iVals .= ",$v"; - } - } - $this->BeginTrans(); - $this->Execute("DELETE FROM $table WHERE $where"); - $ok = $this->Execute("INSERT INTO $table ($iCols) VALUES ($iVals)"); - $this->CommitTrans(); - - return ($ok) ? 2 : 0; - } - */ - function CreateSequence($seqname,$startID=1) - { - $ok = $this->Execute(("INSERT INTO RDB\$GENERATORS (RDB\$GENERATOR_NAME) VALUES (UPPER('$seqname'))" )); - if (!$ok) return false; - return $this->Execute("SET GENERATOR $seqname TO ".($startID-1).';'); - } - - function DropSequence($seqname) - { - $seqname = strtoupper($seqname); - $this->Execute("delete from RDB\$GENERATORS where RDB\$GENERATOR_NAME='$seqname'"); - } - - function GenID($seqname='adodbseq',$startID=1) - { - $getnext = ("SELECT Gen_ID($seqname,1) FROM RDB\$DATABASE"); - $rs = @$this->Execute($getnext); - if (!$rs) { - $this->Execute(("INSERT INTO RDB\$GENERATORS (RDB\$GENERATOR_NAME) VALUES (UPPER('$seqname'))" )); - $this->Execute("SET GENERATOR $seqname TO ".($startID-1).';'); - $rs = $this->Execute($getnext); - } - if ($rs && !$rs->EOF) $this->genID = (integer) reset($rs->fields); - else $this->genID = 0; // false - - if ($rs) $rs->Close(); - - return $this->genID; - } - - function SelectDB($dbName) - { - return false; - } - - function _handleerror() - { - $this->_errorMsg = ibase_errmsg(); - } - - function ErrorNo() - { - if (preg_match('/error code = ([\-0-9]*)/i', $this->_errorMsg,$arr)) return (integer) $arr[1]; - else return 0; - } - - function ErrorMsg() - { - return $this->_errorMsg; - } - - // returns true or false - function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) - { - if ($argDatabasename) $argHostname .= ':'.$argDatabasename; - $this->_connectionID = ibase_connect($argHostname,$argUsername,$argPassword,$this->charSet,$this->buffers,$this->dialect); - if ($this->dialect != 1) { // http://www.ibphoenix.com/ibp_60_del_id_ds.html - $this->replaceQuote = "''"; - } - if ($this->_connectionID === false) { - $this->_handleerror(); - return false; - } - - ibase_timefmt($this->ibase_timefmt); - return true; - } - // returns true or false - function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) - { - if ($argDatabasename) $argHostname .= ':'.$argDatabasename; - $this->_connectionID = ibase_pconnect($argHostname,$argUsername,$argPassword,$this->charSet,$this->buffers,$this->dialect); - if ($this->dialect != 1) { // http://www.ibphoenix.com/ibp_60_del_id_ds.html - $this->replaceQuote = "''"; - } - if ($this->_connectionID === false) { - $this->_handleerror(); - return false; - } - - ibase_timefmt($this->ibase_timefmt); - return true; - } - - function Prepare($sql) - { - // return $sql; - $stmt = ibase_prepare($sql); - if (!$stmt) return false; - return array($sql,$stmt); - } - - // returns query ID if successful, otherwise false - // there have been reports of problems with nested queries - the code is probably not re-entrant? - function _query($sql,$iarr=false) - { - - if (!$this->autoCommit && $this->_transactionID) { - $conn = $this->_transactionID; - $docommit = false; - } else { - $conn = $this->_connectionID; - $docommit = true; - } - if (is_array($sql)) { - $fn = 'ibase_execute'; - $sql = $sql[1]; - - if (is_array($iarr)) { - if (ADODB_PHPVER >= 0x4050) { // actually 4.0.4 - $fnarr = array_merge( array($sql) , $iarr); - $ret = call_user_func_array($fn,$fnarr); - } else { - switch(sizeof($iarr)) { - case 1: $ret = $fn($sql,$iarr[0]); break; - case 2: $ret = $fn($sql,$iarr[0],$iarr[1]); break; - case 3: $ret = $fn($sql,$iarr[0],$iarr[1],$iarr[2]); break; - case 4: $ret = $fn($sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3]); break; - case 5: $ret = $fn($sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4]); break; - case 6: $ret = $fn($sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4],$iarr[5]); break; - case 7: $ret = $fn($sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4],$iarr[5],$iarr[6]); break; - default: ADOConnection::outp( "Too many parameters to ibase query $sql"); - case 8: $ret = $fn($sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4],$iarr[5],$iarr[6],$iarr[7]); break; - } - } - } else $ret = $fn($sql); - } else { - $fn = 'ibase_query'; - - if (is_array($iarr)) { - if (ADODB_PHPVER >= 0x4050) { // actually 4.0.4 - $fnarr = array_merge( array($conn,$sql) , $iarr); - $ret = call_user_func_array($fn,$fnarr); - } else { - switch(sizeof($iarr)) { - case 1: $ret = $fn($conn,$sql,$iarr[0]); break; - case 2: $ret = $fn($conn,$sql,$iarr[0],$iarr[1]); break; - case 3: $ret = $fn($conn,$sql,$iarr[0],$iarr[1],$iarr[2]); break; - case 4: $ret = $fn($conn,$sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3]); break; - case 5: $ret = $fn($conn,$sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4]); break; - case 6: $ret = $fn($conn,$sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4],$iarr[5]); break; - case 7: $ret = $fn($conn,$sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4],$iarr[5],$iarr[6]); break; - default: ADOConnection::outp( "Too many parameters to ibase query $sql"); - case 8: $ret = $fn($conn,$sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4],$iarr[5],$iarr[6],$iarr[7]); break; - } - } - } else $ret = $fn($conn,$sql); - } - if ($docommit && $ret === true) ibase_commit($this->_connectionID); - - $this->_handleerror(); - return $ret; - } - - // returns true or false - function _close() - { - if (!$this->autoCommit) @ibase_rollback($this->_connectionID); - return @ibase_close($this->_connectionID); - } - - // returns array of ADOFieldObjects for current table - function &MetaColumns($table) - { - global $ADODB_FETCH_MODE; - - if ($this->metaColumnsSQL) { - - $save = $ADODB_FETCH_MODE; - $ADODB_FETCH_MODE = ADODB_FETCH_NUM; - - $rs = $this->Execute(sprintf($this->metaColumnsSQL,strtoupper($table))); - - $ADODB_FETCH_MODE = $save; - if ($rs === false) return false; - - $retarr = array(); - while (!$rs->EOF) { //print_r($rs->fields); - $fld = new ADOFieldObject(); - $fld->name = trim($rs->fields[0]); - $tt = $rs->fields[1]; - switch($tt) - { - case 7: - case 8: - case 9:$tt = 'INTEGER'; break; - case 10: - case 27: - case 11:$tt = 'FLOAT'; break; - default: - case 40: - case 14:$tt = 'CHAR'; break; - case 35:$tt = 'DATE'; break; - case 37:$tt = 'VARCHAR'; break; - case 261:$tt = 'BLOB'; break; - case 14: $tt = 'TEXT'; break; - case 13: - case 35:$tt = 'TIMESTAMP'; break; - } - $fld->type = $tt; - $fld->max_length = $rs->fields[2]; - $retarr[strtoupper($fld->name)] = $fld; - - $rs->MoveNext(); - } - $rs->Close(); - return $retarr; - } - return false; - } - - function BlobEncode( $blob ) - { - $blobid = ibase_blob_create( $this->_connectionID); - ibase_blob_add( $blobid, $blob ); - return ibase_blob_close( $blobid ); - } - - // since we auto-decode all blob's since 2.42, - // BlobDecode should not do any transforms - function BlobDecode($blob) - { - return $blob; - } - - // old blobdecode function - // still used to auto-decode all blob's - function _BlobDecode( $blob ) - { - $blobid = ibase_blob_open( $blob ); - $realblob = ibase_blob_get( $blobid,$this->maxblobsize); // 2nd param is max size of blob -- Kevin Boillet- while($string = ibase_blob_get($blobid, 8192)){ - $realblob .= $string; - } - ibase_blob_close( $blobid ); - - return( $realblob ); - } - - function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB') - { - $fd = fopen($path,'rb'); - if ($fd === false) return false; - $blob_id = ibase_blob_create($this->_connectionID); - - /* fill with data */ - - while ($val = fread($fd,32768)){ - ibase_blob_add($blob_id, $val); - } - - /* close and get $blob_id_str for inserting into table */ - $blob_id_str = ibase_blob_close($blob_id); - - fclose($fd); - return $this->Execute("UPDATE $table SET $column=(?) WHERE $where",array($blob_id_str)) != false; - } - - /* - Insert a null into the blob field of the table first. - Then use UpdateBlob to store the blob. - - Usage: - - $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); - $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1'); - */ - function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') - { - $blob_id = ibase_blob_create($this->_connectionID); - - // ibase_blob_add($blob_id, $val); - - // replacement that solves the problem by which only the first modulus 64K / - // of $val are stored at the blob field //////////////////////////////////// - // Thx Abel Berenstein aberenstein#afip.gov.ar - $len = strlen($val); - $chunk_size = 32768; - $tail_size = $len % $chunk_size; - $n_chunks = ($len - $tail_size) / $chunk_size; - - for ($n = 0; $n < $n_chunks; $n++) { - $start = $n * $chunk_size; - $data = substr($val, $start, $chunk_size); - ibase_blob_add($blob_id, $data); - } - - if ($tail_size) { - $start = $n_chunks * $chunk_size; - $data = substr($val, $start, $tail_size); - ibase_blob_add($blob_id, $data); - } - // end replacement ///////////////////////////////////////////////////////// - - $blob_id_str = ibase_blob_close($blob_id); - - return $this->Execute("UPDATE $table SET $column=(?) WHERE $where",array($blob_id_str)) != false; - - } - - - function OldUpdateBlob($table,$column,$val,$where,$blobtype='BLOB') - { - $blob_id = ibase_blob_create($this->_connectionID); - ibase_blob_add($blob_id, $val); - $blob_id_str = ibase_blob_close($blob_id); - return $this->Execute("UPDATE $table SET $column=(?) WHERE $where",array($blob_id_str)) != false; - } - - // Format date column in sql string given an input format that understands Y M D - // Only since Interbase 6.0 - uses EXTRACT - // problem - does not zero-fill the day and month yet - function SQLDate($fmt, $col=false) - { - if (!$col) $col = $this->sysDate; - $s = ''; - - $len = strlen($fmt); - for ($i=0; $i < $len; $i++) { - if ($s) $s .= '||'; - $ch = $fmt[$i]; - switch($ch) { - case 'Y': - case 'y': - $s .= "extract(year from $col)"; - break; - case 'M': - case 'm': - $s .= "extract(month from $col)"; - break; - case 'Q': - case 'q': - $s .= "cast(((extract(month from $col)+2) / 3) as integer)"; - break; - case 'D': - case 'd': - $s .= "(extract(day from $col))"; - break; - default: - if ($ch == '\\') { - $i++; - $ch = substr($fmt,$i,1); - } - $s .= $this->qstr($ch); - break; - } - } - return $s; - } -} - -/*-------------------------------------------------------------------------------------- - Class Name: Recordset ---------------------------------------------------------------------------------------*/ - -class ADORecordset_ibase extends ADORecordSet -{ - - var $databaseType = "ibase"; - var $bind=false; - var $_cacheType; - - function ADORecordset_ibase($id,$mode=false) - { - global $ADODB_FETCH_MODE; - - $this->fetchMode = ($mode === false) ? $ADODB_FETCH_MODE : $mode; - return $this->ADORecordSet($id); - } - - /* 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; - $ibf = ibase_field_info($this->_queryID,$fieldOffset); - $fld->name = strtolower($ibf['alias']); - if (empty($fld->name)) $fld->name = strtolower($ibf['name']); - $fld->type = $ibf['type']; - $fld->max_length = $ibf['length']; - return $fld; - } - - function _initrs() - { - $this->_numOfRows = -1; - $this->_numOfFields = @ibase_num_fields($this->_queryID); - - // cache types for blob decode check - for ($i=0, $max = $this->_numOfFields; $i < $max; $i++) { - $f1 = $this->FetchField($i); - $this->_cacheType[] = $f1->type; - } - } - - function _seek($row) - { - return false; - } - - function _fetch() - { - $f = @ibase_fetch_row($this->_queryID); - if ($f === false) { - $this->fields = false; - return false; - } - // OPN stuff start - optimized - // fix missing nulls and decode blobs automatically - for ($i=0, $max = $this->_numOfFields; $i < $max; $i++) { - if ($this->_cacheType[$i]=="BLOB") { - if (isset($f[$i])) { - $f[$i] = ADODB_ibase::_BlobDecode($f[$i]); - } else { - $f[$i] = null; - } - } else { - if (!isset($f[$i])) { - $f[$i] = null; - } - } - } - // OPN stuff end - - $this->fields = $f; - if ($this->fetchMode & ADODB_FETCH_ASSOC) { - $this->fields = $this->GetRowAssoc(ADODB_ASSOC_CASE); - } - return true; - } - - /* Use associative array to get fields array */ - function Fields($colname) - { - if ($this->fetchMode & ADODB_FETCH_ASSOC) 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)]]; - - } - - - function _close() - { - return @ibase_free_result($this->_queryID); - } - - function MetaType($t,$len=-1,$fieldobj=false) - { - if (is_object($t)) { - $fieldobj = $t; - $t = $fieldobj->type; - $len = $fieldobj->max_length; - } - switch (strtoupper($t)) { - case 'CHAR': - return 'C'; - - case 'TEXT': - case 'VARCHAR': - case 'VARYING': - if ($len <= $this->blobSize) return 'C'; - return 'X'; - case 'BLOB': - return 'B'; - - case 'TIMESTAMP': - case 'DATE': return 'D'; - - //case 'T': return 'T'; - - //case 'L': return 'L'; - case 'INT': - case 'SHORT': - case 'INTEGER': return 'I'; - default: return 'N'; - } - } - -} + + changed transaction handling and added experimental blob stuff + + Docs to interbase at the website + http://www.synectics.co.za/php3/tutorial/IB_PHP3_API.html + + To use gen_id(), see + http://www.volny.cz/iprenosil/interbase/ip_ib_code.htm#_code_creategen + + $rs = $conn->Execute('select gen_id(adodb,1) from rdb$database'); + $id = $rs->fields[0]; + $conn->Execute("insert into table (id, col1,...) values ($id, $val1,...)"); +*/ + + +class ADODB_ibase extends ADOConnection { + var $databaseType = "ibase"; + var $dataProvider = "ibase"; + var $replaceQuote = "''"; /* string to use to replace quotes */ + var $ibase_timefmt = '%Y-%m-%d'; + var $fmtDate = "'Y-m-d'"; + var $fmtTimeStamp = "'Y-m-d, H:i:s'"; + var $concat_operator='||'; + var $_transactionID; + var $metaTablesSQL = "select rdb\$relation_name from rdb\$relations where rdb\$relation_name not like 'RDB\$%'"; + var $metaColumnsSQL = "select a.rdb\$field_name,b.rdb\$field_type,b.rdb\$field_length from rdb\$relation_fields a join rdb\$fields b on a.rdb\$field_source=b.rdb\$field_name where rdb\$relation_name ='%s'"; + var $ibasetrans = IBASE_DEFAULT; + var $hasGenID = true; + var $_bindInputArray = true; + var $buffers = 0; + var $dialect = 1; + var $sysDate = "cast('TODAY' as date)"; + var $sysTimeStamp = "cast('NOW' as timestamp)"; + var $ansiOuter = true; + var $hasAffectedRows = false; + var $poorAffectedRows = true; + var $blobEncodeType = 'C'; + + function ADODB_ibase() + { + } + + function MetaPrimaryKeys($table,$owner_notused=false,$internalKey=false) + { + if ($internalKey) return array('RDB$DB_KEY'); + + $table = strtoupper($table); + + $sql = 'SELECT S.RDB$FIELD_NAME AFIELDNAME + FROM RDB$INDICES I JOIN RDB$INDEX_SEGMENTS S ON I.RDB$INDEX_NAME=S.RDB$INDEX_NAME + WHERE I.RDB$RELATION_NAME=\''.$table.'\' and I.RDB$INDEX_NAME like \'RDB$PRIMARY%\' + ORDER BY I.RDB$INDEX_NAME,S.RDB$FIELD_POSITION'; + + $a = $this->GetCol($sql,false,true); + if ($a && sizeof($a)>0) return $a; + return false; + } + + function ServerInfo() + { + $arr['dialect'] = $this->dialect; + switch($arr['dialect']) { + case '': + case '1': $s = 'Interbase 5.5 or earlier'; break; + case '2': $s = 'Interbase 5.6'; break; + default: + case '3': $s = 'Interbase 6.0'; break; + } + $arr['version'] = ADOConnection::_findvers($s); + $arr['description'] = $s; + return $arr; + } + + function BeginTrans() + { + if ($this->transOff) return true; + $this->transCnt += 1; + $this->autoCommit = false; + $this->_transactionID = $this->_connectionID;/* ibase_trans($this->ibasetrans, $this->_connectionID); */ + return $this->_transactionID; + } + + function CommitTrans($ok=true) + { + if (!$ok) return $this->RollbackTrans(); + if ($this->transOff) return true; + if ($this->transCnt) $this->transCnt -= 1; + $ret = false; + $this->autoCommit = true; + if ($this->_transactionID) { + /* print ' commit '; */ + $ret = ibase_commit($this->_transactionID); + } + $this->_transactionID = false; + return $ret; + } + + function RollbackTrans() + { + if ($this->transOff) return true; + if ($this->transCnt) $this->transCnt -= 1; + $ret = false; + $this->autoCommit = true; + if ($this->_transactionID) + $ret = ibase_rollback($this->_transactionID); + $this->_transactionID = false; + + return $ret; + } + + /* See http://community.borland.com/article/0,1410,25844,00.html */ + function RowLock($tables,$where,$col) + { + if ($this->autoCommit) $this->BeginTrans(); + $this->Execute("UPDATE $table SET $col=$col WHERE $where "); /* is this correct - jlim? */ + return 1; + } + + /*/* use delete and insert instead */ + function Replace($table, $fieldArray, $keyCol,$autoQuote=false) + { + if (count($fieldArray) == 0) return 0; + + if (!is_array($keyCol)) { + $keyCol = array($keyCol); + } + + if ($autoQuote) + foreach($fieldArray as $k => $v) { + if (!is_numeric($v) and $v[0] != "'" and strcasecmp($v,'null')!=0) { + $v = $this->qstr($v); + $fieldArray[$k] = $v; + } + } + + $first = true; + foreach ($keyCol as $v) { + if ($first) { + $first = false; + $where = "$v=$fieldArray[$v]"; + } else { + $where .= " and $v=$fieldArray[$v]"; + } + } + + $first = true; + foreach($fieldArray as $k => $v) { + if ($first) { + $first = false; + $iCols = "$k"; + $iVals = "$v"; + } else { + $iCols .= ",$k"; + $iVals .= ",$v"; + } + } + $this->BeginTrans(); + $this->Execute("DELETE FROM $table WHERE $where"); + $ok = $this->Execute("INSERT INTO $table ($iCols) VALUES ($iVals)"); + $this->CommitTrans(); + + return ($ok) ? 2 : 0; + } + */ + function CreateSequence($seqname,$startID=1) + { + $ok = $this->Execute(("INSERT INTO RDB\$GENERATORS (RDB\$GENERATOR_NAME) VALUES (UPPER('$seqname'))" )); + if (!$ok) return false; + return $this->Execute("SET GENERATOR $seqname TO ".($startID-1).';'); + } + + function DropSequence($seqname) + { + $seqname = strtoupper($seqname); + $this->Execute("delete from RDB\$GENERATORS where RDB\$GENERATOR_NAME='$seqname'"); + } + + function GenID($seqname='adodbseq',$startID=1) + { + $getnext = ("SELECT Gen_ID($seqname,1) FROM RDB\$DATABASE"); + $rs = @$this->Execute($getnext); + if (!$rs) { + $this->Execute(("INSERT INTO RDB\$GENERATORS (RDB\$GENERATOR_NAME) VALUES (UPPER('$seqname'))" )); + $this->Execute("SET GENERATOR $seqname TO ".($startID-1).';'); + $rs = $this->Execute($getnext); + } + if ($rs && !$rs->EOF) $this->genID = (integer) reset($rs->fields); + else $this->genID = 0; /* false */ + + if ($rs) $rs->Close(); + + return $this->genID; + } + + function SelectDB($dbName) + { + return false; + } + + function _handleerror() + { + $this->_errorMsg = ibase_errmsg(); + } + + function ErrorNo() + { + if (preg_match('/error code = ([\-0-9]*)/i', $this->_errorMsg,$arr)) return (integer) $arr[1]; + else return 0; + } + + function ErrorMsg() + { + return $this->_errorMsg; + } + + /* returns true or false */ + function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + if ($argDatabasename) $argHostname .= ':'.$argDatabasename; + $this->_connectionID = ibase_connect($argHostname,$argUsername,$argPassword,$this->charSet,$this->buffers,$this->dialect); + if ($this->dialect != 1) { /* http://www.ibphoenix.com/ibp_60_del_id_ds.html */ + $this->replaceQuote = "''"; + } + if ($this->_connectionID === false) { + $this->_handleerror(); + return false; + } + + ibase_timefmt($this->ibase_timefmt); + return true; + } + /* returns true or false */ + function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + if ($argDatabasename) $argHostname .= ':'.$argDatabasename; + $this->_connectionID = ibase_pconnect($argHostname,$argUsername,$argPassword,$this->charSet,$this->buffers,$this->dialect); + if ($this->dialect != 1) { /* http://www.ibphoenix.com/ibp_60_del_id_ds.html */ + $this->replaceQuote = "''"; + } + if ($this->_connectionID === false) { + $this->_handleerror(); + return false; + } + + ibase_timefmt($this->ibase_timefmt); + return true; + } + + function Prepare($sql) + { + /* return $sql; */ + $stmt = ibase_prepare($sql); + if (!$stmt) return false; + return array($sql,$stmt); + } + + /* returns query ID if successful, otherwise false */ + /* there have been reports of problems with nested queries - the code is probably not re-entrant? */ + function _query($sql,$iarr=false) + { + + if (!$this->autoCommit && $this->_transactionID) { + $conn = $this->_transactionID; + $docommit = false; + } else { + $conn = $this->_connectionID; + $docommit = true; + } + if (is_array($sql)) { + $fn = 'ibase_execute'; + $sql = $sql[1]; + + if (is_array($iarr)) { + if (ADODB_PHPVER >= 0x4050) { /* actually 4.0.4 */ + $fnarr = array_merge( array($sql) , $iarr); + $ret = call_user_func_array($fn,$fnarr); + } else { + switch(sizeof($iarr)) { + case 1: $ret = $fn($sql,$iarr[0]); break; + case 2: $ret = $fn($sql,$iarr[0],$iarr[1]); break; + case 3: $ret = $fn($sql,$iarr[0],$iarr[1],$iarr[2]); break; + case 4: $ret = $fn($sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3]); break; + case 5: $ret = $fn($sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4]); break; + case 6: $ret = $fn($sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4],$iarr[5]); break; + case 7: $ret = $fn($sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4],$iarr[5],$iarr[6]); break; + default: ADOConnection::outp( "Too many parameters to ibase query $sql"); + case 8: $ret = $fn($sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4],$iarr[5],$iarr[6],$iarr[7]); break; + } + } + } else $ret = $fn($sql); + } else { + $fn = 'ibase_query'; + + if (is_array($iarr)) { + if (ADODB_PHPVER >= 0x4050) { /* actually 4.0.4 */ + $fnarr = array_merge( array($conn,$sql) , $iarr); + $ret = call_user_func_array($fn,$fnarr); + } else { + switch(sizeof($iarr)) { + case 1: $ret = $fn($conn,$sql,$iarr[0]); break; + case 2: $ret = $fn($conn,$sql,$iarr[0],$iarr[1]); break; + case 3: $ret = $fn($conn,$sql,$iarr[0],$iarr[1],$iarr[2]); break; + case 4: $ret = $fn($conn,$sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3]); break; + case 5: $ret = $fn($conn,$sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4]); break; + case 6: $ret = $fn($conn,$sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4],$iarr[5]); break; + case 7: $ret = $fn($conn,$sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4],$iarr[5],$iarr[6]); break; + default: ADOConnection::outp( "Too many parameters to ibase query $sql"); + case 8: $ret = $fn($conn,$sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4],$iarr[5],$iarr[6],$iarr[7]); break; + } + } + } else $ret = $fn($conn,$sql); + } + if ($docommit && $ret === true) ibase_commit($this->_connectionID); + + $this->_handleerror(); + return $ret; + } + + /* returns true or false */ + function _close() + { + if (!$this->autoCommit) @ibase_rollback($this->_connectionID); + return @ibase_close($this->_connectionID); + } + + /* returns array of ADOFieldObjects for current table */ + function &MetaColumns($table) + { + global $ADODB_FETCH_MODE; + + if ($this->metaColumnsSQL) { + + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + $rs = $this->Execute(sprintf($this->metaColumnsSQL,strtoupper($table))); + + $ADODB_FETCH_MODE = $save; + if ($rs === false) return false; + + $retarr = array(); + while (!$rs->EOF) { /* print_r($rs->fields); */ + $fld = new ADOFieldObject(); + $fld->name = trim($rs->fields[0]); + $tt = $rs->fields[1]; + switch($tt) + { + case 7: + case 8: + case 9:$tt = 'INTEGER'; break; + case 10: + case 27: + case 11:$tt = 'FLOAT'; break; + default: + case 40: + case 14:$tt = 'CHAR'; break; + case 35:$tt = 'DATE'; break; + case 37:$tt = 'VARCHAR'; break; + case 261:$tt = 'BLOB'; break; + case 14: $tt = 'TEXT'; break; + case 13: + case 35:$tt = 'TIMESTAMP'; break; + } + $fld->type = $tt; + $fld->max_length = $rs->fields[2]; + $retarr[strtoupper($fld->name)] = $fld; + + $rs->MoveNext(); + } + $rs->Close(); + return $retarr; + } + return false; + } + + function BlobEncode( $blob ) + { + $blobid = ibase_blob_create( $this->_connectionID); + ibase_blob_add( $blobid, $blob ); + return ibase_blob_close( $blobid ); + } + + /* since we auto-decode all blob's since 2.42, */ + /* BlobDecode should not do any transforms */ + function BlobDecode($blob) + { + return $blob; + } + + /* old blobdecode function */ + /* still used to auto-decode all blob's */ + function _BlobDecode( $blob ) + { + $blobid = ibase_blob_open( $blob ); + $realblob = ibase_blob_get( $blobid,$this->maxblobsize); /* 2nd param is max size of blob -- Kevin Boillet */ + while($string = ibase_blob_get($blobid, 8192)){ + $realblob .= $string; + } + ibase_blob_close( $blobid ); + + return( $realblob ); + } + + function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB') + { + $fd = fopen($path,'rb'); + if ($fd === false) return false; + $blob_id = ibase_blob_create($this->_connectionID); + + /* fill with data */ + + while ($val = fread($fd,32768)){ + ibase_blob_add($blob_id, $val); + } + + /* close and get $blob_id_str for inserting into table */ + $blob_id_str = ibase_blob_close($blob_id); + + fclose($fd); + return $this->Execute("UPDATE $table SET $column=(?) WHERE $where",array($blob_id_str)) != false; + } + + /* + Insert a null into the blob field of the table first. + Then use UpdateBlob to store the blob. + + Usage: + + $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); + $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1'); + */ + function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') + { + $blob_id = ibase_blob_create($this->_connectionID); + + /* ibase_blob_add($blob_id, $val); */ + + /* replacement that solves the problem by which only the first modulus 64K / */ + /* of $val are stored at the blob field //////////////////////////////////// */ + /* Thx Abel Berenstein aberenstein#afip.gov.ar */ + $len = strlen($val); + $chunk_size = 32768; + $tail_size = $len % $chunk_size; + $n_chunks = ($len - $tail_size) / $chunk_size; + + for ($n = 0; $n < $n_chunks; $n++) { + $start = $n * $chunk_size; + $data = substr($val, $start, $chunk_size); + ibase_blob_add($blob_id, $data); + } + + if ($tail_size) { + $start = $n_chunks * $chunk_size; + $data = substr($val, $start, $tail_size); + ibase_blob_add($blob_id, $data); + } + /* end replacement ///////////////////////////////////////////////////////// */ + + $blob_id_str = ibase_blob_close($blob_id); + + return $this->Execute("UPDATE $table SET $column=(?) WHERE $where",array($blob_id_str)) != false; + + } + + + function OldUpdateBlob($table,$column,$val,$where,$blobtype='BLOB') + { + $blob_id = ibase_blob_create($this->_connectionID); + ibase_blob_add($blob_id, $val); + $blob_id_str = ibase_blob_close($blob_id); + return $this->Execute("UPDATE $table SET $column=(?) WHERE $where",array($blob_id_str)) != false; + } + + /* Format date column in sql string given an input format that understands Y M D */ + /* Only since Interbase 6.0 - uses EXTRACT */ + /* problem - does not zero-fill the day and month yet */ + function SQLDate($fmt, $col=false) + { + if (!$col) $col = $this->sysDate; + $s = ''; + + $len = strlen($fmt); + for ($i=0; $i < $len; $i++) { + if ($s) $s .= '||'; + $ch = $fmt[$i]; + switch($ch) { + case 'Y': + case 'y': + $s .= "extract(year from $col)"; + break; + case 'M': + case 'm': + $s .= "extract(month from $col)"; + break; + case 'Q': + case 'q': + $s .= "cast(((extract(month from $col)+2) / 3) as integer)"; + break; + case 'D': + case 'd': + $s .= "(extract(day from $col))"; + break; + default: + if ($ch == '\\') { + $i++; + $ch = substr($fmt,$i,1); + } + $s .= $this->qstr($ch); + break; + } + } + return $s; + } +} + +/*-------------------------------------------------------------------------------------- + Class Name: Recordset +--------------------------------------------------------------------------------------*/ + +class ADORecordset_ibase extends ADORecordSet +{ + + var $databaseType = "ibase"; + var $bind=false; + var $_cacheType; + + function ADORecordset_ibase($id,$mode=false) + { + global $ADODB_FETCH_MODE; + + $this->fetchMode = ($mode === false) ? $ADODB_FETCH_MODE : $mode; + return $this->ADORecordSet($id); + } + + /* 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; + $ibf = ibase_field_info($this->_queryID,$fieldOffset); + $fld->name = strtolower($ibf['alias']); + if (empty($fld->name)) $fld->name = strtolower($ibf['name']); + $fld->type = $ibf['type']; + $fld->max_length = $ibf['length']; + return $fld; + } + + function _initrs() + { + $this->_numOfRows = -1; + $this->_numOfFields = @ibase_num_fields($this->_queryID); + + /* cache types for blob decode check */ + for ($i=0, $max = $this->_numOfFields; $i < $max; $i++) { + $f1 = $this->FetchField($i); + $this->_cacheType[] = $f1->type; + } + } + + function _seek($row) + { + return false; + } + + function _fetch() + { + $f = @ibase_fetch_row($this->_queryID); + if ($f === false) { + $this->fields = false; + return false; + } + /* OPN stuff start - optimized */ + /* fix missing nulls and decode blobs automatically */ + for ($i=0, $max = $this->_numOfFields; $i < $max; $i++) { + if ($this->_cacheType[$i]=="BLOB") { + if (isset($f[$i])) { + $f[$i] = ADODB_ibase::_BlobDecode($f[$i]); + } else { + $f[$i] = null; + } + } else { + if (!isset($f[$i])) { + $f[$i] = null; + } + } + } + /* OPN stuff end */ + + $this->fields = $f; + if ($this->fetchMode & ADODB_FETCH_ASSOC) { + $this->fields = $this->GetRowAssoc(ADODB_ASSOC_CASE); + } + return true; + } + + /* Use associative array to get fields array */ + function Fields($colname) + { + if ($this->fetchMode & ADODB_FETCH_ASSOC) 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)]]; + + } + + + function _close() + { + return @ibase_free_result($this->_queryID); + } + + function MetaType($t,$len=-1,$fieldobj=false) + { + if (is_object($t)) { + $fieldobj = $t; + $t = $fieldobj->type; + $len = $fieldobj->max_length; + } + switch (strtoupper($t)) { + case 'CHAR': + return 'C'; + + case 'TEXT': + case 'VARCHAR': + case 'VARYING': + if ($len <= $this->blobSize) return 'C'; + return 'X'; + case 'BLOB': + return 'B'; + + case 'TIMESTAMP': + case 'DATE': return 'D'; + + /* case 'T': return 'T'; */ + + /* case 'L': return 'L'; */ + case 'INT': + case 'SHORT': + case 'INTEGER': return 'I'; + default: return 'N'; + } + } + +} ?> \ No newline at end of file diff --git a/lib/adodb/drivers/adodb-informix.inc.php b/lib/adodb/drivers/adodb-informix.inc.php index cd6c1fbc0a..4bb81b105c 100644 --- a/lib/adodb/drivers/adodb-informix.inc.php +++ b/lib/adodb/drivers/adodb-informix.inc.php @@ -1,30 +1,30 @@ -ADORecordset_informix72($id,$mode); - } -} +ADORecordset_informix72($id,$mode); + } +} ?> \ No newline at end of file diff --git a/lib/adodb/drivers/adodb-informix72.inc.php b/lib/adodb/drivers/adodb-informix72.inc.php index 2b1804160d..b554feb738 100644 --- a/lib/adodb/drivers/adodb-informix72.inc.php +++ b/lib/adodb/drivers/adodb-informix72.inc.php @@ -1,314 +1,315 @@ - - -*/ - -class ADODB_informix72 extends ADOConnection { - var $databaseType = "informix72"; - var $dataProvider = "informix"; - 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 $hasAffectedRows = true; - var $metaTablesSQL="select tabname from systables"; - var $metaColumnsSQL = "select colname, coltype, collength from syscolumns c, systables t where c.tabid=t.tabid and tabname='%s'"; - var $concat_operator = '||'; - - var $lastQuery = false; - var $has_insertid = true; - - var $_autocommit = true; - var $_bindInputArray = true; // set to true if ADOConnection.Execute() permits binding of array parameters. - var $sysDate = 'TODAY'; - var $sysTimeStamp = 'CURRENT'; - - function ADODB_informix72() - { - - // alternatively, use older method: - //putenv("DBDATE=Y4MD-"); - - // force ISO date format - putenv('GL_DATE=%Y-%m-%d'); - } - - function _insertid() - { - $sqlca =ifx_getsqlca($this->lastQuery); - return @$sqlca["sqlerrd1"]; - } - - function _affectedrows() - { - if ($this->lastQuery) { - return ifx_affected_rows ($this->lastQuery); - } else - return 0; - } - - function BeginTrans() - { - if ($this->transOff) return true; - $this->transCnt += 1; - $this->Execute('BEGIN'); - $this->_autocommit = false; - return true; - } - - function CommitTrans($ok=true) - { - if (!$ok) return $this->RollbackTrans(); - if ($this->transOff) return true; - if ($this->transCnt) $this->transCnt -= 1; - $this->Execute('COMMIT'); - $this->_autocommit = true; - return true; - } - - function RollbackTrans() - { - if ($this->transOff) return true; - if ($this->transCnt) $this->transCnt -= 1; - $this->Execute('ROLLBACK'); - $this->_autocommit = true; - return true; - } - - function RowLock($tables,$where) - { - if ($this->_autocommit) $this->BeginTrans(); - return $this->GetOne("select 1 as ignore from $tables where $where for update"); - } - - /* Returns: the last error message from previous database operation - Note: This function is NOT available for Microsoft SQL Server. */ - - function ErrorMsg() { - $this->_errorMsg = ifx_errormsg(); - return $this->_errorMsg; - } - - function ErrorNo() - { - return ifx_error(); - } - - function MetaColumns($table) - { - return ADOConnection::MetaColumns($table,false); - } - - function UpdateBlob($table, $column, $val, $where, $blobtype = 'BLOB') - { - $type = ($blobtype == 'TEXT') ? 1 : 0; - $blobid = ifx_create_blob($type,0,$val); - return $this->Execute("UPDATE $table SET $column=(?) WHERE $where",array($blobid)); - } - - function BlobDecode($blobid) - { - return @ifx_get_blob($blobid); - } - // returns true or false - function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) - { - $dbs = $argDatabasename . "@" . $argHostname; - $this->_connectionID = ifx_connect($dbs,$argUsername,$argPassword); - if ($this->_connectionID === false) return false; - #if ($argDatabasename) return $this->SelectDB($argDatabasename); - return true; - } - - // returns true or false - function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) - { - $dbs = $argDatabasename . "@" . $argHostname; - $this->_connectionID = ifx_pconnect($dbs,$argUsername,$argPassword); - if ($this->_connectionID === false) return false; - #if ($argDatabasename) return $this->SelectDB($argDatabasename); - return true; - } -/* - // ifx_do does not accept bind parameters - wierd ??? - function Prepare($sql) - { - $stmt = ifx_prepare($sql); - if (!$stmt) return $sql; - else return array($sql,$stmt); - } -*/ - // returns query ID if successful, otherwise false - function _query($sql,$inputarr) - { - global $ADODB_COUNTRECS; - - // String parameters have to be converted using ifx_create_char - if ($inputarr) { - foreach($inputarr as $v) { - if (gettype($v) == 'string') { - $tab[] = ifx_create_char($v); - } - else { - $tab[] = $v; - } - } - } - - // In case of select statement, we use a scroll cursor in order - // to be able to call "move", or "movefirst" statements - if (!$ADODB_COUNTRECS && preg_match("/^\s*select/i", $sql)) { - if ($inputarr) { - $this->lastQuery = ifx_query($sql,$this->_connectionID, IFX_SCROLL, $tab); - } - else { - $this->lastQuery = ifx_query($sql,$this->_connectionID, IFX_SCROLL); - } - } - else { - if ($inputarr) { - $this->lastQuery = ifx_query($sql,$this->_connectionID, $tab); - } - else { - $this->lastQuery = ifx_query($sql,$this->_connectionID); - } - } - - // Following line have been commented because autocommit mode is - // not supported by informix SE 7.2 - - //if ($this->_autocommit) ifx_query('COMMIT',$this->_connectionID); - - return $this->lastQuery; - } - - // returns true or false - function _close() - { - $this->lastQuery = false; - return ifx_close($this->_connectionID); - } -} - - -/*-------------------------------------------------------------------------------------- - Class Name: Recordset ---------------------------------------------------------------------------------------*/ - -class ADORecordset_informix72 extends ADORecordSet { - - var $databaseType = "informix72"; - var $canSeek = true; - var $_fieldprops = false; - - function ADORecordset_informix72($id,$mode=false) - { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - $this->fetchMode = $mode; - return $this->ADORecordSet($id); - } - - - - /* 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 (empty($this->_fieldprops)) { - $fp = ifx_fieldproperties($this->_queryID); - foreach($fp as $k => $v) { - $o = new ADOFieldObject; - $o->name = $k; - $arr = split(';',$v); //"SQLTYPE;length;precision;scale;ISNULLABLE" - $o->type = $arr[0]; - $o->max_length = $arr[1]; - $this->_fieldprops[] = $o; - } - } - return $this->_fieldprops[$fieldOffset]; - } - - function _initrs() - { - $this->_numOfRows = -1; // ifx_affected_rows not reliable, only returns estimate -- ($ADODB_COUNTRECS)? ifx_affected_rows($this->_queryID):-1; - $this->_numOfFields = ifx_num_fields($this->_queryID); - } - - function _seek($row) - { - return @ifx_fetch_row($this->_queryID, $row); - } - - function MoveLast() - { - $this->fields = @ifx_fetch_row($this->_queryID, "LAST"); - if ($this->fields) $this->EOF = false; - $this->_currentRow = -1; - - if ($this->fetchMode == ADODB_FETCH_NUM) { - foreach($this->fields as $v) { - $arr[] = $v; - } - $this->fields = $arr; - } - - return true; - } - - function MoveFirst() - { - $this->fields = @ifx_fetch_row($this->_queryID, "FIRST"); - if ($this->fields) $this->EOF = false; - $this->_currentRow = 0; - - if ($this->fetchMode == ADODB_FETCH_NUM) { - foreach($this->fields as $v) { - $arr[] = $v; - } - $this->fields = $arr; - } - - return true; - } - - function _fetch($ignore_fields=false) - { - - $this->fields = @ifx_fetch_row($this->_queryID); - - if (!is_array($this->fields)) return false; - - if ($this->fetchMode == ADODB_FETCH_NUM) { - foreach($this->fields as $v) { - $arr[] = $v; - } - $this->fields = $arr; - } - return true; - } - - /* 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() - { - return ifx_free_result($this->_queryID); - } - -} + + +*/ + +class ADODB_informix72 extends ADOConnection { + var $databaseType = "informix72"; + var $dataProvider = "informix"; + 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 $hasAffectedRows = true; + var $metaTablesSQL="select tabname from systables"; + var $metaColumnsSQL = "select colname, coltype, collength from syscolumns c, systables t where c.tabid=t.tabid and tabname='%s'"; + var $concat_operator = '||'; + + var $lastQuery = false; + var $has_insertid = true; + + var $_autocommit = true; + var $_bindInputArray = true; /* set to true if ADOConnection.Execute() permits binding of array parameters. */ + var $sysDate = 'TODAY'; + var $sysTimeStamp = 'CURRENT'; + + function ADODB_informix72() + { + + /* alternatively, use older method: */ + /* putenv("DBDATE=Y4MD-"); */ + + /* force ISO date format */ + putenv('GL_DATE=%Y-%m-%d'); + } + + function _insertid() + { + $sqlca =ifx_getsqlca($this->lastQuery); + return @$sqlca["sqlerrd1"]; + } + + function _affectedrows() + { + if ($this->lastQuery) { + return ifx_affected_rows ($this->lastQuery); + } else + return 0; + } + + function BeginTrans() + { + if ($this->transOff) return true; + $this->transCnt += 1; + $this->Execute('BEGIN'); + $this->_autocommit = false; + return true; + } + + function CommitTrans($ok=true) + { + if (!$ok) return $this->RollbackTrans(); + if ($this->transOff) return true; + if ($this->transCnt) $this->transCnt -= 1; + $this->Execute('COMMIT'); + $this->_autocommit = true; + return true; + } + + function RollbackTrans() + { + if ($this->transOff) return true; + if ($this->transCnt) $this->transCnt -= 1; + $this->Execute('ROLLBACK'); + $this->_autocommit = true; + return true; + } + + function RowLock($tables,$where) + { + if ($this->_autocommit) $this->BeginTrans(); + return $this->GetOne("select 1 as ignore from $tables where $where for update"); + } + + /* Returns: the last error message from previous database operation + Note: This function is NOT available for Microsoft SQL Server. */ + + function ErrorMsg() { + $this->_errorMsg = ifx_errormsg(); + return $this->_errorMsg; + } + + function ErrorNo() + { + return ifx_error(); + } + + function &MetaColumns($table) + { + return ADOConnection::MetaColumns($table,false); + } + + function UpdateBlob($table, $column, $val, $where, $blobtype = 'BLOB') + { + $type = ($blobtype == 'TEXT') ? 1 : 0; + $blobid = ifx_create_blob($type,0,$val); + return $this->Execute("UPDATE $table SET $column=(?) WHERE $where",array($blobid)); + } + + function BlobDecode($blobid) + { + return @ifx_get_blob($blobid); + } + /* returns true or false */ + function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + $dbs = $argDatabasename . "@" . $argHostname; + $this->_connectionID = ifx_connect($dbs,$argUsername,$argPassword); + if ($this->_connectionID === false) return false; + #if ($argDatabasename) return $this->SelectDB($argDatabasename); + return true; + } + + /* returns true or false */ + function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + $dbs = $argDatabasename . "@" . $argHostname; + $this->_connectionID = ifx_pconnect($dbs,$argUsername,$argPassword); + if ($this->_connectionID === false) return false; + #if ($argDatabasename) return $this->SelectDB($argDatabasename); + return true; + } +/* + // ifx_do does not accept bind parameters - wierd ??? + function Prepare($sql) + { + $stmt = ifx_prepare($sql); + if (!$stmt) return $sql; + else return array($sql,$stmt); + } +*/ + /* returns query ID if successful, otherwise false */ + function _query($sql,$inputarr) + { + global $ADODB_COUNTRECS; + + /* String parameters have to be converted using ifx_create_char */ + if ($inputarr) { + foreach($inputarr as $v) { + if (gettype($v) == 'string') { + $tab[] = ifx_create_char($v); + } + else { + $tab[] = $v; + } + } + } + + /* In case of select statement, we use a scroll cursor in order */ + /* to be able to call "move", or "movefirst" statements */ + if (!$ADODB_COUNTRECS && preg_match("/^\s*select/is", $sql)) { + if ($inputarr) { + $this->lastQuery = ifx_query($sql,$this->_connectionID, IFX_SCROLL, $tab); + } + else { + $this->lastQuery = ifx_query($sql,$this->_connectionID, IFX_SCROLL); + } + } + else { + if ($inputarr) { + $this->lastQuery = ifx_query($sql,$this->_connectionID, $tab); + } + else { + $this->lastQuery = ifx_query($sql,$this->_connectionID); + } + } + + /* Following line have been commented because autocommit mode is */ + /* not supported by informix SE 7.2 */ + + /* if ($this->_autocommit) ifx_query('COMMIT',$this->_connectionID); */ + + return $this->lastQuery; + } + + /* returns true or false */ + function _close() + { + $this->lastQuery = false; + return ifx_close($this->_connectionID); + } +} + + +/*-------------------------------------------------------------------------------------- + Class Name: Recordset +--------------------------------------------------------------------------------------*/ + +class ADORecordset_informix72 extends ADORecordSet { + + var $databaseType = "informix72"; + var $canSeek = true; + var $_fieldprops = false; + + function ADORecordset_informix72($id,$mode=false) + { + if ($mode === false) { + global $ADODB_FETCH_MODE; + $mode = $ADODB_FETCH_MODE; + } + $this->fetchMode = $mode; + return $this->ADORecordSet($id); + } + + + + /* 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 (empty($this->_fieldprops)) { + $fp = ifx_fieldproperties($this->_queryID); + foreach($fp as $k => $v) { + $o = new ADOFieldObject; + $o->name = $k; + $arr = split(';',$v); /* "SQLTYPE;length;precision;scale;ISNULLABLE" */ + $o->type = $arr[0]; + $o->max_length = $arr[1]; + $this->_fieldprops[] = $o; + $o->not_null = $arr[4]=="N"; + } + } + return $this->_fieldprops[$fieldOffset]; + } + + function _initrs() + { + $this->_numOfRows = -1; /* ifx_affected_rows not reliable, only returns estimate -- ($ADODB_COUNTRECS)? ifx_affected_rows($this->_queryID):-1; */ + $this->_numOfFields = ifx_num_fields($this->_queryID); + } + + function _seek($row) + { + return @ifx_fetch_row($this->_queryID, $row); + } + + function MoveLast() + { + $this->fields = @ifx_fetch_row($this->_queryID, "LAST"); + if ($this->fields) $this->EOF = false; + $this->_currentRow = -1; + + if ($this->fetchMode == ADODB_FETCH_NUM) { + foreach($this->fields as $v) { + $arr[] = $v; + } + $this->fields = $arr; + } + + return true; + } + + function MoveFirst() + { + $this->fields = @ifx_fetch_row($this->_queryID, "FIRST"); + if ($this->fields) $this->EOF = false; + $this->_currentRow = 0; + + if ($this->fetchMode == ADODB_FETCH_NUM) { + foreach($this->fields as $v) { + $arr[] = $v; + } + $this->fields = $arr; + } + + return true; + } + + function _fetch($ignore_fields=false) + { + + $this->fields = @ifx_fetch_row($this->_queryID); + + if (!is_array($this->fields)) return false; + + if ($this->fetchMode == ADODB_FETCH_NUM) { + foreach($this->fields as $v) { + $arr[] = $v; + } + $this->fields = $arr; + } + return true; + } + + /* 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() + { + return ifx_free_result($this->_queryID); + } + +} ?> \ No newline at end of file diff --git a/lib/adodb/drivers/adodb-mssql.inc.php b/lib/adodb/drivers/adodb-mssql.inc.php index d713cc9a8d..7b5342c1ae 100644 --- a/lib/adodb/drivers/adodb-mssql.inc.php +++ b/lib/adodb/drivers/adodb-mssql.inc.php @@ -1,731 +1,762 @@ -= 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_mssql extends ADOConnection { - var $databaseType = "mssql"; - var $dataProvider = "mssql"; - var $replaceQuote = "''"; // string to use to replace quotes - var $fmtDate = "'Y-m-d'"; - var $fmtTimeStamp = "'Y-m-d h:i:sA'"; - var $hasInsertID = true; - var $hasAffectedRows = true; - var $metaTablesSQL="select name 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'))"; - var $metaColumnsSQL = "select c.name,t.name,c.length 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 $_has_mssql_init; - var $maxParameterLen = 4000; - var $arrayClass = 'ADORecordSet_array_mssql'; - var $uniqueSort = true; - var $leftOuter = '*='; - var $rightOuter = '=*'; - var $ansiOuter = true; // for mssql7 or later - var $poorAffectedRows = true; - var $identitySQL = 'select @@IDENTITY'; // 'select SCOPE_IDENTITY'; # for mssql 2000 - - - function ADODB_mssql() - { - $this->_has_mssql_init = (strnatcmp(PHP_VERSION,'4.1.0')>=0); - } - - function ServerInfo() - { - global $ADODB_FETCH_MODE; - - $stmt = $this->PrepareSP('sp_server_info'); - $val = 2; - if ($this->fetchMode === false) { - $savem = $ADODB_FETCH_MODE; - $ADODB_FETCH_MODE = ADODB_FETCH_NUM; - } else - $savem = $this->SetFetchMode(ADODB_FETCH_NUM); - - - $this->Parameter($stmt,$val,'attribute_id'); - $row = $this->GetRow($stmt); - - //$row = $this->GetRow("execute sp_server_info 2"); - - if ($this->fetchMode === false) { - $ADODB_FETCH_MODE = $savem; - } else - $this->SetFetchMode($savem); - - $arr['description'] = $row[2]; - $arr['version'] = ADOConnection::_findvers($arr['description']); - return $arr; - } - - 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 $this->GetOne('select @@rowcount'); - } - - var $_dropSeqSQL = "drop table %s"; - - function CreateSequence($seq='adodbseq',$start=1) - { - $start -= 1; - $this->Execute("create table $seq (id float(53))"); - $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)"); - if (!$ok) { - $this->Execute('ROLLBACK TRANSACTION adodbseq'); - return false; - } - $this->Execute('COMMIT TRANSACTION adodbseq'); - return true; - } - - function GenID($seq='adodbseq',$start=1) - { - //$this->debug=1; - $this->Execute('BEGIN TRANSACTION adodbseq'); - $ok = $this->Execute("update $seq with (tablock,holdlock) set id = id + 1"); - if (!$ok) { - $this->Execute("create table $seq (id float(53))"); - $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)"); - if (!$ok) { - $this->Execute('ROLLBACK TRANSACTION adodbseq'); - return false; - } - $this->Execute('COMMIT TRANSACTION adodbseq'); - return $start; - } - $num = $this->GetOne("select id from $seq"); - $this->Execute('COMMIT TRANSACTION adodbseq'); - return $num; - - // in old implementation, pre 1.90, we returned GUID... - //return $this->GetOne("SELECT CONVERT(varchar(255), NEWID()) AS 'Char'"); - } - - // Format date column in sql string given an input format that understands Y M D - function SQLDate($fmt, $col=false) - { - if (!$col) $col = $this->sysDate; - $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': - 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; - 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; - $this->Execute('BEGIN TRAN'); - return true; - } - - 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; - } - function RollbackTrans() - { - if ($this->transOff) return true; - if ($this->transCnt) $this->transCnt -= 1; - $this->Execute('ROLLBACK TRAN'); - return true; - } - - /* - 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) - { - if (!$this->transCnt) $this->BeginTrans(); - return $this->GetOne("select top 1 null as ignore from $tables with (ROWLOCK,HOLDLOCK) where $where"); - } - - //From: Fernando Moreira - function MetaDatabases() - { - if(@mssql_select_db("master")) { - $qry="select name from sysdatabases where name <> 'master'"; - if($rs=@mssql_query($qry)){ - $tmpAr=$ar=array(); - while($tmpAr=@mssql_fetch_row($rs)) - $ar[]=$tmpAr[0]; - @mssql_select_db($this->databaseName); - if(sizeof($ar)) - return($ar); - else - return(false); - } else { - @mssql_select_db($this->databaseName); - return(false); - } - } - return(false); - } - - // "Stein-Aksel Basma" - // tested with MSSQL 2000 - function MetaPrimaryKeys($table) - { - $sql = "select k.column_name 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'"; - - $a = $this->GetCol($sql); - if ($a && sizeof($a)>0) return $a; - return false; - } - - - - function SelectDB($dbName) - { - $this->databaseName = $dbName; - if ($this->_connectionID) { - return @mssql_select_db($dbName); - } - else return false; - } - - function ErrorMsg() - { - if (empty($this->_errorMsg)){ - $this->_errorMsg = mssql_get_last_message(); - } - return $this->_errorMsg; - } - - function ErrorNo() - { - if (empty($this->_errorMsg)) { - $this->_errorMsg = mssql_get_last_message(); - } - $id = @mssql_query("select @@ERROR",$this->_connectionID); - if (!$id) return false; - $arr = mssql_fetch_array($id); - @mssql_free_result($id); - if (is_array($arr)) return $arr[0]; - else return -1; - } - - // returns true or false - function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) - { - $this->_connectionID = mssql_connect($argHostname,$argUsername,$argPassword); - if ($this->_connectionID === false) return false; - if ($argDatabasename) return $this->SelectDB($argDatabasename); - return true; - } - - - // returns true or false - function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) - { - $this->_connectionID = mssql_pconnect($argHostname,$argUsername,$argPassword); - if ($this->_connectionID === false) return false; - - // persistent connections can forget to rollback on crash, so we do it here. - if ($this->autoRollback) { - $cnt = $this->GetOne('select @@TRANCOUNT'); - while (--$cnt >= 0) $this->Execute('ROLLBACK TRAN'); - } - if ($argDatabasename) return $this->SelectDB($argDatabasename); - return true; - } - - function Prepare($sql) - { - return $sql; - } - - function PrepareSP($sql) - { - if (!$this->_has_mssql_init) { - ADOConnection::outp( "PrepareSP: mssql_init only available since PHP 4.1.0"); - return $sql; - } - $stmt = mssql_init($sql,$this->_connectionID); - if (!$stmt) return $sql; - return array($sql,$stmt); - } - - /* - Usage: - $stmt = $db->PrepareSP('SP_RUNSOMETHING'); -- takes 2 params, @myid and @group - - # note that the parameter does not have @ in front! - $db->Parameter($stmt,$id,'myid'); - $db->Parameter($stmt,$group,'group',false,64); - $db->Execute($stmt); - - @param $stmt Statement returned by Prepare() or PrepareSP(). - @param $var PHP variable to bind to. Can set to null (for isNull support). - @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 mssql_bind documentation at php.net. - */ - function Parameter(&$stmt, &$var, $name, $isOutput=false, $maxLen=4000, $type=false) - { - if (!$this->_has_mssql_init) { - ADOConnection::outp( "Parameter: mssql_bind only available since PHP 4.1.0"); - return $sql; - } - - $isNull = is_null($var); // php 4.0.4 and above... - - if ($type === false) - switch(gettype($var)) { - default: - case 'string': $type = SQLCHAR; break; - case 'double': $type = SQLFLT8; break; - case 'integer': $type = SQLINT4; break; - case 'boolean': $type = SQLINT1; break; # SQLBIT not supported in 4.1.0 - } - - if ($this->debug) { - ADOConnection::outp( "Parameter(\$stmt, \$php_var='$var', \$name='$name'); (type=$type)"); - } - return mssql_bind($stmt[1], '@'.$name, $var, $type, $isOutput, $isNull, $maxLen); - } - - /* - 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') - { - $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) - { - $this->_errorMsg = false; - if (is_array($sql)) $rez = mssql_execute($sql[1]); - else $rez = mssql_query($sql,$this->_connectionID); - return $rez; - } - - // returns true or false - function _close() - { - if ($this->transCnt) $this->RollbackTrans(); - $rez = @mssql_close($this->_connectionID); - $this->_connectionID = false; - return $rez; - } - - // mssql uses a default date like Dec 30 2000 12:00AM - function UnixDate($v) - { - return ADORecordSet_array_mssql::UnixDate($v); - } - - function UnixTimeStamp($v) - { - return ADORecordSet_array_mssql::UnixTimeStamp($v); - } -} - -/*-------------------------------------------------------------------------------------- - Class Name: Recordset ---------------------------------------------------------------------------------------*/ - -class ADORecordset_mssql extends ADORecordSet { - - var $databaseType = "mssql"; - var $canSeek = true; - // _mths works only in non-localised system - - function ADORecordset_mssql($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; - $this->_numOfRows = ($ADODB_COUNTRECS)? @mssql_num_rows($this->_queryID):-1; - $this->_numOfFields = @mssql_num_fields($this->_queryID); - } - - - //Contributed by "Sven Axelsson" - // get next resultset - requires PHP 4.0.5 or later - function NextRecordSet() - { - if (!mssql_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 ($fieldOffset != -1) { - return @mssql_fetch_field($this->_queryID, $fieldOffset); - } - else if ($fieldOffset == -1) { /* The $fieldOffset argument is not provided thus its -1 */ - return @mssql_fetch_field($this->_queryID); - } - return null; - } - - function _seek($row) - { - return @mssql_data_seek($this->_queryID, $row); - } - - // speedup - function MoveNext() - { - if ($this->EOF) return false; - - $this->_currentRow++; - - if ($this->fetchMode & ADODB_FETCH_ASSOC) { - if ($this->fetchMode & ADODB_FETCH_NUM) { - //ADODB_FETCH_BOTH mode - $this->fields = @mssql_fetch_array($this->_queryID); - } - else { - if (ADODB_PHPVER >= 0x4200) {// only for PHP 4.2.0 or later - $this->fields = @mssql_fetch_assoc($this->_queryID); - } else { - $flds = @mssql_fetch_array($this->_queryID); - if (is_array($flds)) { - $fassoc = array(); - foreach($flds as $k => $v) { - if (is_numeric($k)) continue; - $fassoc[$k] = $v; - } - $this->fields = $fassoc; - } - } - } - - if (is_array($this->fields)) { - 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 { - $this->fields = @mssql_fetch_row($this->_queryID); - } - if ($this->fields) return true; - $this->EOF = true; - - 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->fetchMode & ADODB_FETCH_ASSOC) { - if ($this->fetchMode & ADODB_FETCH_NUM) { - //ADODB_FETCH_BOTH mode - $this->fields = @mssql_fetch_array($this->_queryID); - } else { - if (ADODB_PHPVER >= 0x4200) // only for PHP 4.2.0 or later - $this->fields = @mssql_fetch_assoc($this->_queryID); - else { - $this->fields = @mssql_fetch_array($this->_queryID); - if (is_array($$this->fields)) { - $fassoc = array(); - foreach($$this->fields as $k => $v) { - if (is_integer($k)) continue; - $fassoc[$k] = $v; - } - $this->fields = $fassoc; - } - } - } - - if (!$this->fields) { - } else 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 { - $this->fields = @mssql_fetch_row($this->_queryID); - } - 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 = mssql_free_result($this->_queryID); - $this->_queryID = false; - return $rez; - } - // mssql uses a default date like Dec 30 2000 12:00AM - function UnixDate($v) - { - return ADORecordSet_array_mssql::UnixDate($v); - } - - function UnixTimeStamp($v) - { - return ADORecordSet_array_mssql::UnixTimeStamp($v); - } - -} - - -class ADORecordSet_array_mssql extends ADORecordSet_array { - function ADORecordSet_array_mssql($id=-1,$mode=false) - { - $this->ADORecordSet_array($id,$mode); - } - - // mssql uses a default date like Dec 30 2000 12:00AM - 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]); - } - - 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]); - } -} - += 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_mssql extends ADOConnection { + var $databaseType = "mssql"; + var $dataProvider = "mssql"; + var $replaceQuote = "''"; /* string to use to replace quotes */ + var $fmtDate = "'Y-m-d'"; + var $fmtTimeStamp = "'Y-m-d h:i:sA'"; + var $hasInsertID = true; + var $hasAffectedRows = true; + var $metaDatabasesSQL = "select name from sysdatabases where name <> 'master'"; + var $metaTablesSQL="select name 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 $_has_mssql_init; + var $maxParameterLen = 4000; + var $arrayClass = 'ADORecordSet_array_mssql'; + var $uniqueSort = true; + var $leftOuter = '*='; + var $rightOuter = '=*'; + var $ansiOuter = true; /* for mssql7 or later */ + var $poorAffectedRows = true; + var $identitySQL = 'select @@IDENTITY'; /* 'select SCOPE_IDENTITY'; # for mssql 2000 */ + var $uniqueOrderBy = true; + + function ADODB_mssql() + { + $this->_has_mssql_init = (strnatcmp(PHP_VERSION,'4.1.0')>=0); + } + + function ServerInfo() + { + global $ADODB_FETCH_MODE; + + $stmt = $this->PrepareSP('sp_server_info'); + $val = 2; + if ($this->fetchMode === false) { + $savem = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + } else + $savem = $this->SetFetchMode(ADODB_FETCH_NUM); + + + $this->Parameter($stmt,$val,'attribute_id'); + $row = $this->GetRow($stmt); + + /* $row = $this->GetRow("execute sp_server_info 2"); */ + + if ($this->fetchMode === false) { + $ADODB_FETCH_MODE = $savem; + } else + $this->SetFetchMode($savem); + + $arr['description'] = $row[2]; + $arr['version'] = ADOConnection::_findvers($arr['description']); + return $arr; + } + + 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 $this->GetOne('select @@rowcount'); + } + + var $_dropSeqSQL = "drop table %s"; + + function CreateSequence($seq='adodbseq',$start=1) + { + $start -= 1; + $this->Execute("create table $seq (id float(53))"); + $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)"); + if (!$ok) { + $this->Execute('ROLLBACK TRANSACTION adodbseq'); + return false; + } + $this->Execute('COMMIT TRANSACTION adodbseq'); + return true; + } + + function GenID($seq='adodbseq',$start=1) + { + /* $this->debug=1; */ + $this->Execute('BEGIN TRANSACTION adodbseq'); + $ok = $this->Execute("update $seq with (tablock,holdlock) set id = id + 1"); + if (!$ok) { + $this->Execute("create table $seq (id float(53))"); + $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)"); + if (!$ok) { + $this->Execute('ROLLBACK TRANSACTION adodbseq'); + return false; + } + $this->Execute('COMMIT TRANSACTION adodbseq'); + return $start; + } + $num = $this->GetOne("select id from $seq"); + $this->Execute('COMMIT TRANSACTION adodbseq'); + return $num; + + /* in old implementation, pre 1.90, we returned GUID... */ + /* return $this->GetOne("SELECT CONVERT(varchar(255), NEWID()) AS 'Char'"); */ + } + + /* 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(mi,$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; + $this->Execute('BEGIN TRAN'); + return true; + } + + 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; + } + function RollbackTrans() + { + if ($this->transOff) return true; + if ($this->transCnt) $this->transCnt -= 1; + $this->Execute('ROLLBACK TRAN'); + return true; + } + + /* + 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) + { + if (!$this->transCnt) $this->BeginTrans(); + return $this->GetOne("select top 1 null as ignore from $tables with (ROWLOCK,HOLDLOCK) where $where"); + } + + /* From: Fernando Moreira */ + function MetaDatabases() + { + if(@mssql_select_db("master")) { + $qry=$this->metaDatabasesSQL; + if($rs=@mssql_query($qry)){ + $tmpAr=$ar=array(); + while($tmpAr=@mssql_fetch_row($rs)) + $ar[]=$tmpAr[0]; + @mssql_select_db($this->databaseName); + if(sizeof($ar)) + return($ar); + else + return(false); + } else { + @mssql_select_db($this->databaseName); + return(false); + } + } + return(false); + } + + /* "Stein-Aksel Basma" */ + /* tested with MSSQL 2000 */ + function MetaPrimaryKeys($table) + { + $sql = "select k.column_name 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'"; + + $a = $this->GetCol($sql); + if ($a && sizeof($a)>0) return $a; + return false; + } + + + + function SelectDB($dbName) + { + $this->databaseName = $dbName; + if ($this->_connectionID) { + return @mssql_select_db($dbName); + } + else return false; + } + + function ErrorMsg() + { + if (empty($this->_errorMsg)){ + $this->_errorMsg = mssql_get_last_message(); + } + return $this->_errorMsg; + } + + function ErrorNo() + { + if (empty($this->_errorMsg)) { + $this->_errorMsg = mssql_get_last_message(); + } + $id = @mssql_query("select @@ERROR",$this->_connectionID); + if (!$id) return false; + $arr = mssql_fetch_array($id); + @mssql_free_result($id); + if (is_array($arr)) return $arr[0]; + else return -1; + } + + /* returns true or false */ + function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + $this->_connectionID = mssql_connect($argHostname,$argUsername,$argPassword); + if ($this->_connectionID === false) return false; + if ($argDatabasename) return $this->SelectDB($argDatabasename); + return true; + } + + + /* returns true or false */ + function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + $this->_connectionID = mssql_pconnect($argHostname,$argUsername,$argPassword); + if ($this->_connectionID === false) return false; + + /* persistent connections can forget to rollback on crash, so we do it here. */ + if ($this->autoRollback) { + $cnt = $this->GetOne('select @@TRANCOUNT'); + while (--$cnt >= 0) $this->Execute('ROLLBACK TRAN'); + } + if ($argDatabasename) return $this->SelectDB($argDatabasename); + return true; + } + + function Prepare($sql) + { + return $sql; + } + + function PrepareSP($sql) + { + if (!$this->_has_mssql_init) { + ADOConnection::outp( "PrepareSP: mssql_init only available since PHP 4.1.0"); + return $sql; + } + $stmt = mssql_init($sql,$this->_connectionID); + if (!$stmt) return $sql; + return array($sql,$stmt); + } + + /* + Usage: + $stmt = $db->PrepareSP('SP_RUNSOMETHING'); -- takes 2 params, @myid and @group + + # note that the parameter does not have @ in front! + $db->Parameter($stmt,$id,'myid'); + $db->Parameter($stmt,$group,'group',false,64); + $db->Execute($stmt); + + @param $stmt Statement returned by Prepare() or PrepareSP(). + @param $var PHP variable to bind to. Can set to null (for isNull support). + @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 mssql_bind documentation at php.net. + */ + function Parameter(&$stmt, &$var, $name, $isOutput=false, $maxLen=4000, $type=false) + { + if (!$this->_has_mssql_init) { + ADOConnection::outp( "Parameter: mssql_bind only available since PHP 4.1.0"); + return $sql; + } + + $isNull = is_null($var); /* php 4.0.4 and above... */ + + if ($type === false) + switch(gettype($var)) { + default: + case 'string': $type = SQLCHAR; break; + case 'double': $type = SQLFLT8; break; + case 'integer': $type = SQLINT4; break; + case 'boolean': $type = SQLINT1; break; # SQLBIT not supported in 4.1.0 + } + + if ($this->debug) { + ADOConnection::outp( "Parameter(\$stmt, \$php_var='$var', \$name='$name'); (type=$type)"); + } + return mssql_bind($stmt[1], '@'.$name, $var, $type, $isOutput, $isNull, $maxLen); + } + + /* + 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') + { + $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) + { + $this->_errorMsg = false; + if (is_array($sql)) $rez = mssql_execute($sql[1]); + else $rez = mssql_query($sql,$this->_connectionID); + return $rez; + } + + /* returns true or false */ + function _close() + { + if ($this->transCnt) $this->RollbackTrans(); + $rez = @mssql_close($this->_connectionID); + $this->_connectionID = false; + return $rez; + } + + /* mssql uses a default date like Dec 30 2000 12:00AM */ + function UnixDate($v) + { + return ADORecordSet_array_mssql::UnixDate($v); + } + + function UnixTimeStamp($v) + { + return ADORecordSet_array_mssql::UnixTimeStamp($v); + } +} + +/*-------------------------------------------------------------------------------------- + Class Name: Recordset +--------------------------------------------------------------------------------------*/ + +class ADORecordset_mssql extends ADORecordSet { + + var $databaseType = "mssql"; + var $canSeek = true; + var $hasFetchAssoc; /* see http://phplens.com/lens/lensforum/msgs.php?id=6083 */ + /* _mths works only in non-localised system */ + + function ADORecordset_mssql($id,$mode=false) + { + /* freedts check... */ + $this->hasFetchAssoc = function_exists('mssql_fetch_assoc'); + + if ($mode === false) { + global $ADODB_FETCH_MODE; + $mode = $ADODB_FETCH_MODE; + } + $this->fetchMode = $mode; + return $this->ADORecordSet($id,$mode); + } + + + function _initrs() + { + GLOBAL $ADODB_COUNTRECS; + $this->_numOfRows = ($ADODB_COUNTRECS)? @mssql_num_rows($this->_queryID):-1; + $this->_numOfFields = @mssql_num_fields($this->_queryID); + } + + + /* Contributed by "Sven Axelsson" */ + /* get next resultset - requires PHP 4.0.5 or later */ + function NextRecordSet() + { + if (!mssql_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 ($fieldOffset != -1) { + return @mssql_fetch_field($this->_queryID, $fieldOffset); + } + else if ($fieldOffset == -1) { /* The $fieldOffset argument is not provided thus its -1 */ + return @mssql_fetch_field($this->_queryID); + } + return null; + } + + function _seek($row) + { + return @mssql_data_seek($this->_queryID, $row); + } + + /* speedup */ + function MoveNext() + { + if ($this->EOF) return false; + + $this->_currentRow++; + + if ($this->fetchMode & ADODB_FETCH_ASSOC) { + if ($this->fetchMode & ADODB_FETCH_NUM) { + /* ADODB_FETCH_BOTH mode */ + $this->fields = @mssql_fetch_array($this->_queryID); + } + else { + if ($this->hasFetchAssoc) {/* only for PHP 4.2.0 or later */ + $this->fields = @mssql_fetch_assoc($this->_queryID); + } else { + $flds = @mssql_fetch_array($this->_queryID); + if (is_array($flds)) { + $fassoc = array(); + foreach($flds as $k => $v) { + if (is_numeric($k)) continue; + $fassoc[$k] = $v; + } + $this->fields = $fassoc; + } else + $this->fields = false; + } + } + + if (is_array($this->fields)) { + 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 { + $this->fields = @mssql_fetch_row($this->_queryID); + } + if ($this->fields) return true; + $this->EOF = true; + + 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->fetchMode & ADODB_FETCH_ASSOC) { + if ($this->fetchMode & ADODB_FETCH_NUM) { + /* ADODB_FETCH_BOTH mode */ + $this->fields = @mssql_fetch_array($this->_queryID); + } else { + if ($this->hasFetchAssoc) /* only for PHP 4.2.0 or later */ + $this->fields = @mssql_fetch_assoc($this->_queryID); + else { + $this->fields = @mssql_fetch_array($this->_queryID); + if (is_array($$this->fields)) { + $fassoc = array(); + foreach($$this->fields as $k => $v) { + if (is_integer($k)) continue; + $fassoc[$k] = $v; + } + $this->fields = $fassoc; + } + } + } + + if (!$this->fields) { + } else 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 { + $this->fields = @mssql_fetch_row($this->_queryID); + } + 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 = mssql_free_result($this->_queryID); + $this->_queryID = false; + return $rez; + } + /* mssql uses a default date like Dec 30 2000 12:00AM */ + function UnixDate($v) + { + return ADORecordSet_array_mssql::UnixDate($v); + } + + function UnixTimeStamp($v) + { + return ADORecordSet_array_mssql::UnixTimeStamp($v); + } + +} + + +class ADORecordSet_array_mssql extends ADORecordSet_array { + function ADORecordSet_array_mssql($id=-1,$mode=false) + { + $this->ADORecordSet_array($id,$mode); + } + + /* mssql uses a default date like Dec 30 2000 12:00AM */ + 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]); + } + + 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]); + } +} + ?> \ No newline at end of file diff --git a/lib/adodb/drivers/adodb-mssqlpo.inc.php b/lib/adodb/drivers/adodb-mssqlpo.inc.php index 6784bba5e7..a677ea3bc7 100644 --- a/lib/adodb/drivers/adodb-mssqlpo.inc.php +++ b/lib/adodb/drivers/adodb-mssqlpo.inc.php @@ -1,59 +1,59 @@ -_has_mssql_init) { - ADOConnection::outp( "PrepareSP: mssql_init only available since PHP 4.1.0"); - return $sql; - } - if (is_string($sql)) $sql = str_replace('||','+',$sql); - $stmt = mssql_init($sql,$this->_connectionID); - if (!$stmt) return $sql; - return array($sql,$stmt); - } - - function _query($sql,$inputarr) - { - if (is_string($sql)) $sql = str_replace('||','+',$sql); - return ADODB_mssql::_query($sql,$inputarr); - } -} - -class ADORecordset_mssqlpo extends ADORecordset_mssql { - var $databaseType = "mssqlpo"; - function ADORecordset_mssqlpo($id,$mode=false) - { - $this->ADORecordset_mssql($id,$mode); - } -} +_has_mssql_init) { + ADOConnection::outp( "PrepareSP: mssql_init only available since PHP 4.1.0"); + return $sql; + } + if (is_string($sql)) $sql = str_replace('||','+',$sql); + $stmt = mssql_init($sql,$this->_connectionID); + if (!$stmt) return $sql; + return array($sql,$stmt); + } + + function _query($sql,$inputarr) + { + if (is_string($sql)) $sql = str_replace('||','+',$sql); + return ADODB_mssql::_query($sql,$inputarr); + } +} + +class ADORecordset_mssqlpo extends ADORecordset_mssql { + var $databaseType = "mssqlpo"; + function ADORecordset_mssqlpo($id,$mode=false) + { + $this->ADORecordset_mssql($id,$mode); + } +} ?> \ No newline at end of file diff --git a/lib/adodb/drivers/adodb-mysql.inc.php b/lib/adodb/drivers/adodb-mysql.inc.php index 4582fb7864..c1a32caf28 100644 --- a/lib/adodb/drivers/adodb-mysql.inc.php +++ b/lib/adodb/drivers/adodb-mysql.inc.php @@ -1,537 +1,564 @@ -GetOne("select version()"); - $arr['version'] = ADOConnection::_findvers($arr['description']); - return $arr; - } - - // if magic quotes disabled, use mysql_real_escape_string() - function qstr($s,$magic_quotes=false) - { - if (!$magic_quotes) { - - if (ADODB_PHPVER >= 0x4300) { - if (is_resource($this->_connectionID)) - return "'".mysql_real_escape_string($s,$this->_connectionID)."'"; - else - return "'".mysql_real_escape_string($s)."'"; - } - if ($this->replaceQuote[0] == '\\'){ - $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s); - } - return "'".str_replace("'",$this->replaceQuote,$s)."'"; - } - - // undo magic quotes for " - $s = str_replace('\\"','"',$s); - return "'$s'"; - } - - function _insertid() - { - return mysql_insert_id($this->_connectionID); - } - - function _affectedrows() - { - return mysql_affected_rows($this->_connectionID); - } - - // See http://www.mysql.com/doc/M/i/Miscellaneous_functions.html - // Reference on Last_Insert_ID on the recommended way to simulate sequences - var $_genIDSQL = "update %s set id=LAST_INSERT_ID(id+1);"; - var $_genSeqSQL = "create table %s (id int not null)"; - var $_genSeq2SQL = "insert into %s values (%s)"; - var $_dropSeqSQL = "drop table %s"; - - function CreateSequence($seqname='adodbseq',$startID=1) - { - if (empty($this->_genSeqSQL)) return false; - $u = strtoupper($seqname); - - $ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname)); - if (!$ok) return false; - return $this->Execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1)); - } - - function GenID($seqname='adodbseq',$startID=1) - { - // post-nuke sets hasGenID to false - if (!$this->hasGenID) return false; - - $getnext = sprintf($this->_genIDSQL,$seqname); - $rs = @$this->Execute($getnext); - if (!$rs) { - $u = strtoupper($seqname); - $this->Execute(sprintf($this->_genSeqSQL,$seqname)); - $this->Execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1)); - $rs = $this->Execute($getnext); - } - $this->genID = mysql_insert_id($this->_connectionID); - - if ($rs) $rs->Close(); - - return $this->genID; - } - - function &MetaDatabases() - { - $qid = mysql_list_dbs($this->_connectionID); - $arr = array(); - $i = 0; - $max = mysql_num_rows($qid); - while ($i < $max) { - $arr[] = mysql_tablename($qid,$i); - $i += 1; - } - return $arr; - } - - - // Format date column in sql string given an input format that understands Y M D - function SQLDate($fmt, $col=false) - { - if (!$col) $col = $this->sysDate; - $s = 'DATE_FORMAT('.$col.",'"; - $concat = false; - $len = strlen($fmt); - for ($i=0; $i < $len; $i++) { - $ch = $fmt[$i]; - switch($ch) { - case 'Y': - case 'y': - $s .= '%Y'; - break; - case 'Q': - case 'q': - $s .= "'),Quarter($col)"; - - if ($len > $i+1) $s .= ",DATE_FORMAT($col,'"; - else $s .= ",('"; - $concat = true; - break; - case 'M': - case 'm': - $s .= '%m'; - break; - case 'D': - case 'd': - $s .= '%d'; - break; - default: - - if ($ch == '\\') { - $i++; - $ch = substr($fmt,$i,1); - } - $s .= $ch; - break; - } - } - $s.="')"; - if ($concat) $s = "CONCAT($s)"; - return $s; - } - - - // returns concatenated string - // much easier to run "mysqld --ansi" or "mysqld --sql-mode=PIPES_AS_CONCAT" and use || operator - function Concat() - { - $s = ""; - $arr = func_get_args(); - $first = true; - /* - foreach($arr as $a) { - if ($first) { - $s = $a; - $first = false; - } else $s .= ','.$a; - }*/ - - // suggestion by andrew005@mnogo.ru - $s = implode(',',$arr); - if (strlen($s) > 0) return "CONCAT($s)"; - else return ''; - } - - function OffsetDate($dayFraction,$date=false) - { - if (!$date) $date = $this->sysDate; - return "from_unixtime(unix_timestamp($date)+($dayFraction)*24*3600)"; - } - - // returns true or false - function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) - { - if (ADODB_PHPVER >= 0x4300) - $this->_connectionID = mysql_connect($argHostname,$argUsername,$argPassword, - $this->forceNewConnect,$this->clientFlags); - else if (ADODB_PHPVER >= 0x4200) - $this->_connectionID = mysql_connect($argHostname,$argUsername,$argPassword, - $this->forceNewConnect); - else - $this->_connectionID = mysql_connect($argHostname,$argUsername,$argPassword); - - if ($this->_connectionID === false) return false; - if ($argDatabasename) return $this->SelectDB($argDatabasename); - return true; - } - - // returns true or false - function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) - { - if (ADODB_PHPVER >= 0x4300) - $this->_connectionID = mysql_pconnect($argHostname,$argUsername,$argPassword,$this->clientFlags); - else - $this->_connectionID = mysql_pconnect($argHostname,$argUsername,$argPassword); - if ($this->_connectionID === false) return false; - if ($this->autoRollback) $this->RollbackTrans(); - if ($argDatabasename) return $this->SelectDB($argDatabasename); - return true; - } - - function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename) - { - $this->forceNewConnect = true; - $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename); - } - - function &MetaColumns($table) - { - - if ($this->metaColumnsSQL) { - global $ADODB_FETCH_MODE; - - $save = $ADODB_FETCH_MODE; - $ADODB_FETCH_MODE = ADODB_FETCH_NUM; - - $rs = $this->Execute(sprintf($this->metaColumnsSQL,$table)); - - $ADODB_FETCH_MODE = $save; - - if ($rs === false) return false; - - $retarr = array(); - while (!$rs->EOF){ - $fld = new ADOFieldObject(); - $fld->name = $rs->fields[0]; - $fld->type = $rs->fields[1]; - - // split type into type(length): - if (preg_match("/^(.+)\((\d+)\)$/", $fld->type, $query_array)) { - $fld->type = $query_array[1]; - $fld->max_length = $query_array[2]; - } else { - $fld->max_length = -1; - } - $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($fld->type,'blob') !== false); - if (!$fld->binary) { - $d = $rs->fields[4]; - if ($d != "" && $d != "NULL") { - $fld->has_default = true; - $fld->default_value = $d; - } else { - $fld->has_default = false; - } - } - - $retarr[strtoupper($fld->name)] = $fld; - $rs->MoveNext(); - } - $rs->Close(); - return $retarr; - } - return false; - } - - // returns true or false - function SelectDB($dbName) - { - $this->databaseName = $dbName; - if ($this->_connectionID) { - return @mysql_select_db($dbName,$this->_connectionID); - } - else return false; - } - - // parameters use PostgreSQL convention, not MySQL - function &SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false, $arg3=false,$secs=0) - { - $offsetStr =($offset>=0) ? "$offset," : ''; - - return ($secs) ? $this->CacheExecute($secs,$sql." LIMIT $offsetStr$nrows",$inputarr,$arg3) - : $this->Execute($sql." LIMIT $offsetStr$nrows",$inputarr,$arg3); - - } - - - // returns queryID or false - function _query($sql,$inputarr) - { - //global $ADODB_COUNTRECS; - //if($ADODB_COUNTRECS) - return mysql_query($sql,$this->_connectionID); - //else return @mysql_unbuffered_query($sql,$this->_connectionID); // requires PHP >= 4.0.6 - } - - /* Returns: the last error message from previous database operation */ - function ErrorMsg() - { - if (empty($this->_connectionID)) $this->_errorMsg = @mysql_error(); - else $this->_errorMsg = @mysql_error($this->_connectionID); - return $this->_errorMsg; - } - - /* Returns: the last error number from previous database operation */ - function ErrorNo() - { - if (empty($this->_connectionID)) return @mysql_errno(); - else return @mysql_errno($this->_connectionID); - } - - - - // returns true or false - function _close() - { - @mysql_close($this->_connectionID); - $this->_connectionID = false; - } - - - /* - * Maximum size of C field - */ - function CharMax() - { - return 255; - } - - /* - * Maximum size of X field - */ - function TextMax() - { - return 4294967295; - } - -} - -/*-------------------------------------------------------------------------------------- - Class Name: Recordset ---------------------------------------------------------------------------------------*/ - -class ADORecordSet_mysql extends ADORecordSet{ - - var $databaseType = "mysql"; - var $canSeek = true; - - function ADORecordSet_mysql($queryID,$mode=false) - { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - switch ($mode) - { - case ADODB_FETCH_NUM: $this->fetchMode = MYSQL_NUM; break; - case ADODB_FETCH_ASSOC:$this->fetchMode = MYSQL_ASSOC; break; - default: - case ADODB_FETCH_DEFAULT: - case ADODB_FETCH_BOTH:$this->fetchMode = MYSQL_BOTH; break; - } - - $this->ADORecordSet($queryID); - } - - function _initrs() - { - //GLOBAL $ADODB_COUNTRECS; - // $this->_numOfRows = ($ADODB_COUNTRECS) ? @mysql_num_rows($this->_queryID):-1; - $this->_numOfRows = @mysql_num_rows($this->_queryID); - $this->_numOfFields = @mysql_num_fields($this->_queryID); - } - - function &FetchField($fieldOffset = -1) - { - - if ($fieldOffset != -1) { - $o = @mysql_fetch_field($this->_queryID, $fieldOffset); - $f = @mysql_field_flags($this->_queryID,$fieldOffset); - $o->max_length = @mysql_field_len($this->_queryID,$fieldOffset); // suggested by: Jim Nicholson (jnich@att.com) - //$o->max_length = -1; // mysql returns the max length less spaces -- so it is unrealiable - $o->binary = (strpos($f,'binary')!== false); - } - else if ($fieldOffset == -1) { /* The $fieldOffset argument is not provided thus its -1 */ - $o = @mysql_fetch_field($this->_queryID); - $o->max_length = @mysql_field_len($this->_queryID); // suggested by: Jim Nicholson (jnich@att.com) - //$o->max_length = -1; // mysql returns the max length less spaces -- so it is unrealiable - } - - return $o; - } - - function &GetRowAssoc($upper=true) - { - if ($this->fetchMode == MYSQL_ASSOC && !$upper) return $this->fields; - return ADORecordSet::GetRowAssoc($upper); - } - - /* Use associative array to get fields array */ - function Fields($colname) - { - // added @ by "Michael William Miller" - if ($this->fetchMode != MYSQL_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)]]; - } - - function _seek($row) - { - if ($this->_numOfRows == 0) return false; - return @mysql_data_seek($this->_queryID,$row); - } - - - // 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++; - $this->fields = @mysql_fetch_array($this->_queryID,$this->fetchMode); - if (is_array($this->fields)) return true; - - $this->EOF = true; - - /* -- tested raising an error -- appears pointless - $conn = $this->connection; - if ($conn && $conn->raiseErrorFn && ($errno = $conn->ErrorNo())) { - $fn = $conn->raiseErrorFn; - $fn($conn->databaseType,'MOVENEXT',$errno,$conn->ErrorMsg().' ('.$this->sql.')',$conn->host,$conn->database); - } - */ - return false; - } - - function _fetch() - { - $this->fields = @mysql_fetch_array($this->_queryID,$this->fetchMode); - return is_array($this->fields); - } - - function _close() { - @mysql_free_result($this->_queryID); - $this->_queryID = false; - } - - 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 'STRING': - case 'CHAR': - case 'VARCHAR': - case 'TINYBLOB': - case 'TINYTEXT': - case 'ENUM': - case 'SET': - if ($len <= $this->blobSize) return 'C'; - - case 'TEXT': - case 'LONGTEXT': - case 'MEDIUMTEXT': - return 'X'; - - // php_mysql extension always returns 'blob' even if 'text' - // so we have to check whether binary... - case 'IMAGE': - case 'LONGBLOB': - case 'BLOB': - case 'MEDIUMBLOB': - return !empty($fieldobj->binary) ? 'B' : 'X'; - case 'YEAR': - case 'DATE': return 'D'; - - case 'TIME': - case 'DATETIME': - case 'TIMESTAMP': return 'T'; - - case 'INT': - case 'INTEGER': - case 'BIGINT': - case 'TINYINT': - case 'MEDIUMINT': - case 'SMALLINT': - - if (!empty($fieldobj->primary_key)) return 'R'; - else return 'I'; - - default: return 'N'; - } - } - -} -} +GetOne("select version()"); + $arr['version'] = ADOConnection::_findvers($arr['description']); + return $arr; + } + + /* if magic quotes disabled, use mysql_real_escape_string() */ + function qstr($s,$magic_quotes=false) + { + if (!$magic_quotes) { + + if (ADODB_PHPVER >= 0x4300) { + if (is_resource($this->_connectionID)) + return "'".mysql_real_escape_string($s,$this->_connectionID)."'"; + } + if ($this->replaceQuote[0] == '\\'){ + $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s); + } + return "'".str_replace("'",$this->replaceQuote,$s)."'"; + } + + /* undo magic quotes for " */ + $s = str_replace('\\"','"',$s); + return "'$s'"; + } + + function _insertid() + { + return mysql_insert_id($this->_connectionID); + } + + function _affectedrows() + { + return mysql_affected_rows($this->_connectionID); + } + + /* See http://www.mysql.com/doc/M/i/Miscellaneous_functions.html */ + /* Reference on Last_Insert_ID on the recommended way to simulate sequences */ + var $_genIDSQL = "update %s set id=LAST_INSERT_ID(id+1);"; + var $_genSeqSQL = "create table %s (id int not null)"; + var $_genSeq2SQL = "insert into %s values (%s)"; + var $_dropSeqSQL = "drop table %s"; + + function CreateSequence($seqname='adodbseq',$startID=1) + { + if (empty($this->_genSeqSQL)) return false; + $u = strtoupper($seqname); + + $ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname)); + if (!$ok) return false; + return $this->Execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1)); + } + + function GenID($seqname='adodbseq',$startID=1) + { + /* post-nuke sets hasGenID to false */ + if (!$this->hasGenID) return false; + + $getnext = sprintf($this->_genIDSQL,$seqname); + $rs = @$this->Execute($getnext); + if (!$rs) { + $u = strtoupper($seqname); + $this->Execute(sprintf($this->_genSeqSQL,$seqname)); + $this->Execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1)); + $rs = $this->Execute($getnext); + } + $this->genID = mysql_insert_id($this->_connectionID); + + if ($rs) $rs->Close(); + + return $this->genID; + } + + function &MetaDatabases() + { + $qid = mysql_list_dbs($this->_connectionID); + $arr = array(); + $i = 0; + $max = mysql_num_rows($qid); + while ($i < $max) { + $db = mysql_tablename($qid,$i); + if ($db != 'mysql') $arr[] = $db; + $i += 1; + } + return $arr; + } + + + /* 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 = 'DATE_FORMAT('.$col.",'"; + $concat = false; + $len = strlen($fmt); + for ($i=0; $i < $len; $i++) { + $ch = $fmt[$i]; + switch($ch) { + case 'Y': + case 'y': + $s .= '%Y'; + break; + case 'Q': + case 'q': + $s .= "'),Quarter($col)"; + + if ($len > $i+1) $s .= ",DATE_FORMAT($col,'"; + else $s .= ",('"; + $concat = true; + break; + case 'M': + $s .= '%b'; + break; + + case 'm': + $s .= '%m'; + break; + case 'D': + case 'd': + $s .= '%d'; + break; + + case 'H': + $s .= '%H'; + break; + + case 'h': + $s .= '%I'; + break; + + case 'i': + $s .= '%i'; + break; + + case 's': + $s .= '%s'; + break; + + case 'a': + case 'A': + $s .= '%p'; + break; + + default: + + if ($ch == '\\') { + $i++; + $ch = substr($fmt,$i,1); + } + $s .= $ch; + break; + } + } + $s.="')"; + if ($concat) $s = "CONCAT($s)"; + return $s; + } + + + /* returns concatenated string */ + /* much easier to run "mysqld --ansi" or "mysqld --sql-mode=PIPES_AS_CONCAT" and use || operator */ + function Concat() + { + $s = ""; + $arr = func_get_args(); + $first = true; + /* + foreach($arr as $a) { + if ($first) { + $s = $a; + $first = false; + } else $s .= ','.$a; + }*/ + + /* suggestion by andrew005@mnogo.ru */ + $s = implode(',',$arr); + if (strlen($s) > 0) return "CONCAT($s)"; + else return ''; + } + + function OffsetDate($dayFraction,$date=false) + { + if (!$date) $date = $this->sysDate; + return "from_unixtime(unix_timestamp($date)+($dayFraction)*24*3600)"; + } + + /* returns true or false */ + function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + if (ADODB_PHPVER >= 0x4300) + $this->_connectionID = mysql_connect($argHostname,$argUsername,$argPassword, + $this->forceNewConnect,$this->clientFlags); + else if (ADODB_PHPVER >= 0x4200) + $this->_connectionID = mysql_connect($argHostname,$argUsername,$argPassword, + $this->forceNewConnect); + else + $this->_connectionID = mysql_connect($argHostname,$argUsername,$argPassword); + + if ($this->_connectionID === false) return false; + if ($argDatabasename) return $this->SelectDB($argDatabasename); + return true; + } + + /* returns true or false */ + function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + if (ADODB_PHPVER >= 0x4300) + $this->_connectionID = mysql_pconnect($argHostname,$argUsername,$argPassword,$this->clientFlags); + else + $this->_connectionID = mysql_pconnect($argHostname,$argUsername,$argPassword); + if ($this->_connectionID === false) return false; + if ($this->autoRollback) $this->RollbackTrans(); + if ($argDatabasename) return $this->SelectDB($argDatabasename); + return true; + } + + function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + $this->forceNewConnect = true; + $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename); + } + + function &MetaColumns($table) + { + + if ($this->metaColumnsSQL) { + 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(sprintf($this->metaColumnsSQL,$table)); + + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + + if ($rs === false) return false; + + $retarr = array(); + while (!$rs->EOF){ + $fld = new ADOFieldObject(); + $fld->name = $rs->fields[0]; + $type = $rs->fields[1]; + + /* split type into type(length): */ + if (preg_match("/^(.+)\((\d+)/", $type, $query_array)) { + $fld->type = $query_array[1]; + $fld->max_length = $query_array[2]; + } else { + $fld->max_length = -1; + $fld->type = $type; + } + $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($fld->type,'blob') !== false); + if (!$fld->binary) { + $d = $rs->fields[4]; + if ($d != "" && $d != "NULL") { + $fld->has_default = true; + $fld->default_value = $d; + } else { + $fld->has_default = false; + } + } + + $retarr[strtoupper($fld->name)] = $fld; + $rs->MoveNext(); + } + $rs->Close(); + return $retarr; + } + return false; + } + + /* returns true or false */ + function SelectDB($dbName) + { + $this->databaseName = $dbName; + if ($this->_connectionID) { + return @mysql_select_db($dbName,$this->_connectionID); + } + else return false; + } + + /* parameters use PostgreSQL convention, not MySQL */ + function &SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false, $arg3=false,$secs=0) + { + $offsetStr =($offset>=0) ? "$offset," : ''; + + return ($secs) ? $this->CacheExecute($secs,$sql." LIMIT $offsetStr$nrows",$inputarr,$arg3) + : $this->Execute($sql." LIMIT $offsetStr$nrows",$inputarr,$arg3); + + } + + + /* returns queryID or false */ + function _query($sql,$inputarr) + { + /* global $ADODB_COUNTRECS; */ + /* if($ADODB_COUNTRECS) */ + return mysql_query($sql,$this->_connectionID); + /* else return @mysql_unbuffered_query($sql,$this->_connectionID); // requires PHP >= 4.0.6 */ + } + + /* Returns: the last error message from previous database operation */ + function ErrorMsg() + { + if (empty($this->_connectionID)) $this->_errorMsg = @mysql_error(); + else $this->_errorMsg = @mysql_error($this->_connectionID); + return $this->_errorMsg; + } + + /* Returns: the last error number from previous database operation */ + function ErrorNo() + { + if (empty($this->_connectionID)) return @mysql_errno(); + else return @mysql_errno($this->_connectionID); + } + + + + /* returns true or false */ + function _close() + { + @mysql_close($this->_connectionID); + $this->_connectionID = false; + } + + + /* + * Maximum size of C field + */ + function CharMax() + { + return 255; + } + + /* + * Maximum size of X field + */ + function TextMax() + { + return 4294967295; + } + +} + +/*-------------------------------------------------------------------------------------- + Class Name: Recordset +--------------------------------------------------------------------------------------*/ + +class ADORecordSet_mysql extends ADORecordSet{ + + var $databaseType = "mysql"; + var $canSeek = true; + + function ADORecordSet_mysql($queryID,$mode=false) + { + if ($mode === false) { + global $ADODB_FETCH_MODE; + $mode = $ADODB_FETCH_MODE; + } + switch ($mode) + { + case ADODB_FETCH_NUM: $this->fetchMode = MYSQL_NUM; break; + case ADODB_FETCH_ASSOC:$this->fetchMode = MYSQL_ASSOC; break; + default: + case ADODB_FETCH_DEFAULT: + case ADODB_FETCH_BOTH:$this->fetchMode = MYSQL_BOTH; break; + } + + $this->ADORecordSet($queryID); + } + + function _initrs() + { + /* GLOBAL $ADODB_COUNTRECS; */ + /* $this->_numOfRows = ($ADODB_COUNTRECS) ? @mysql_num_rows($this->_queryID):-1; */ + $this->_numOfRows = @mysql_num_rows($this->_queryID); + $this->_numOfFields = @mysql_num_fields($this->_queryID); + } + + function &FetchField($fieldOffset = -1) + { + + if ($fieldOffset != -1) { + $o = @mysql_fetch_field($this->_queryID, $fieldOffset); + $f = @mysql_field_flags($this->_queryID,$fieldOffset); + $o->max_length = @mysql_field_len($this->_queryID,$fieldOffset); /* suggested by: Jim Nicholson (jnich@att.com) */ + /* $o->max_length = -1; // mysql returns the max length less spaces -- so it is unrealiable */ + $o->binary = (strpos($f,'binary')!== false); + } + else if ($fieldOffset == -1) { /* The $fieldOffset argument is not provided thus its -1 */ + $o = @mysql_fetch_field($this->_queryID); + $o->max_length = @mysql_field_len($this->_queryID); /* suggested by: Jim Nicholson (jnich@att.com) */ + /* $o->max_length = -1; // mysql returns the max length less spaces -- so it is unrealiable */ + } + + return $o; + } + + function &GetRowAssoc($upper=true) + { + if ($this->fetchMode == MYSQL_ASSOC && !$upper) return $this->fields; + return ADORecordSet::GetRowAssoc($upper); + } + + /* Use associative array to get fields array */ + function Fields($colname) + { + /* added @ by "Michael William Miller" */ + if ($this->fetchMode != MYSQL_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)]]; + } + + function _seek($row) + { + if ($this->_numOfRows == 0) return false; + return @mysql_data_seek($this->_queryID,$row); + } + + + /* 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++; + $this->fields = @mysql_fetch_array($this->_queryID,$this->fetchMode); + if (is_array($this->fields)) return true; + + $this->EOF = true; + + /* -- tested raising an error -- appears pointless + $conn = $this->connection; + if ($conn && $conn->raiseErrorFn && ($errno = $conn->ErrorNo())) { + $fn = $conn->raiseErrorFn; + $fn($conn->databaseType,'MOVENEXT',$errno,$conn->ErrorMsg().' ('.$this->sql.')',$conn->host,$conn->database); + } + */ + return false; + } + + function _fetch() + { + $this->fields = @mysql_fetch_array($this->_queryID,$this->fetchMode); + return is_array($this->fields); + } + + function _close() { + @mysql_free_result($this->_queryID); + $this->_queryID = false; + } + + 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 'STRING': + case 'CHAR': + case 'VARCHAR': + case 'TINYBLOB': + case 'TINYTEXT': + case 'ENUM': + case 'SET': + if ($len <= $this->blobSize) return 'C'; + + case 'TEXT': + case 'LONGTEXT': + case 'MEDIUMTEXT': + return 'X'; + + /* php_mysql extension always returns 'blob' even if 'text' */ + /* so we have to check whether binary... */ + case 'IMAGE': + case 'LONGBLOB': + case 'BLOB': + case 'MEDIUMBLOB': + return !empty($fieldobj->binary) ? 'B' : 'X'; + case 'YEAR': + case 'DATE': return 'D'; + + case 'TIME': + case 'DATETIME': + case 'TIMESTAMP': return 'T'; + + case 'INT': + case 'INTEGER': + case 'BIGINT': + case 'TINYINT': + case 'MEDIUMINT': + case 'SMALLINT': + + if (!empty($fieldobj->primary_key)) return 'R'; + else return 'I'; + + default: return 'N'; + } + } + +} +} ?> \ No newline at end of file diff --git a/lib/adodb/drivers/adodb-mysqlt.inc.php b/lib/adodb/drivers/adodb-mysqlt.inc.php index 5b8e8ffa0d..c45ae27503 100644 --- a/lib/adodb/drivers/adodb-mysqlt.inc.php +++ b/lib/adodb/drivers/adodb-mysqlt.inc.php @@ -1,76 +1,76 @@ - - - Requires mysql client. Works on Windows and Unix. -*/ - - -include_once(ADODB_DIR."/drivers/adodb-mysql.inc.php"); - - -class ADODB_mysqlt extends ADODB_mysql { - var $databaseType = 'mysqlt'; - var $ansiOuter = true; // for Version 3.23.17 or later - var $hasTransactions = true; - - function BeginTrans() - { - if ($this->transOff) return true; - $this->transCnt += 1; - $this->Execute('SET AUTOCOMMIT=0'); - $this->Execute('BEGIN'); - return true; - } - - function CommitTrans($ok=true) - { - if ($this->transOff) return true; - if (!$ok) return $this->RollbackTrans(); - - if ($this->transCnt) $this->transCnt -= 1; - $this->Execute('COMMIT'); - $this->Execute('SET AUTOCOMMIT=1'); - return true; - } - - function RollbackTrans() - { - if ($this->transOff) return true; - if ($this->transCnt) $this->transCnt -= 1; - $this->Execute('ROLLBACK'); - $this->Execute('SET AUTOCOMMIT=1'); - return true; - } - -} - -class ADORecordSet_mysqlt extends ADORecordSet_mysql{ - var $databaseType = "mysqlt"; - - function ADORecordSet_mysqlt($queryID,$mode=false) { - return $this->ADORecordSet_mysql($queryID,$mode); - } - - function MoveNext() - { - if ($this->EOF) return false; - - $this->_currentRow++; - // using & below slows things down by 20%! - $this->fields = @mysql_fetch_array($this->_queryID,$this->fetchMode); - if ($this->fields) return true; - $this->EOF = true; - - return false; - } -} + + + Requires mysql client. Works on Windows and Unix. +*/ + + +include_once(ADODB_DIR."/drivers/adodb-mysql.inc.php"); + + +class ADODB_mysqlt extends ADODB_mysql { + var $databaseType = 'mysqlt'; + var $ansiOuter = true; /* for Version 3.23.17 or later */ + var $hasTransactions = true; + + function BeginTrans() + { + if ($this->transOff) return true; + $this->transCnt += 1; + $this->Execute('SET AUTOCOMMIT=0'); + $this->Execute('BEGIN'); + return true; + } + + function CommitTrans($ok=true) + { + if ($this->transOff) return true; + if (!$ok) return $this->RollbackTrans(); + + if ($this->transCnt) $this->transCnt -= 1; + $this->Execute('COMMIT'); + $this->Execute('SET AUTOCOMMIT=1'); + return true; + } + + function RollbackTrans() + { + if ($this->transOff) return true; + if ($this->transCnt) $this->transCnt -= 1; + $this->Execute('ROLLBACK'); + $this->Execute('SET AUTOCOMMIT=1'); + return true; + } + +} + +class ADORecordSet_mysqlt extends ADORecordSet_mysql{ + var $databaseType = "mysqlt"; + + function ADORecordSet_mysqlt($queryID,$mode=false) { + return $this->ADORecordSet_mysql($queryID,$mode); + } + + function MoveNext() + { + if ($this->EOF) return false; + + $this->_currentRow++; + /* using & below slows things down by 20%! */ + $this->fields = @mysql_fetch_array($this->_queryID,$this->fetchMode); + if ($this->fields) return true; + $this->EOF = true; + + return false; + } +} ?> \ No newline at end of file diff --git a/lib/adodb/drivers/adodb-oci8.inc.php b/lib/adodb/drivers/adodb-oci8.inc.php index 85d2c98c8f..a8b3a2b36f 100644 --- a/lib/adodb/drivers/adodb-oci8.inc.php +++ b/lib/adodb/drivers/adodb-oci8.inc.php @@ -1,998 +1,1061 @@ - - - 13 Nov 2000 jlim - removed all ora_* references. -*/ - -/* -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 (20002049), and years over 50 as years in -the 20th century (19501999). 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. -*/ -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 $_stmt; - var $_commit = OCI_COMMIT_ON_SUCCESS; - var $_initdate = true; // init date to YYYY-MM-DD - var $metaTablesSQL = "select table_name from cat where table_type in ('TABLE','VIEW')"; - var $metaColumnsSQL = "select cname,coltype,width from col where tname='%s' order by colno"; - 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 $upperCase = 'upper'; - var $noNullStrings = false; - var $connectSID = false; - var $_bind = false; - var $_hasOCIFetchStatement = false; - var $_getarray = false; // currently not working - var $leftOuter = '(+)='; - 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'; - var $useDBDateFormatForTextInput=false; - - // var $ansiOuter = true; // if oracle9 - - function ADODB_oci8() - { - - $this->_hasOCIFetchStatement = ADODB_PHPVER >= 0x4200; - } - - -/* - - 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($argHostname) { // added by Jorma Tuomainen - if (empty($argDatabasename)) $argDatabasename = $argHostname; - else { - if(strpos($argHostname,":")) { - $argHostinfo=explode(":",$argHostname); - $argHostname=$argHostinfo[0]; - $argHostport=$argHostinfo[1]; - } else { - $argHostport="1521"; - } - - 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 " Connect: 1st argument should be left blank for $this->databaseType
"; - if ($mode==1) { - $this->_connectionID = OCIPLogon($argUsername,$argPassword, $argDatabasename); - if ($this->_connectionID && $this->autoRollback) OCIrollback($this->_connectionID); - } else if ($mode==2) { - $this->_connectionID = OCINLogon($argUsername,$argPassword, $argDatabasename); - } else { - $this->_connectionID = OCILogon($argUsername,$argPassword, $argDatabasename); - } - if ($this->_connectionID === false) 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 Affected_Rows() - { - return OCIRowCount($this->_stmt); - } - - // 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->NLS_DATE_FORMAT."')"; - } - - - // 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($this->fmtTimeStamp,$ts).",'RRRR-MM-DD, HH:MI:SS AM')"; - } - - function RowLock($tables,$where) - { - if ($this->autoCommit) $this->BeginTrans(); - return $this->GetOne("select 1 as ignore from $tables where $where for update"); - } - - function BeginTrans() - { - if ($this->transOff) return true; - $this->transCnt += 1; - $this->autoCommit = false; - $this->_commit = OCI_DEFAULT; - 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; - } - - /* there seems to be a bug in the oracle extension -- always returns ORA-00000 - no error */ - function ErrorMsg() - { - $arr = @OCIerror($this->_stmt); - - if ($arr === false) { - $arr = @OCIerror($this->_connectionID); - if ($arr === false) $arr = @OCIError(); - if ($arr === false) return ''; - } - $this->_errorMsg = $arr['message']; - return $this->_errorMsg; - } - - function ErrorNo() - { - if (is_resource($this->_stmt)) - $arr = @ocierror($this->_stmt); - else { - $arr = @ocierror($this->_connectionID); - if ($arr === false) $arr = @ocierror(); - if ($arr == false) return ''; - } - 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->sysDate; - $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': - case 'm': - $s .= 'MM'; - break; - case 'D': - case 'd': - $s .= 'DD'; - break; - default: - // handle escape characters... - if ($ch == '\\') { - $i++; - $ch = substr($fmt,$i,1); - } - if (strpos('-/.:;, ',$ch) !== false) $s .= $ch; - else $s .= '"'.$ch.'"'; - - } - } - return $s. "')"; - } - - - /* - 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,$arg3=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) { - 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 - - return ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$arg3,$secs2cache); - } 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"; - if (!$stmt = OCIParse($this->_connectionID, $q_fields)) { - return false; - } - 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 (!$success = 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) return $this->CacheExecute($secs2cache, $sql,$inputarr,$arg3); - else return $this->Execute($sql,$inputarr,$arg3); - } - - } - - /** - * 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') - { - switch(strtoupper($blobtype)) { - default: ADOConnection::outp("UpdateBlob: 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 = ADODB_oci8::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( "UpdateBlob: 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; - } - - /* - Example of usage: - - $stmt = $this->Prepare('insert into emp (empno, ename) values (:empno, :ename)'); - */ - function Prepare($sql) - { - static $BINDNUM = 0; - - $stmt = OCIParse($this->_connectionID,$sql); - - if (!$stmt) return $sql; // error in statement, let Execute() handle the error - - $BINDNUM += 1; - - if (@OCIStatementType($stmt) == 'BEGIN') { - return array($sql,$stmt,0,$BINDNUM,OCINewCursor($this->_connectionID)); - } - - return array($sql,$stmt,0,$BINDNUM); - } - - /* - Call an oracle stored procedure and return a cursor variable. - Convert the cursor variable into 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) - { - $stmt = ADODB_oci8::Prepare($sql); - - if (is_array($stmt) && sizeof($stmt) >= 5) { - $this->Parameter($stmt, $ignoreCur, $cursorName, false, -1, OCI_B_CURSOR); - if ($params) { - reset($params); - while (list($k,$v) = each($params)) { - $this->Parameter($stmt,$params[$k], $k); - } - } - } - return $this->Execute($stmt); - } - - /* - 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) - { - 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],":".$name,$var,$size,$type); - else $rez = OCIBindByName($stmt[1],":".$stmt[2],$var,$size); // +1 byte for null terminator - $stmt[2] += 1; - } else { - 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; - } - - /* - 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) { - ADOConnection::outp( "Parameter(\$stmt, \$php_var='$var', \$name='$name');"); - } - return $this->Bind($stmt,$var,$maxLen,$type,$name); - } - - /* - 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],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]); - } 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,$this->_commit)) { - - switch (@OCIStatementType($stmt)) { - case "SELECT" : - return $stmt; - - case "BEGIN" : - if (isset($sql[4])) { - // jlim - $cursor = $sql[4]; - // jlim - OCIExecute($cursor); - return $cursor; - } else { - return $stmt; - } - break; - default : - return true; - } - - /* Now this could be an Update/Insert or Delete */ - //if (@OCIStatementType($stmt) != 'SELECT') return true; - //return $stmt; - - } - return false; - } - - // returns true or false - function _close() - { - if (!$this->autoCommit) OCIRollback($this->_connectionID); - 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')))"; - } else $owner_clause = ''; - - $sql = " -SELECT /*+ RULE */ distinct b.column_name - FROM ALL_CONSTRAINTS a - , ALL_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[] = $v[0]; - } - return $a; - } - else return false; - } - - - - 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); - - if ($this->replaceQuote == "\\'") // ' already quoted, no need to change anything - return "'$s'"; - else {// change \' to '' for sybase/mssql - $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) - { - default: - case ADODB_FETCH_NUM: $this->fetchMode = OCI_NUM+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break; - 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; - } - $this->_queryID = $queryID; - } - - - function Init() - { - if ($this->_inited) return; - - $this->_inited = true; - - if ($this->_queryID) { - - $this->_currentRow = 0; - @$this->_initrs(); - $this->EOF = !$this->_fetch(); - 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); - if ($fld->type == 'NUMBER') { - //$p = OCIColumnPrecision($this->_queryID, $fieldOffset); - $sc = OCIColumnScale($this->_queryID, $fieldOffset); - if ($sc == 0) $fld->type = 'INT'; - } - 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; - } - - /* Optimize SelectLimit() by using OCIFetch() instead of OCIFetchInto() */ - function GetArrayLimit($nrows,$offset=-1) - { - if ($offset <= 0) return $this->GetArray($nrows); - for ($i=1; $i < $offset; $i++) - if (!@OCIFetch($this->_queryID)) return array(); - - if (!@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) return array(); - $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() - { - 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': - 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 'D'; - - //case 'T': return 'T'; - - case 'INT': - case 'SMALLINT': - case 'INTEGER': - return 'I'; - - default: return 'N'; - } - } -} + + + 13 Nov 2000 jlim - removed all ora_* references. +*/ + +/* +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 (20002049), and years over 50 as years in +the 20th century (19501999). 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. +*/ +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 from cat where table_type in ('TABLE','VIEW')"; + 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 $upperCase = 'upper'; + var $noNullStrings = false; + var $connectSID = false; + var $_bind = false; + var $_hasOCIFetchStatement = false; + var $_getarray = false; /* currently not working */ + var $leftOuter = '(+)='; + 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'; + var $useDBDateFormatForTextInput=false; + + /* var $ansiOuter = true; // if oracle9 */ + + function ADODB_oci8() + { + + $this->_hasOCIFetchStatement = ADODB_PHPVER >= 0x4200; + } + + /* Function &MetaColumns($table) added by smondino@users.sourceforge.net*/ + function &MetaColumns($table) + { + 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(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' && $rs->fields[3] == 0) { + $fld->type ='INT'; + $fld->max_length = $rs->fields[4]; + } + + $fld->not_null = $rs->fields[5]; + $fld->default_value = $rs->fields[6]; + $retarr[strtoupper($fld->name)] = $fld; + $rs->MoveNext(); + } + $rs->Close(); + return $retarr; + } + +/* + + 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($argHostname) { /* added by Jorma Tuomainen*/ + if (empty($argDatabasename)) $argDatabasename = $argHostname; + else { + if(strpos($argHostname,":")) { + $argHostinfo=explode(":",$argHostname); + $argHostname=$argHostinfo[0]; + $argHostport=$argHostinfo[1]; + } else { + $argHostport="1521"; + } + + 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 " Connect: 1st argument should be left blank for $this->databaseType
"; */ + if ($mode==1) { + $this->_connectionID = OCIPLogon($argUsername,$argPassword, $argDatabasename); + if ($this->_connectionID && $this->autoRollback) OCIrollback($this->_connectionID); + } else if ($mode==2) { + $this->_connectionID = OCINLogon($argUsername,$argPassword, $argDatabasename); + } else { + $this->_connectionID = OCILogon($argUsername,$argPassword, $argDatabasename); + } + if ($this->_connectionID === false) 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 Affected_Rows() + { + return OCIRowCount($this->_stmt); + } + + /* 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->NLS_DATE_FORMAT."')"; + } + + + /* 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($this->fmtTimeStamp,$ts).",'RRRR-MM-DD, HH:MI:SS AM')"; + } + + function RowLock($tables,$where) + { + if ($this->autoCommit) $this->BeginTrans(); + return $this->GetOne("select 1 as ignore from $tables where $where for update"); + } + + function BeginTrans() + { + if ($this->transOff) return true; + $this->transCnt += 1; + $this->autoCommit = false; + $this->_commit = OCI_DEFAULT; + 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; + } + + /* there seems to be a bug in the oracle extension -- always returns ORA-00000 - no error */ + function ErrorMsg() + { + $arr = @OCIerror($this->_stmt); + + if ($arr === false) { + $arr = @OCIerror($this->_connectionID); + if ($arr === false) $arr = @OCIError(); + if ($arr === false) return ''; + } + $this->_errorMsg = $arr['message']; + return $this->_errorMsg; + } + + function ErrorNo() + { + if (is_resource($this->_stmt)) + $arr = @ocierror($this->_stmt); + else { + $arr = @ocierror($this->_connectionID); + if ($arr === false) $arr = @ocierror(); + if ($arr == false) return ''; + } + 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; + + default: + /* handle escape characters... */ + if ($ch == '\\') { + $i++; + $ch = substr($fmt,$i,1); + } + if (strpos('-/.:;, ',$ch) !== false) $s .= $ch; + else $s .= '"'.$ch.'"'; + + } + } + return $s. "')"; + } + + + /* + 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,$arg3=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) { + 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 */ + + return ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$arg3,$secs2cache); + } 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"; + if (!$stmt = OCIParse($this->_connectionID, $q_fields)) { + return false; + } + 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) return $this->CacheExecute($secs2cache, $sql,$inputarr,$arg3); + else return $this->Execute($sql,$inputarr,$arg3); + } + + } + + /** + * 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') + { + switch(strtoupper($blobtype)) { + default: ADOConnection::outp("UpdateBlob: 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 = ADODB_oci8::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( "UpdateBlob: 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; + } + + /* + Example of usage: + + $stmt = $this->Prepare('insert into emp (empno, ename) values (:empno, :ename)'); + */ + function Prepare($sql) + { + static $BINDNUM = 0; + + $stmt = OCIParse($this->_connectionID,$sql); + + if (!$stmt) return $sql; /* error in statement, let Execute() handle the error */ + + $BINDNUM += 1; + + if (@OCIStatementType($stmt) == 'BEGIN') { + return array($sql,$stmt,0,$BINDNUM,OCINewCursor($this->_connectionID)); + } + + return array($sql,$stmt,0,$BINDNUM); + } + + /* + Call an oracle stored procedure and return a cursor variable. + Convert the cursor variable into 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) + { + $stmt = ADODB_oci8::Prepare($sql); + + if (is_array($stmt) && sizeof($stmt) >= 5) { + $this->Parameter($stmt, $ignoreCur, $cursorName, false, -1, OCI_B_CURSOR); + if ($params) { + reset($params); + while (list($k,$v) = each($params)) { + $this->Parameter($stmt,$params[$k], $k); + } + } + } + return $this->Execute($stmt); + } + + /* + 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) + { + 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],":".$name,$var,$size,$type); + else $rez = OCIBindByName($stmt[1],":".$stmt[2],$var,$size); /* +1 byte for null terminator */ + $stmt[2] += 1; + } else { + 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; + } + + /* + 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) { + ADOConnection::outp( "Parameter(\$stmt, \$php_var='$var', \$name='$name');"); + } + return $this->Bind($stmt,$var,$maxLen,$type,$name); + } + + /* + 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],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]); + } 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,$this->_commit)) { + + switch (@OCIStatementType($stmt)) { + case "SELECT" : + return $stmt; + + case "BEGIN" : + if (isset($sql[4])) { + /* jlim */ + $cursor = $sql[4]; + /* jlim */ + if (is_resource($cursor)) { + OCIExecute($cursor); + return $cursor; + } + return $stmt; + } else { + if (!is_array($sql) && is_resource($stmt)) { + OCIFreeStatement($stmt); + return true; + } + return $stmt; + } + break; + default : + /* ociclose? */ + return true; + } + } + return false; + } + + /* returns true or false */ + function _close() + { + if (!$this->autoCommit) OCIRollback($this->_connectionID); + 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')))"; + } else $owner_clause = ''; + + $sql = " +SELECT /*+ RULE */ distinct b.column_name + FROM ALL_CONSTRAINTS a + , ALL_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[] = $v[0]; + } + return $a; + } + else return false; + } + + + + 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); + + if ($this->replaceQuote == "\\'") /* ' already quoted, no need to change anything */ + return "'$s'"; + else {/* change \' to '' for sybase/mssql */ + $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) + { + default: + case ADODB_FETCH_NUM: $this->fetchMode = OCI_NUM+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break; + 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; + } + $this->_queryID = $queryID; + } + + + function Init() + { + if ($this->_inited) return; + + $this->_inited = true; + + if ($this->_queryID) { + + $this->_currentRow = 0; + @$this->_initrs(); + $this->EOF = !$this->_fetch(); + 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); + if ($fld->type == 'NUMBER') { + /* $p = OCIColumnPrecision($this->_queryID, $fieldOffset); */ + $sc = OCIColumnScale($this->_queryID, $fieldOffset); + if ($sc == 0) $fld->type = 'INT'; + } + 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; + } + + /* Optimize SelectLimit() by using OCIFetch() instead of OCIFetchInto() */ + function &GetArrayLimit($nrows,$offset=-1) + { + if ($offset <= 0) return $this->GetArray($nrows); + for ($i=1; $i < $offset; $i++) + if (!@OCIFetch($this->_queryID)) return array(); + + if (!@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) return array(); + $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() + { + 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 'D'; + + /* case 'T': return 'T'; */ + + case 'INT': + case 'SMALLINT': + case 'INTEGER': + return 'I'; + + default: return 'N'; + } + } +} ?> \ No newline at end of file diff --git a/lib/adodb/drivers/adodb-oci805.inc.php b/lib/adodb/drivers/adodb-oci805.inc.php index 17b2f0740b..b3640b599f 100644 --- a/lib/adodb/drivers/adodb-oci805.inc.php +++ b/lib/adodb/drivers/adodb-oci805.inc.php @@ -1,56 +1,56 @@ -ADODB_oci8(); - } - - function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$arg3=false,$secs2cache=0) - { - // seems that oracle only supports 1 hint comment in 8i - if (strpos($sql,'/*+') !== false) - $sql = str_replace('/*+ ','/*+FIRST_ROWS ',$sql); - else - $sql = preg_replace('/^[ \t\n]*select/i','SELECT /*+FIRST_ROWS*/',$sql); - - /* - The following is only available from 8.1.5 because order by in inline views not - available before then... - http://www.jlcomp.demon.co.uk/faq/top_sql.html - if ($nrows > 0) { - if ($offset > 0) $nrows += $offset; - $sql = "select * from ($sql) where rownum <= $nrows"; - $nrows = -1; - } - */ - - return ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$arg3,$secs2cache); - } -} - -class ADORecordset_oci805 extends ADORecordset_oci8 { - var $databaseType = "oci805"; - function ADORecordset_oci805($id,$mode=false) - { - $this->ADORecordset_oci8($id,$mode); - } -} +ADODB_oci8(); + } + + function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$arg3=false,$secs2cache=0) + { + /* seems that oracle only supports 1 hint comment in 8i */ + if (strpos($sql,'/*+') !== false) + $sql = str_replace('/*+ ','/*+FIRST_ROWS ',$sql); + else + $sql = preg_replace('/^[ \t\n]*select/i','SELECT /*+FIRST_ROWS*/',$sql); + + /* + The following is only available from 8.1.5 because order by in inline views not + available before then... + http://www.jlcomp.demon.co.uk/faq/top_sql.html + if ($nrows > 0) { + if ($offset > 0) $nrows += $offset; + $sql = "select * from ($sql) where rownum <= $nrows"; + $nrows = -1; + } + */ + + return ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$arg3,$secs2cache); + } +} + +class ADORecordset_oci805 extends ADORecordset_oci8 { + var $databaseType = "oci805"; + function ADORecordset_oci805($id,$mode=false) + { + $this->ADORecordset_oci8($id,$mode); + } +} ?> \ No newline at end of file diff --git a/lib/adodb/drivers/adodb-oci8po.inc.php b/lib/adodb/drivers/adodb-oci8po.inc.php index c11e8c9d67..742a8cccf0 100644 --- a/lib/adodb/drivers/adodb-oci8po.inc.php +++ b/lib/adodb/drivers/adodb-oci8po.inc.php @@ -1,166 +1,166 @@ - - - Should some emulation of RecordCount() be implemented? - -*/ - -include_once(ADODB_DIR.'/drivers/adodb-oci8.inc.php'); - -class ADODB_oci8po extends ADODB_oci8 { - var $databaseType = 'oci8po'; - var $dataProvider = 'oci8'; - var $metaColumnsSQL = "select lower(cname),coltype,width from col where tname='%s' order by colno"; - var $metaTablesSQL = "select lower(table_name) from cat where table_type in ('TABLE','VIEW')"; - - function Prepare($sql) - { - $sqlarr = explode('?',$sql); - $sql = $sqlarr[0]; - for ($i = 1, $max = sizeof($sqlarr); $i < $max; $i++) { - $sql .= ':'.($i-1) . $sqlarr[$i]; - } - return ADODB_oci8::Prepare($sql); - } - - // emulate handling of parameters ? ?, replacing with :bind0 :bind1 - function _query($sql,$inputarr) - { - if (is_array($inputarr)) { - $i = 0; - if (is_array($sql)) { - foreach($inputarr as $v) { - $arr['bind'.$i++] = $v; - } - } else { - $sqlarr = explode('?',$sql); - $sql = $sqlarr[0]; - foreach($inputarr as $k => $v) { - $sql .= ":$k" . $sqlarr[++$i]; - } - } - } - return ADODB_oci8::_query($sql,$inputarr); - } - - -} - -/*-------------------------------------------------------------------------------------- - Class Name: Recordset ---------------------------------------------------------------------------------------*/ - -class ADORecordset_oci8po extends ADORecordset_oci8 { - - var $databaseType = 'oci8po'; - - function ADORecordset_oci8po($queryID,$mode=false) - { - $this->ADORecordset_oci8($queryID,$mode); - } - - function Fields($colname) - { - if ($this->fetchMode & OCI_ASSOC) 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)]]; - } - - // lowercase field names... - function &_FetchField($fieldOffset = -1) - { - $fld = new ADOFieldObject; - $fieldOffset += 1; - $fld->name = strtolower(OCIcolumnname($this->_queryID, $fieldOffset)); - $fld->type = OCIcolumntype($this->_queryID, $fieldOffset); - $fld->max_length = OCIcolumnsize($this->_queryID, $fieldOffset); - if ($fld->type == 'NUMBER') { - //$p = OCIColumnPrecision($this->_queryID, $fieldOffset); - $sc = OCIColumnScale($this->_queryID, $fieldOffset); - if ($sc == 0) $fld->type = 'INT'; - } - return $fld; - } - - // 10% speedup to move MoveNext to child class - function MoveNext() - { - if (!$this->EOF) { - $this->_currentRow++; - if(@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) { - if ($this->fetchMode & OCI_ASSOC) $this->_updatefields(); - return true; - } - $this->EOF = true; - } - return false; - } - - /* Optimize SelectLimit() by using OCIFetch() instead of OCIFetchInto() */ - function &GetArrayLimit($nrows,$offset=-1) - { - if ($offset <= 0) return $this->GetArray($nrows); - for ($i=1; $i < $offset; $i++) - if (!@OCIFetch($this->_queryID)) return array(); - - if (!@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) return array(); - if ($this->fetchMode & OCI_ASSOC) $this->_updatefields(); - $results = array(); - $cnt = 0; - while (!$this->EOF && $nrows != $cnt) { - $results[$cnt++] = $this->fields; - $this->MoveNext(); - } - - return $results; - } - - // Create associative array - function _updatefields() - { - if (ADODB_ASSOC_CASE == 2) return; // native - - $arr = array(); - $lowercase = ADODB_ASSOC_CASE == 0; - foreach ($this->fields as $k => $v) { - if (is_integer($k)) $arr[$k] = $v; - else { - if ($lowercase) - $arr[strtolower($k)] = $v; - else - $arr[strtoupper($k)] = $v; - } - } - $this->fields = $arr; - } - - function _fetch() - { - $ret = @OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode); - if ($ret) { - if ($this->fetchMode & OCI_ASSOC) $this->_updatefields(); - } - return $ret; - } - -} + + + Should some emulation of RecordCount() be implemented? + +*/ + +include_once(ADODB_DIR.'/drivers/adodb-oci8.inc.php'); + +class ADODB_oci8po extends ADODB_oci8 { + var $databaseType = 'oci8po'; + var $dataProvider = 'oci8'; + var $metaColumnsSQL = "select lower(cname),coltype,width, SCALE, PRECISION, NULLS, DEFAULTVAL from col where tname='%s' order by colno"; /* changed by smondino@users.sourceforge. net */ + var $metaTablesSQL = "select lower(table_name) from cat where table_type in ('TABLE','VIEW')"; + + function Prepare($sql) + { + $sqlarr = explode('?',$sql); + $sql = $sqlarr[0]; + for ($i = 1, $max = sizeof($sqlarr); $i < $max; $i++) { + $sql .= ':'.($i-1) . $sqlarr[$i]; + } + return ADODB_oci8::Prepare($sql); + } + + /* emulate handling of parameters ? ?, replacing with :bind0 :bind1 */ + function _query($sql,$inputarr) + { + if (is_array($inputarr)) { + $i = 0; + if (is_array($sql)) { + foreach($inputarr as $v) { + $arr['bind'.$i++] = $v; + } + } else { + $sqlarr = explode('?',$sql); + $sql = $sqlarr[0]; + foreach($inputarr as $k => $v) { + $sql .= ":$k" . $sqlarr[++$i]; + } + } + } + return ADODB_oci8::_query($sql,$inputarr); + } + + +} + +/*-------------------------------------------------------------------------------------- + Class Name: Recordset +--------------------------------------------------------------------------------------*/ + +class ADORecordset_oci8po extends ADORecordset_oci8 { + + var $databaseType = 'oci8po'; + + function ADORecordset_oci8po($queryID,$mode=false) + { + $this->ADORecordset_oci8($queryID,$mode); + } + + function Fields($colname) + { + if ($this->fetchMode & OCI_ASSOC) 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)]]; + } + + /* lowercase field names... */ + function &_FetchField($fieldOffset = -1) + { + $fld = new ADOFieldObject; + $fieldOffset += 1; + $fld->name = strtolower(OCIcolumnname($this->_queryID, $fieldOffset)); + $fld->type = OCIcolumntype($this->_queryID, $fieldOffset); + $fld->max_length = OCIcolumnsize($this->_queryID, $fieldOffset); + if ($fld->type == 'NUMBER') { + /* $p = OCIColumnPrecision($this->_queryID, $fieldOffset); */ + $sc = OCIColumnScale($this->_queryID, $fieldOffset); + if ($sc == 0) $fld->type = 'INT'; + } + return $fld; + } + + /* 10% speedup to move MoveNext to child class */ + function MoveNext() + { + if (!$this->EOF) { + $this->_currentRow++; + if(@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) { + if ($this->fetchMode & OCI_ASSOC) $this->_updatefields(); + return true; + } + $this->EOF = true; + } + return false; + } + + /* Optimize SelectLimit() by using OCIFetch() instead of OCIFetchInto() */ + function &GetArrayLimit($nrows,$offset=-1) + { + if ($offset <= 0) return $this->GetArray($nrows); + for ($i=1; $i < $offset; $i++) + if (!@OCIFetch($this->_queryID)) return array(); + + if (!@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) return array(); + if ($this->fetchMode & OCI_ASSOC) $this->_updatefields(); + $results = array(); + $cnt = 0; + while (!$this->EOF && $nrows != $cnt) { + $results[$cnt++] = $this->fields; + $this->MoveNext(); + } + + return $results; + } + + /* Create associative array */ + function _updatefields() + { + if (ADODB_ASSOC_CASE == 2) return; /* native */ + + $arr = array(); + $lowercase = ADODB_ASSOC_CASE == 0; + foreach ($this->fields as $k => $v) { + if (is_integer($k)) $arr[$k] = $v; + else { + if ($lowercase) + $arr[strtolower($k)] = $v; + else + $arr[strtoupper($k)] = $v; + } + } + $this->fields = $arr; + } + + function _fetch() + { + $ret = @OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode); + if ($ret) { + if ($this->fetchMode & OCI_ASSOC) $this->_updatefields(); + } + return $ret; + } + +} ?> \ No newline at end of file diff --git a/lib/adodb/drivers/adodb-odbc.inc.php b/lib/adodb/drivers/adodb-odbc.inc.php index e1280dd6a2..5abb417724 100644 --- a/lib/adodb/drivers/adodb-odbc.inc.php +++ b/lib/adodb/drivers/adodb-odbc.inc.php @@ -1,640 +1,660 @@ -_haserrorfunctions = ADODB_PHPVER >= 0x4050; - $this->_has_stupid_odbc_fetch_api_change = ADODB_PHPVER >= 0x4200; - } - - function ServerInfo() - { - - if (!empty($this->host) && ADODB_PHPVER >= 0x4300) { - $dsn = strtoupper($this->host); - $first = true; - $found = false; - while(true) { - $rez = odbc_data_source($this->_connectionID, - $first ? SQL_FETCH_FIRST : SQL_FETCH_NEXT); - $first = false; - if (!is_array($rez)) break; - if (strtoupper($rez['server']) == $dsn) { - $found = true; - break; - } - } - if (!$found) return ADOConnection::ServerInfo(); - if (!isset($rez['version'])) $rez['version'] = ''; - return $rez; - } else { - return ADOConnection::ServerInfo(); - } - } - - function ErrorMsg() - { - if ($this->_haserrorfunctions) { - if (empty($this->_connectionID)) return @odbc_errormsg(); - return @odbc_errormsg($this->_connectionID); - } else return ADOConnection::ErrorMsg(); - } - - function CreateSequence($seqname='adodbseq',$start=1) - { - if (empty($this->_genSeqSQL)) return false; - $ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname)); - if (!$ok) return false; - $start -= 1; - return $this->Execute("insert into $seqname values($start)"); - } - - var $_dropSeqSQL = 'drop table %s'; - function DropSequence($seqname) - { - if (empty($this->_dropSeqSQL)) return false; - return $this->Execute(sprintf($this->_dropSeqSQL,$seqname)); - } - - /* - This algorithm is not very efficient, but works even if table locking - is not available. - - Will return false if unable to generate an ID after $MAXLOOPS attempts. - */ - function GenID($seq='adodbseq',$start=1) - { - // if you have to modify the parameter below, your database is overloaded, - // or you need to implement generation of id's yourself! - $MAXLOOPS = 100; - //$this->debug=1; - while (--$MAXLOOPS>=0) { - $num = $this->GetOne("select id from $seq"); - if ($num === false) { - $this->Execute(sprintf($this->_genSeqSQL ,$seq)); - $start -= 1; - $num = '0'; - $ok = $this->Execute("insert into $seq values($start)"); - if (!$ok) return false; - } - $this->Execute("update $seq set id=id+1 where id=$num"); - - if ($this->affected_rows() > 0) { - $num += 1; - $this->genID = $num; - return $num; - } - } - if ($fn = $this->raiseErrorFn) { - $fn($this->databaseType,'GENID',-32000,"Unable to generate unique id after $MAXLOOPS attempts",$seq,$num); - } - return false; - } - - function ErrorNo() - { - if ($this->_haserrorfunctions) { - if (empty($this->_connectionID)) $e = @odbc_error(); - else $e = @odbc_error($this->_connectionID); - - // bug in 4.0.6, error number can be corrupted string (should be 6 digits) - // so we check and patch - if (strlen($e)<=2) return 0; - return $e; - } else return ADOConnection::ErrorNo(); - } - - - // returns true or false - function _connect($argDSN, $argUsername, $argPassword, $argDatabasename) - { - global $php_errormsg; - if ($this->debug && $argDatabasename) { - ADOConnection::outp("For odbc Connect(), $argDatabasename is not used. Place dsn in 1st parameter."); - } - $php_errormsg = ''; - if ($this->curmode === false) $this->_connectionID = odbc_connect($argDSN,$argUsername,$argPassword); - else $this->_connectionID = odbc_connect($argDSN,$argUsername,$argPassword,$this->curmode); - $this->_errorMsg = $php_errormsg; - - //if ($this->_connectionID) odbc_autocommit($this->_connectionID,true); - return $this->_connectionID != false; - } - - // returns true or false - function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename) - { - global $php_errormsg; - $php_errormsg = ''; - if ($this->debug && $argDatabasename) { - ADOConnection::outp("For odbc PConnect(), $argDatabasename is not used. Place dsn in 1st parameter."); - } - // print "dsn=$argDSN u=$argUsername p=$argPassword
"; flush(); - if ($this->curmode === false) $this->_connectionID = odbc_connect($argDSN,$argUsername,$argPassword); - else $this->_connectionID = odbc_pconnect($argDSN,$argUsername,$argPassword,$this->curmode); - - $this->_errorMsg = $php_errormsg; - if ($this->_connectionID && $this->autoRollback) @odbc_rollback($this->_connectionID); - return $this->_connectionID != false; - } - - function BeginTrans() - { - if (!$this->hasTransactions) return false; - if ($this->transOff) return true; - $this->transCnt += 1; - $this->_autocommit = false; - return odbc_autocommit($this->_connectionID,false); - } - - function CommitTrans($ok=true) - { - if ($this->transOff) return true; - if (!$ok) return $this->RollbackTrans(); - if ($this->transCnt) $this->transCnt -= 1; - $this->_autocommit = true; - $ret = odbc_commit($this->_connectionID); - odbc_autocommit($this->_connectionID,true); - return $ret; - } - - function RollbackTrans() - { - if ($this->transOff) return true; - if ($this->transCnt) $this->transCnt -= 1; - $this->_autocommit = true; - $ret = odbc_rollback($this->_connectionID); - odbc_autocommit($this->_connectionID,true); - return $ret; - } - - function MetaPrimaryKeys($table) - { - global $ADODB_FETCH_MODE; - - $savem = $ADODB_FETCH_MODE; - $ADODB_FETCH_MODE = ADODB_FETCH_NUM; - $qid = @odbc_primarykeys($this->_connectionID,'','',$table); - - if (!$qid) { - $ADODB_FETCH_MODE = $savem; - return false; - } - $rs = new ADORecordSet_odbc($qid); - $ADODB_FETCH_MODE = $savem; - - if (!$rs) return false; - $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change; - - $arr = $rs->GetArray(); - $rs->Close(); - //print_r($arr); - $arr2 = array(); - for ($i=0; $i < sizeof($arr); $i++) { - if ($arr[$i][3]) $arr2[] = $arr[$i][3]; - } - return $arr2; - } - - function MetaTables() - { - global $ADODB_FETCH_MODE; - - $savem = $ADODB_FETCH_MODE; - $ADODB_FETCH_MODE = ADODB_FETCH_NUM; - $qid = odbc_tables($this->_connectionID); - - $rs = new ADORecordSet_odbc($qid); - - $ADODB_FETCH_MODE = $savem; - if (!$rs) return false; - - $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change; - - //print_r($rs); - $arr = $rs->GetArray(); - - $rs->Close(); - $arr2 = array(); - for ($i=0; $i < sizeof($arr); $i++) { - if ($arr[$i][2]) $arr2[] = $arr[$i][2]; - } - return $arr2; - } - -/* -/ SQL data type codes / -#define SQL_UNKNOWN_TYPE 0 -#define SQL_CHAR 1 -#define SQL_NUMERIC 2 -#define SQL_DECIMAL 3 -#define SQL_INTEGER 4 -#define SQL_SMALLINT 5 -#define SQL_FLOAT 6 -#define SQL_REAL 7 -#define SQL_DOUBLE 8 -#if (ODBCVER >= 0x0300) -#define SQL_DATETIME 9 -#endif -#define SQL_VARCHAR 12 - -/ One-parameter shortcuts for date/time data types / -#if (ODBCVER >= 0x0300) -#define SQL_TYPE_DATE 91 -#define SQL_TYPE_TIME 92 -#define SQL_TYPE_TIMESTAMP 93 - -#define SQL_UNICODE (-95) -#define SQL_UNICODE_VARCHAR (-96) -#define SQL_UNICODE_LONGVARCHAR (-97) -*/ - function ODBCTypes($t) - { - switch ((integer)$t) { - case 1: - case 12: - case 0: - case -95: - case -96: - return 'C'; - case -97: - case -1: //text - return 'X'; - case -4: //image - return 'B'; - - case 91: - case 11: - return 'D'; - - case 92: - case 93: - case 9: return 'T'; - case 4: - case 5: - case -6: - return 'I'; - - case -11: // uniqidentifier - return 'R'; - case -7: //bit - return 'L'; - - default: - return 'N'; - } - } - - function MetaColumns($table) - { - global $ADODB_FETCH_MODE; - - $table = strtoupper($table); - - $savem = $ADODB_FETCH_MODE; - $ADODB_FETCH_MODE = ADODB_FETCH_NUM; - - if (false) { // after testing, confirmed that the following does not work becoz of a bug - $qid2 = odbc_tables($this->_connectionID); - $rs = new ADORecordSet_odbc($qid2); - $ADODB_FETCH_MODE = $savem; - if (!$rs) return false; - $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change; - $rs->_fetch(); - - while (!$rs->EOF) { - if ($table == strtoupper($rs->fields[2])) { - $q = $rs->fields[0]; - $o = $rs->fields[1]; - break; - } - $rs->MoveNext(); - } - $rs->Close(); - - $qid = odbc_columns($this->_connectionID,$q,$o,strtoupper($table),'%'); - } else if ($this->databaseType == 'access') { - $qid = odbc_columns($this->_connectionID); - } else { - $qid = odbc_columns($this->_connectionID,'%','%',strtoupper($table),'%'); - } - - $rs = new ADORecordSet_odbc($qid); - $ADODB_FETCH_MODE = $savem; - - if (!$rs) return false; - - //print_r($rs); - $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change; - $rs->_fetch(); - $retarr = array(); - - /* - $rs->fields indices - 0 TABLE_QUALIFIER - 1 TABLE_SCHEM - 2 TABLE_NAME - 3 COLUMN_NAME - 4 DATA_TYPE - 5 TYPE_NAME - 6 PRECISION - 7 LENGTH - 8 SCALE - 9 RADIX - 10 NULLABLE - 11 REMARKS - */ - while (!$rs->EOF) { - //print_r($rs->fields); - if (strtoupper($rs->fields[2]) == $table) { - $fld = new ADOFieldObject(); - $fld->name = $rs->fields[3]; - $fld->type = $this->ODBCTypes($rs->fields[4]); - - // ref: http://msdn.microsoft.com/library/default.asp?url=/archive/en-us/dnaraccgen/html/msdn_odk.asp - // access uses precision to store length for char/varchar - if ($fld->type == 'C' or $fld->type == 'X') { - if ($this->databaseType == 'access') - $fld->max_length = $rs->fields[6]; - else if ($rs->fields[4] <= -95) // UNICODE - $fld->max_length = $rs->fields[7]/2; - else - $fld->max_length = $rs->fields[7]; - } else - $fld->max_length = $rs->fields[7]; - $fld->not_null = !empty($rs->fields[10]); - $retarr[strtoupper($fld->name)] = $fld; - } else if (sizeof($retarr)>0) - break; - $rs->MoveNext(); - } - $rs->Close(); //-- crashes 4.03pl1 -- why? - - return $retarr; - } - - function Prepare($sql) - { - if (! $this->_bindInputArray) return $sql; // no binding - $stmt = odbc_prepare($this->_connectionID,$sql); - if (!$stmt) { - // print "Prepare Error for ($sql) ".$this->ErrorMsg()."
"; - return $sql; - } - return array($sql,$stmt,false); - } - - /* returns queryID or false */ - function _query($sql,$inputarr=false) - { - GLOBAL $php_errormsg; - $php_errormsg = ''; - $this->_error = ''; - - if ($inputarr) { - if (is_array($sql)) { - $stmtid = $sql[1]; - } else { - $stmtid = odbc_prepare($this->_connectionID,$sql); - - if ($stmtid == false) { - $this->_errorMsg = $php_errormsg; - return false; - } - } - if (! odbc_execute($stmtid,$inputarr)) { - //@odbc_free_result($stmtid); - return false; - } - - } else if (is_array($sql)) { - $stmtid = $sql[1]; - if (!odbc_execute($stmtid)) { - //@odbc_free_result($stmtid); - return false; - } - } else - $stmtid = odbc_exec($this->_connectionID,$sql); - - if ($stmtid) { - if (@odbc_num_fields($stmtid) == 0) { - $this->_lastAffectedRows = odbc_num_rows($stmtid); - $stmtid = true; - } else { - odbc_binmode($stmtid,$this->binmode); - odbc_longreadlen($stmtid,$this->maxblobsize); - } - } - $this->_errorMsg = $php_errormsg; - return $stmtid; - } - - /* - Insert a null into the blob field of the table first. - Then use UpdateBlob to store the blob. - - Usage: - - $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); - $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1'); - */ - function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') - { - return $this->Execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false; - } - - // returns true or false - function _close() - { - $ret = @odbc_close($this->_connectionID); - $this->_connectionID = false; - return $ret; - } - - function _affectedrows() - { - return $this->_lastAffectedRows; - } - -} - -/*-------------------------------------------------------------------------------------- - Class Name: Recordset ---------------------------------------------------------------------------------------*/ - -class ADORecordSet_odbc extends ADORecordSet { - - var $bind = false; - var $databaseType = "odbc"; - var $dataProvider = "odbc"; - var $useFetchArray; - var $_has_stupid_odbc_fetch_api_change; - - function ADORecordSet_odbc($id,$mode=false) - { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - $this->fetchMode = $mode; - - $this->_queryID = $id; - - // the following is required for mysql odbc driver in 4.3.1 -- why? - $this->EOF = false; - $this->_currentRow = -1; - //$this->ADORecordSet($id); - } - - - // returns the field object - function &FetchField($fieldOffset = -1) - { - - $off=$fieldOffset+1; // offsets begin at 1 - - $o= new ADOFieldObject(); - $o->name = @odbc_field_name($this->_queryID,$off); - $o->type = @odbc_field_type($this->_queryID,$off); - $o->max_length = @odbc_field_len($this->_queryID,$off); - - return $o; - } - - /* Use associative array to get fields array */ - function Fields($colname) - { - if ($this->fetchMode & ADODB_FETCH_ASSOC) 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)]]; - } - - - function _initrs() - { - global $ADODB_COUNTRECS; - $this->_numOfRows = ($ADODB_COUNTRECS) ? @odbc_num_rows($this->_queryID) : -1; - $this->_numOfFields = @odbc_num_fields($this->_queryID); - // some silly drivers such as db2 as/400 and intersystems cache return _numOfRows = 0 - if ($this->_numOfRows == 0) $this->_numOfRows = -1; - //$this->useFetchArray = $this->connection->useFetchArray; - $this->_has_stupid_odbc_fetch_api_change = ADODB_PHPVER >= 0x4200; - } - - function _seek($row) - { - return false; - } - - // speed up SelectLimit() by switching to ADODB_FETCH_NUM as ADODB_FETCH_ASSOC is emulated - function GetArrayLimit($nrows,$offset=-1) - { - if ($offset <= 0) return $this->GetArray($nrows); - $savem = $this->fetchMode; - $this->fetchMode = ADODB_FETCH_NUM; - $this->Move($offset); - $this->fetchMode = $savem; - - if ($this->fetchMode & ADODB_FETCH_ASSOC) { - $this->fields = $this->GetRowAssoc(ADODB_ASSOC_CASE); - } - - $results = array(); - $cnt = 0; - while (!$this->EOF && $nrows != $cnt) { - $results[$cnt++] = $this->fields; - $this->MoveNext(); - } - - return $results; - } - - - function MoveNext() - { - if ($this->_numOfRows != 0 && !$this->EOF) { - $this->_currentRow++; - $row = 0; - if ($this->_has_stupid_odbc_fetch_api_change) - $rez = @odbc_fetch_into($this->_queryID,$this->fields); - else - $rez = @odbc_fetch_into($this->_queryID,$row,$this->fields); - if ($rez) { - if ($this->fetchMode & ADODB_FETCH_ASSOC) { - $this->fields = $this->GetRowAssoc(ADODB_ASSOC_CASE); - } - return true; - } - } - $this->fields = false; - $this->EOF = true; - return false; - } - - function _fetch() - { - $row = 0; - if ($this->_has_stupid_odbc_fetch_api_change) - $rez = @odbc_fetch_into($this->_queryID,$this->fields,$row); - else - $rez = @odbc_fetch_into($this->_queryID,$row,$this->fields); - - if ($rez) { - if ($this->fetchMode & ADODB_FETCH_ASSOC) { - $this->fields = $this->GetRowAssoc(ADODB_ASSOC_CASE); - } - return true; - } - $this->fields = false; - return false; - } - - function _close() - { - return @odbc_free_result($this->_queryID); - } - -} - -?> +_haserrorfunctions = ADODB_PHPVER >= 0x4050; + $this->_has_stupid_odbc_fetch_api_change = ADODB_PHPVER >= 0x4200; + } + + function ServerInfo() + { + + if (!empty($this->host) && ADODB_PHPVER >= 0x4300) { + $dsn = strtoupper($this->host); + $first = true; + $found = false; + + if (!function_exists('odbc_data_source')) return false; + + while(true) { + + $rez = odbc_data_source($this->_connectionID, + $first ? SQL_FETCH_FIRST : SQL_FETCH_NEXT); + $first = false; + if (!is_array($rez)) break; + if (strtoupper($rez['server']) == $dsn) { + $found = true; + break; + } + } + if (!$found) return ADOConnection::ServerInfo(); + if (!isset($rez['version'])) $rez['version'] = ''; + return $rez; + } else { + return ADOConnection::ServerInfo(); + } + } + + function ErrorMsg() + { + if ($this->_haserrorfunctions) { + if (empty($this->_connectionID)) return @odbc_errormsg(); + return @odbc_errormsg($this->_connectionID); + } else return ADOConnection::ErrorMsg(); + } + + function CreateSequence($seqname='adodbseq',$start=1) + { + if (empty($this->_genSeqSQL)) return false; + $ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname)); + if (!$ok) return false; + $start -= 1; + return $this->Execute("insert into $seqname values($start)"); + } + + var $_dropSeqSQL = 'drop table %s'; + function DropSequence($seqname) + { + if (empty($this->_dropSeqSQL)) return false; + return $this->Execute(sprintf($this->_dropSeqSQL,$seqname)); + } + + /* + This algorithm is not very efficient, but works even if table locking + is not available. + + Will return false if unable to generate an ID after $MAXLOOPS attempts. + */ + function GenID($seq='adodbseq',$start=1) + { + /* if you have to modify the parameter below, your database is overloaded, */ + /* or you need to implement generation of id's yourself! */ + $MAXLOOPS = 100; + /* $this->debug=1; */ + while (--$MAXLOOPS>=0) { + $num = $this->GetOne("select id from $seq"); + if ($num === false) { + $this->Execute(sprintf($this->_genSeqSQL ,$seq)); + $start -= 1; + $num = '0'; + $ok = $this->Execute("insert into $seq values($start)"); + if (!$ok) return false; + } + $this->Execute("update $seq set id=id+1 where id=$num"); + + if ($this->affected_rows() > 0) { + $num += 1; + $this->genID = $num; + return $num; + } + } + if ($fn = $this->raiseErrorFn) { + $fn($this->databaseType,'GENID',-32000,"Unable to generate unique id after $MAXLOOPS attempts",$seq,$num); + } + return false; + } + + function ErrorNo() + { + if ($this->_haserrorfunctions) { + if (empty($this->_connectionID)) $e = @odbc_error(); + else $e = @odbc_error($this->_connectionID); + + /* bug in 4.0.6, error number can be corrupted string (should be 6 digits) */ + /* so we check and patch */ + if (strlen($e)<=2) return 0; + return $e; + } else return ADOConnection::ErrorNo(); + } + + + /* returns true or false */ + function _connect($argDSN, $argUsername, $argPassword, $argDatabasename) + { + global $php_errormsg; + if ($this->debug && $argDatabasename) { + ADOConnection::outp("For odbc Connect(), $argDatabasename is not used. Place dsn in 1st parameter."); + } + $php_errormsg = ''; + if ($this->curmode === false) $this->_connectionID = odbc_connect($argDSN,$argUsername,$argPassword); + else $this->_connectionID = odbc_connect($argDSN,$argUsername,$argPassword,$this->curmode); + $this->_errorMsg = $php_errormsg; + if (isset($this->connectStmt)) $this->Execute($this->connectStmt); + + /* if ($this->_connectionID) odbc_autocommit($this->_connectionID,true); */ + return $this->_connectionID != false; + } + + /* returns true or false */ + function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename) + { + global $php_errormsg; + $php_errormsg = ''; + if ($this->debug && $argDatabasename) { + ADOConnection::outp("For odbc PConnect(), $argDatabasename is not used. Place dsn in 1st parameter."); + } + /* print "dsn=$argDSN u=$argUsername p=$argPassword
"; flush(); */ + if ($this->curmode === false) $this->_connectionID = odbc_connect($argDSN,$argUsername,$argPassword); + else $this->_connectionID = odbc_pconnect($argDSN,$argUsername,$argPassword,$this->curmode); + + $this->_errorMsg = $php_errormsg; + if ($this->_connectionID && $this->autoRollback) @odbc_rollback($this->_connectionID); + if (isset($this->connectStmt)) $this->Execute($this->connectStmt); + + return $this->_connectionID != false; + } + + function BeginTrans() + { + if (!$this->hasTransactions) return false; + if ($this->transOff) return true; + $this->transCnt += 1; + $this->_autocommit = false; + return odbc_autocommit($this->_connectionID,false); + } + + function CommitTrans($ok=true) + { + if ($this->transOff) return true; + if (!$ok) return $this->RollbackTrans(); + if ($this->transCnt) $this->transCnt -= 1; + $this->_autocommit = true; + $ret = odbc_commit($this->_connectionID); + odbc_autocommit($this->_connectionID,true); + return $ret; + } + + function RollbackTrans() + { + if ($this->transOff) return true; + if ($this->transCnt) $this->transCnt -= 1; + $this->_autocommit = true; + $ret = odbc_rollback($this->_connectionID); + odbc_autocommit($this->_connectionID,true); + return $ret; + } + + function MetaPrimaryKeys($table) + { + global $ADODB_FETCH_MODE; + + $savem = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + $qid = @odbc_primarykeys($this->_connectionID,'','',$table); + + if (!$qid) { + $ADODB_FETCH_MODE = $savem; + return false; + } + $rs = new ADORecordSet_odbc($qid); + $ADODB_FETCH_MODE = $savem; + + if (!$rs) return false; + $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change; + + $arr =& $rs->GetArray(); + $rs->Close(); + /* print_r($arr); */ + $arr2 = array(); + for ($i=0; $i < sizeof($arr); $i++) { + if ($arr[$i][3]) $arr2[] = $arr[$i][3]; + } + return $arr2; + } + + function &MetaTables() + { + global $ADODB_FETCH_MODE; + + $savem = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + $qid = odbc_tables($this->_connectionID); + + $rs = new ADORecordSet_odbc($qid); + + $ADODB_FETCH_MODE = $savem; + if (!$rs) return false; + + $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change; + + /* print_r($rs); */ + $arr =& $rs->GetArray(); + + $rs->Close(); + $arr2 = array(); + for ($i=0; $i < sizeof($arr); $i++) { + if ($arr[$i][2]) $arr2[] = $arr[$i][2]; + } + return $arr2; + } + +/* +/ SQL data type codes / +#define SQL_UNKNOWN_TYPE 0 +#define SQL_CHAR 1 +#define SQL_NUMERIC 2 +#define SQL_DECIMAL 3 +#define SQL_INTEGER 4 +#define SQL_SMALLINT 5 +#define SQL_FLOAT 6 +#define SQL_REAL 7 +#define SQL_DOUBLE 8 +#if (ODBCVER >= 0x0300) +#define SQL_DATETIME 9 +#endif +#define SQL_VARCHAR 12 + +/ One-parameter shortcuts for date/time data types / +#if (ODBCVER >= 0x0300) +#define SQL_TYPE_DATE 91 +#define SQL_TYPE_TIME 92 +#define SQL_TYPE_TIMESTAMP 93 + +#define SQL_UNICODE (-95) +#define SQL_UNICODE_VARCHAR (-96) +#define SQL_UNICODE_LONGVARCHAR (-97) +*/ + function ODBCTypes($t) + { + switch ((integer)$t) { + case 1: + case 12: + case 0: + case -95: + case -96: + return 'C'; + case -97: + case -1: /* text */ + return 'X'; + case -4: /* image */ + return 'B'; + + case 91: + case 11: + return 'D'; + + case 92: + case 93: + case 9: return 'T'; + case 4: + case 5: + case -6: + return 'I'; + + case -11: /* uniqidentifier */ + return 'R'; + case -7: /* bit */ + return 'L'; + + default: + return 'N'; + } + } + + function &MetaColumns($table) + { + global $ADODB_FETCH_MODE; + + $table = strtoupper($table); + + $savem = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + if (false) { /* after testing, confirmed that the following does not work becoz of a bug */ + $qid2 = odbc_tables($this->_connectionID); + $rs = new ADORecordSet_odbc($qid2); + $ADODB_FETCH_MODE = $savem; + if (!$rs) return false; + $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change; + $rs->_fetch(); + + while (!$rs->EOF) { + if ($table == strtoupper($rs->fields[2])) { + $q = $rs->fields[0]; + $o = $rs->fields[1]; + break; + } + $rs->MoveNext(); + } + $rs->Close(); + + $qid = odbc_columns($this->_connectionID,$q,$o,strtoupper($table),'%'); + } else switch ($this->databaseType) { + case 'access': + case 'vfp': + case 'db2': + $qid = odbc_columns($this->_connectionID); + break; + + default: + $qid = @odbc_columns($this->_connectionID,'%','%',strtoupper($table),'%'); + if (empty($qid)) $qid = odbc_columns($this->_connectionID); + break; + } + if (empty($qid)) return false; + + $rs = new ADORecordSet_odbc($qid); + $ADODB_FETCH_MODE = $savem; + + if (!$rs) return false; + + /* print_r($rs); */ + $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change; + $rs->_fetch(); + $retarr = array(); + + /* + $rs->fields indices + 0 TABLE_QUALIFIER + 1 TABLE_SCHEM + 2 TABLE_NAME + 3 COLUMN_NAME + 4 DATA_TYPE + 5 TYPE_NAME + 6 PRECISION + 7 LENGTH + 8 SCALE + 9 RADIX + 10 NULLABLE + 11 REMARKS + */ + while (!$rs->EOF) { + /* print_r($rs->fields); */ + if (strtoupper($rs->fields[2]) == $table) { + $fld = new ADOFieldObject(); + $fld->name = $rs->fields[3]; + $fld->type = $this->ODBCTypes($rs->fields[4]); + + /* ref: http://msdn.microsoft.com/library/default.asp?url=/archive/en-us/dnaraccgen/html/msdn_odk.asp */ + /* access uses precision to store length for char/varchar */ + if ($fld->type == 'C' or $fld->type == 'X') { + if ($this->databaseType == 'access') + $fld->max_length = $rs->fields[6]; + else if ($rs->fields[4] <= -95) /* UNICODE */ + $fld->max_length = $rs->fields[7]/2; + else + $fld->max_length = $rs->fields[7]; + } else + $fld->max_length = $rs->fields[7]; + $fld->not_null = !empty($rs->fields[10]); + $fld->scale = $rs->fields[8]; + $retarr[strtoupper($fld->name)] = $fld; + } else if (sizeof($retarr)>0) + break; + $rs->MoveNext(); + } + $rs->Close(); /* -- crashes 4.03pl1 -- why? */ + + return $retarr; + } + + function Prepare($sql) + { + if (! $this->_bindInputArray) return $sql; /* no binding */ + $stmt = odbc_prepare($this->_connectionID,$sql); + if (!$stmt) { + /* print "Prepare Error for ($sql) ".$this->ErrorMsg()."
"; */ + return $sql; + } + return array($sql,$stmt,false); + } + + /* returns queryID or false */ + function _query($sql,$inputarr=false) + { + GLOBAL $php_errormsg; + $php_errormsg = ''; + $this->_error = ''; + + if ($inputarr) { + if (is_array($sql)) { + $stmtid = $sql[1]; + } else { + $stmtid = odbc_prepare($this->_connectionID,$sql); + + if ($stmtid == false) { + $this->_errorMsg = $php_errormsg; + return false; + } + } + if (! odbc_execute($stmtid,$inputarr)) { + /* @odbc_free_result($stmtid); */ + return false; + } + + } else if (is_array($sql)) { + $stmtid = $sql[1]; + if (!odbc_execute($stmtid)) { + /* @odbc_free_result($stmtid); */ + return false; + } + } else + $stmtid = odbc_exec($this->_connectionID,$sql); + + $this->_lastAffectedRows = 0; + if ($stmtid) { + if (@odbc_num_fields($stmtid) == 0) { + $this->_lastAffectedRows = odbc_num_rows($stmtid); + $stmtid = true; + } else { + $this->_lastAffectedRows = 0; + odbc_binmode($stmtid,$this->binmode); + odbc_longreadlen($stmtid,$this->maxblobsize); + } + } + + $this->_errorMsg = $php_errormsg; + return $stmtid; + } + + /* + Insert a null into the blob field of the table first. + Then use UpdateBlob to store the blob. + + Usage: + + $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); + $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1'); + */ + function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') + { + return $this->Execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false; + } + + /* returns true or false */ + function _close() + { + $ret = @odbc_close($this->_connectionID); + $this->_connectionID = false; + return $ret; + } + + function _affectedrows() + { + return $this->_lastAffectedRows; + } + +} + +/*-------------------------------------------------------------------------------------- + Class Name: Recordset +--------------------------------------------------------------------------------------*/ + +class ADORecordSet_odbc extends ADORecordSet { + + var $bind = false; + var $databaseType = "odbc"; + var $dataProvider = "odbc"; + var $useFetchArray; + var $_has_stupid_odbc_fetch_api_change; + + function ADORecordSet_odbc($id,$mode=false) + { + if ($mode === false) { + global $ADODB_FETCH_MODE; + $mode = $ADODB_FETCH_MODE; + } + $this->fetchMode = $mode; + + $this->_queryID = $id; + + /* the following is required for mysql odbc driver in 4.3.1 -- why? */ + $this->EOF = false; + $this->_currentRow = -1; + /* $this->ADORecordSet($id); */ + } + + + /* returns the field object */ + function &FetchField($fieldOffset = -1) + { + + $off=$fieldOffset+1; /* offsets begin at 1 */ + + $o= new ADOFieldObject(); + $o->name = @odbc_field_name($this->_queryID,$off); + $o->type = @odbc_field_type($this->_queryID,$off); + $o->max_length = @odbc_field_len($this->_queryID,$off); + if (ADODB_ASSOC_CASE == 0) $o->name = strtolower($o->name); + else if (ADODB_ASSOC_CASE == 1) $o->name = strtoupper($o->name); + return $o; + } + + /* Use associative array to get fields array */ + function Fields($colname) + { + if ($this->fetchMode & ADODB_FETCH_ASSOC) 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)]]; + } + + + function _initrs() + { + global $ADODB_COUNTRECS; + $this->_numOfRows = ($ADODB_COUNTRECS) ? @odbc_num_rows($this->_queryID) : -1; + $this->_numOfFields = @odbc_num_fields($this->_queryID); + /* some silly drivers such as db2 as/400 and intersystems cache return _numOfRows = 0 */ + if ($this->_numOfRows == 0) $this->_numOfRows = -1; + /* $this->useFetchArray = $this->connection->useFetchArray; */ + $this->_has_stupid_odbc_fetch_api_change = ADODB_PHPVER >= 0x4200; + } + + function _seek($row) + { + return false; + } + + /* speed up SelectLimit() by switching to ADODB_FETCH_NUM as ADODB_FETCH_ASSOC is emulated */ + function &GetArrayLimit($nrows,$offset=-1) + { + if ($offset <= 0) return $this->GetArray($nrows); + $savem = $this->fetchMode; + $this->fetchMode = ADODB_FETCH_NUM; + $this->Move($offset); + $this->fetchMode = $savem; + + if ($this->fetchMode & ADODB_FETCH_ASSOC) { + $this->fields = $this->GetRowAssoc(ADODB_ASSOC_CASE); + } + + $results = array(); + $cnt = 0; + while (!$this->EOF && $nrows != $cnt) { + $results[$cnt++] = $this->fields; + $this->MoveNext(); + } + + return $results; + } + + + function MoveNext() + { + if ($this->_numOfRows != 0 && !$this->EOF) { + $this->_currentRow++; + $row = 0; + if ($this->_has_stupid_odbc_fetch_api_change) + $rez = @odbc_fetch_into($this->_queryID,$this->fields); + else + $rez = @odbc_fetch_into($this->_queryID,$row,$this->fields); + if ($rez) { + if ($this->fetchMode & ADODB_FETCH_ASSOC) { + $this->fields = $this->GetRowAssoc(ADODB_ASSOC_CASE); + } + return true; + } + } + $this->fields = false; + $this->EOF = true; + return false; + } + + function _fetch() + { + $row = 0; + if ($this->_has_stupid_odbc_fetch_api_change) + $rez = @odbc_fetch_into($this->_queryID,$this->fields,$row); + else + $rez = @odbc_fetch_into($this->_queryID,$row,$this->fields); + + if ($rez) { + if ($this->fetchMode & ADODB_FETCH_ASSOC) { + $this->fields = $this->GetRowAssoc(ADODB_ASSOC_CASE); + } + return true; + } + $this->fields = false; + return false; + } + + function _close() + { + return @odbc_free_result($this->_queryID); + } + +} + +?> diff --git a/lib/adodb/drivers/adodb-odbc_mssql.inc.php b/lib/adodb/drivers/adodb-odbc_mssql.inc.php index 1f2bbf0570..74923bd553 100644 --- a/lib/adodb/drivers/adodb-odbc_mssql.inc.php +++ b/lib/adodb/drivers/adodb-odbc_mssql.inc.php @@ -1,135 +1,164 @@ -ADODB_odbc(); - } - - function xServerInfo() - { - $row = $this->GetRow("execute sp_server_info 2"); - $arr['description'] = $row[2]; - $arr['version'] = ADOConnection::_findvers($arr['description']); - return $arr; - } - - - 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 MetaTables() - { - return ADOConnection::MetaTables(); - } - - function MetaColumns($table) - { - return ADOConnection::MetaColumns($table); - } - - // "Stein-Aksel Basma"- // tested with MSSQL 2000 - function MetaPrimaryKeys($table) - { - $sql = "select k.column_name 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'"; - - $a = $this->GetCol($sql); - if ($a && sizeof($a)>0) return $a; - return false; - } - - // Format date column in sql string given an input format that understands Y M D - function SQLDate($fmt, $col=false) - { - if (!$col) $col = $this->sysDate; - $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': - 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; - default: - if ($ch == '\\') { - $i++; - $ch = substr($fmt,$i,1); - } - $s .= $this->qstr($ch); - break; - } - } - return $s; - } -} - -class ADORecordSet_odbc_mssql extends ADORecordSet_odbc { - - var $databaseType = 'odbc_mssql'; - - function ADORecordSet_odbc_mssql($id,$mode=false) - { - return $this->ADORecordSet_odbc($id,$mode); - } -} +ADODB_odbc(); + } + + /* crashes php... */ + function xServerInfo() + { + $row = $this->GetRow("execute sp_server_info 2"); + $arr['description'] = $row[2]; + $arr['version'] = ADOConnection::_findvers($arr['description']); + return $arr; + } + + + 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 &MetaTables() + { + return ADOConnection::MetaTables(); + } + + function &MetaColumns($table) + { + return ADOConnection::MetaColumns($table); + } + + function _query($sql,$inputarr) + { + if (is_string($sql)) $sql = str_replace('||','+',$sql); + return ADODB_odbc::_query($sql,$inputarr); + } + + /* "Stein-Aksel Basma" */ + /* tested with MSSQL 2000 */ + function &MetaPrimaryKeys($table) + { + $sql = "select k.column_name 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'"; + + $a = $this->GetCol($sql); + if ($a && sizeof($a)>0) return $a; + return false; + } + + /* 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(mi,$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; + } + +} + +class ADORecordSet_odbc_mssql extends ADORecordSet_odbc { + + var $databaseType = 'odbc_mssql'; + + function ADORecordSet_odbc_mssql($id,$mode=false) + { + return $this->ADORecordSet_odbc($id,$mode); + } +} ?> \ No newline at end of file diff --git a/lib/adodb/drivers/adodb-odbc_oracle.inc.php b/lib/adodb/drivers/adodb-odbc_oracle.inc.php index a628ea6f17..93f969ce48 100644 --- a/lib/adodb/drivers/adodb-odbc_oracle.inc.php +++ b/lib/adodb/drivers/adodb-odbc_oracle.inc.php @@ -1,112 +1,112 @@ -ADODB_odbc(); - } - - function &MetaTables() - { - if ($this->metaTablesSQL) { - $rs = $this->Execute($this->metaTablesSQL); - if ($rs === false) return false; - $arr = $rs->GetArray(); - $arr2 = array(); - for ($i=0; $i < sizeof($arr); $i++) { - $arr2[] = $arr[$i][0]; - } - $rs->Close(); - return $arr2; - } - return false; - } - - function &MetaColumns($table) - { - if (!empty($this->metaColumnsSQL)) { - - $rs = $this->Execute(sprintf($this->metaColumnsSQL,strtoupper($table))); - if ($rs === false) 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]; - $retarr[strtoupper($fld->name)] = $fld; - - $rs->MoveNext(); - } - $rs->Close(); - return $retarr; - } - return false; - } - - // returns true or false - function _connect($argDSN, $argUsername, $argPassword, $argDatabasename) - { - global $php_errormsg; - - $php_errormsg = ''; - $this->_connectionID = odbc_connect($argDSN,$argUsername,$argPassword,SQL_CUR_USE_ODBC ); - $this->_errorMsg = $php_errormsg; - - $this->Execute("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'"); - //if ($this->_connectionID) odbc_autocommit($this->_connectionID,true); - return $this->_connectionID != false; - } - // returns true or false - function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename) - { - global $php_errormsg; - $php_errormsg = ''; - $this->_connectionID = odbc_pconnect($argDSN,$argUsername,$argPassword,SQL_CUR_USE_ODBC ); - $this->_errorMsg = $php_errormsg; - - $this->Execute("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'"); - //if ($this->_connectionID) odbc_autocommit($this->_connectionID,true); - return $this->_connectionID != false; - } -} - -class ADORecordSet_odbc_oracle extends ADORecordSet_odbc { - - var $databaseType = 'odbc_oracle'; - - function ADORecordSet_odbc_oracle($id,$mode=false) - { - return $this->ADORecordSet_odbc($id,$mode); - } -} +ADODB_odbc(); + } + + function &MetaTables() + { + if ($this->metaTablesSQL) { + $rs = $this->Execute($this->metaTablesSQL); + if ($rs === false) return false; + $arr = $rs->GetArray(); + $arr2 = array(); + for ($i=0; $i < sizeof($arr); $i++) { + $arr2[] = $arr[$i][0]; + } + $rs->Close(); + return $arr2; + } + return false; + } + + function &MetaColumns($table) + { + if (!empty($this->metaColumnsSQL)) { + + $rs = $this->Execute(sprintf($this->metaColumnsSQL,strtoupper($table))); + if ($rs === false) 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]; + $retarr[strtoupper($fld->name)] = $fld; + + $rs->MoveNext(); + } + $rs->Close(); + return $retarr; + } + return false; + } + + /* returns true or false */ + function _connect($argDSN, $argUsername, $argPassword, $argDatabasename) + { + global $php_errormsg; + + $php_errormsg = ''; + $this->_connectionID = odbc_connect($argDSN,$argUsername,$argPassword,SQL_CUR_USE_ODBC ); + $this->_errorMsg = $php_errormsg; + + $this->Execute("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'"); + /* if ($this->_connectionID) odbc_autocommit($this->_connectionID,true); */ + return $this->_connectionID != false; + } + /* returns true or false */ + function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename) + { + global $php_errormsg; + $php_errormsg = ''; + $this->_connectionID = odbc_pconnect($argDSN,$argUsername,$argPassword,SQL_CUR_USE_ODBC ); + $this->_errorMsg = $php_errormsg; + + $this->Execute("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'"); + /* if ($this->_connectionID) odbc_autocommit($this->_connectionID,true); */ + return $this->_connectionID != false; + } +} + +class ADORecordSet_odbc_oracle extends ADORecordSet_odbc { + + var $databaseType = 'odbc_oracle'; + + function ADORecordSet_odbc_oracle($id,$mode=false) + { + return $this->ADORecordSet_odbc($id,$mode); + } +} ?> \ No newline at end of file diff --git a/lib/adodb/drivers/adodb-oracle.inc.php b/lib/adodb/drivers/adodb-oracle.inc.php index c4868b421d..f5ec74be28 100644 --- a/lib/adodb/drivers/adodb-oracle.inc.php +++ b/lib/adodb/drivers/adodb-oracle.inc.php @@ -1,268 +1,268 @@ -fmtDate,$d).",'YYYY-MM-DD')"; - } - - // format and return date string in database timestamp format - function DBTimeStamp($ts) - { - if (is_string($ts)) $d = ADORecordSet::UnixTimeStamp($ts); - return 'TO_DATE('.adodb_date($this->fmtTimeStamp,$ts).",'RRRR-MM-DD, HH:MI:SS AM')"; - } - - function BeginTrans() - { - $this->autoCommit = false; - ora_commitoff($this->_connectionID); - return true; - } - - function CommitTrans($ok=true) - { - if (!$ok) return $this->RollbackTrans(); - $ret = ora_commit($this->_connectionID); - ora_commiton($this->_connectionID); - return $ret; - } - - function RollbackTrans() - { - $ret = ora_rollback($this->_connectionID); - ora_commiton($this->_connectionID); - return $ret; - } - - /* there seems to be a bug in the oracle extension -- always returns ORA-00000 - no error */ - function ErrorMsg() - { - $this->_errorMsg = @ora_error($this->_curs); - if (!$this->_errorMsg) $this->_errorMsg = @ora_error($this->_connectionID); - return $this->_errorMsg; - } - - function ErrorNo() - { - $err = @ora_errorcode($this->_curs); - if (!$err) return @ora_errorcode($this->_connectionID); - } - - - // returns true or false - function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) - { - if ($argHostname) putenv("ORACLE_HOME=$argHostname"); - if ($argDatabasename) $argUsername .= "@$argDatabasename"; - //if ($argHostname) print " Connect: 1st argument should be left blank for $this->databaseType
"; - $this->_connectionID = ora_logon($argUsername,$argPassword); - if ($this->_connectionID === false) return false; - if ($this->autoCommit) ora_commiton($this->_connectionID); - if ($this->_initdate) { - $rs = $this->_query("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD'"); - if ($rs) ora_close($rs); - } - return true; - } - // returns true or false - function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) - { - if ($argHostname) putenv("ORACLE_HOME=$argHostname"); - if ($argDatabasename) $argUsername .= "@$argDatabasename"; - //if ($argHostname) print "PConnect: 1st argument should be left blank for $this->databaseType
"; - $this->_connectionID = ora_plogon($argUsername,$argPassword); - if ($this->_connectionID === false) return false; - if ($this->autoCommit) ora_commiton($this->_connectionID); - if ($this->autoRollback) ora_rollback($this->_connectionID); - if ($this->_initdate) { - $rs = $this->_query("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD'"); - if ($rs) ora_close($rs); - } - return true; - } - - // returns query ID if successful, otherwise false - function _query($sql,$inputarr=false) - { - $curs = ora_open($this->_connectionID); - - if ($curs === false) return false; - $this->_curs = $curs; - if (!ora_parse($curs,$sql)) return false; - if (ora_exec($curs)) return $curs; - - @ora_close($curs); - return false; - } - - // returns true or false - function _close() - { - return @ora_close($this->_connectionID); - } - - -} - -/*-------------------------------------------------------------------------------------- - Class Name: Recordset ---------------------------------------------------------------------------------------*/ - -class ADORecordset_oracle extends ADORecordSet { - - var $databaseType = "oracle"; - var $bind = false; - - function ADORecordset_oracle($queryID,$mode=false) - { - - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - $this->fetchMode = $mode; - - $this->_queryID = $queryID; - - $this->_inited = true; - $this->fields = array(); - if ($queryID) { - $this->_currentRow = 0; - $this->EOF = !$this->_fetch(); - @$this->_initrs(); - } else { - $this->_numOfRows = 0; - $this->_numOfFields = 0; - $this->EOF = true; - } - - return $this->_queryID; - } - - - - /* 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; - $fld->name = ora_columnname($this->_queryID, $fieldOffset); - $fld->type = ora_columntype($this->_queryID, $fieldOffset); - $fld->max_length = ora_columnsize($this->_queryID, $fieldOffset); - return $fld; - } - - /* 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 _initrs() - { - $this->_numOfRows = -1; - $this->_numOfFields = @ora_numcols($this->_queryID); - } - - - function _seek($row) - { - return false; - } - - function _fetch($ignore_fields=false) { -// should remove call by reference, but ora_fetch_into requires it in 4.0.3pl1 - if ($this->fetchMode & ADODB_FETCH_ASSOC) - return @ora_fetch_into($this->_queryID,&$this->fields,ORA_FETCHINTO_NULLS|ORA_FETCHINTO_ASSOC); - else - return @ora_fetch_into($this->_queryID,&$this->fields,ORA_FETCHINTO_NULLS); - } - - /* 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() -{ - return @ora_close($this->_queryID); - } - - 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': - if ($len <= $this->blobSize) return 'C'; - case 'LONG': - case 'LONG VARCHAR': - case 'CLOB': - return 'X'; - case 'LONG RAW': - case 'LONG VARBINARY': - case 'BLOB': - return 'B'; - - case 'DATE': return 'D'; - - //case 'T': return 'T'; - - case 'BIT': return 'L'; - case 'INT': - case 'SMALLINT': - case 'INTEGER': return 'I'; - default: return 'N'; - } - } -} +fmtDate,$d).",'YYYY-MM-DD')"; + } + + /* format and return date string in database timestamp format */ + function DBTimeStamp($ts) + { + if (is_string($ts)) $d = ADORecordSet::UnixTimeStamp($ts); + return 'TO_DATE('.adodb_date($this->fmtTimeStamp,$ts).",'RRRR-MM-DD, HH:MI:SS AM')"; + } + + function BeginTrans() + { + $this->autoCommit = false; + ora_commitoff($this->_connectionID); + return true; + } + + function CommitTrans($ok=true) + { + if (!$ok) return $this->RollbackTrans(); + $ret = ora_commit($this->_connectionID); + ora_commiton($this->_connectionID); + return $ret; + } + + function RollbackTrans() + { + $ret = ora_rollback($this->_connectionID); + ora_commiton($this->_connectionID); + return $ret; + } + + /* there seems to be a bug in the oracle extension -- always returns ORA-00000 - no error */ + function ErrorMsg() + { + $this->_errorMsg = @ora_error($this->_curs); + if (!$this->_errorMsg) $this->_errorMsg = @ora_error($this->_connectionID); + return $this->_errorMsg; + } + + function ErrorNo() + { + $err = @ora_errorcode($this->_curs); + if (!$err) return @ora_errorcode($this->_connectionID); + } + + + /* returns true or false */ + function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + if ($argHostname) putenv("ORACLE_HOME=$argHostname"); + if ($argDatabasename) $argUsername .= "@$argDatabasename"; + /* if ($argHostname) print "Connect: 1st argument should be left blank for $this->databaseType
"; */ + $this->_connectionID = ora_logon($argUsername,$argPassword); + if ($this->_connectionID === false) return false; + if ($this->autoCommit) ora_commiton($this->_connectionID); + if ($this->_initdate) { + $rs = $this->_query("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD'"); + if ($rs) ora_close($rs); + } + return true; + } + /* returns true or false */ + function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + if ($argHostname) putenv("ORACLE_HOME=$argHostname"); + if ($argDatabasename) $argUsername .= "@$argDatabasename"; + /* if ($argHostname) print "PConnect: 1st argument should be left blank for $this->databaseType
"; */ + $this->_connectionID = ora_plogon($argUsername,$argPassword); + if ($this->_connectionID === false) return false; + if ($this->autoCommit) ora_commiton($this->_connectionID); + if ($this->autoRollback) ora_rollback($this->_connectionID); + if ($this->_initdate) { + $rs = $this->_query("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD'"); + if ($rs) ora_close($rs); + } + return true; + } + + /* returns query ID if successful, otherwise false */ + function _query($sql,$inputarr=false) + { + $curs = ora_open($this->_connectionID); + + if ($curs === false) return false; + $this->_curs = $curs; + if (!ora_parse($curs,$sql)) return false; + if (ora_exec($curs)) return $curs; + + @ora_close($curs); + return false; + } + + /* returns true or false */ + function _close() + { + return @ora_logoff($this->_connectionID); + } + + +} + +/*-------------------------------------------------------------------------------------- + Class Name: Recordset +--------------------------------------------------------------------------------------*/ + +class ADORecordset_oracle extends ADORecordSet { + + var $databaseType = "oracle"; + var $bind = false; + + function ADORecordset_oracle($queryID,$mode=false) + { + + if ($mode === false) { + global $ADODB_FETCH_MODE; + $mode = $ADODB_FETCH_MODE; + } + $this->fetchMode = $mode; + + $this->_queryID = $queryID; + + $this->_inited = true; + $this->fields = array(); + if ($queryID) { + $this->_currentRow = 0; + $this->EOF = !$this->_fetch(); + @$this->_initrs(); + } else { + $this->_numOfRows = 0; + $this->_numOfFields = 0; + $this->EOF = true; + } + + return $this->_queryID; + } + + + + /* 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; + $fld->name = ora_columnname($this->_queryID, $fieldOffset); + $fld->type = ora_columntype($this->_queryID, $fieldOffset); + $fld->max_length = ora_columnsize($this->_queryID, $fieldOffset); + return $fld; + } + + /* 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 _initrs() + { + $this->_numOfRows = -1; + $this->_numOfFields = @ora_numcols($this->_queryID); + } + + + function _seek($row) + { + return false; + } + + function _fetch($ignore_fields=false) { +/* should remove call by reference, but ora_fetch_into requires it in 4.0.3pl1 */ + if ($this->fetchMode & ADODB_FETCH_ASSOC) + return @ora_fetch_into($this->_queryID,&$this->fields,ORA_FETCHINTO_NULLS|ORA_FETCHINTO_ASSOC); + else + return @ora_fetch_into($this->_queryID,&$this->fields,ORA_FETCHINTO_NULLS); + } + + /* 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() +{ + return @ora_close($this->_queryID); + } + + 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': + if ($len <= $this->blobSize) return 'C'; + case 'LONG': + case 'LONG VARCHAR': + case 'CLOB': + return 'X'; + case 'LONG RAW': + case 'LONG VARBINARY': + case 'BLOB': + return 'B'; + + case 'DATE': return 'D'; + + /* case 'T': return 'T'; */ + + case 'BIT': return 'L'; + case 'INT': + case 'SMALLINT': + case 'INTEGER': return 'I'; + default: return 'N'; + } + } +} ?> \ No newline at end of file diff --git a/lib/adodb/drivers/adodb-postgres.inc.php b/lib/adodb/drivers/adodb-postgres.inc.php index dbbc7286ac..3cc3fbb51f 100644 --- a/lib/adodb/drivers/adodb-postgres.inc.php +++ b/lib/adodb/drivers/adodb-postgres.inc.php @@ -1,14 +1,14 @@ - \ No newline at end of file diff --git a/lib/adodb/drivers/adodb-postgres64.inc.php b/lib/adodb/drivers/adodb-postgres64.inc.php index 94b42d437e..6af60142d0 100644 --- a/lib/adodb/drivers/adodb-postgres64.inc.php +++ b/lib/adodb/drivers/adodb-postgres64.inc.php @@ -1,710 +1,738 @@ - - jlim - changed concat operator to || and data types to MetaType to match documented pgsql types - see http://www.postgresql.org/devel-corner/docs/postgres/datatype.htm - 22 Nov 2000 jlim - added changes to FetchField() and MetaTables() contributed by "raser"- 27 Nov 2000 jlim - added changes to _connect/_pconnect from ideas by "Lennie" - 15 Dec 2000 jlim - added changes suggested by Additional code changes by "Eric G. Werk" egw@netguide.dk. - 31 Jan 2002 jlim - finally installed postgresql. testing - 01 Mar 2001 jlim - Freek Dijkstra changes, also support for text type -*/ - -function adodb_addslashes($s) -{ - $len = strlen($s); - if ($len == 0) return "''"; - if (substr($s,0,1) == "'" && substr(s,$len-1) == "'") return $s; // already quoted - - return "'".addslashes($s)."'"; -} - -class ADODB_postgres64 extends ADOConnection{ - var $databaseType = 'postgres64'; - var $dataProvider = 'postgres'; - var $hasInsertID = true; - var $_resultid = false; - var $concat_operator='||'; - var $metaTablesSQL = "select tablename from pg_tables where tablename not like 'pg\_%' order by 1"; - //"select tablename from pg_tables where tablename not like 'pg_%' order by 1"; - var $isoDates = true; // accepts dates in ISO format - var $sysDate = "CURRENT_DATE"; - var $sysTimeStamp = "CURRENT_TIMESTAMP"; - var $blobEncodeType = 'C'; -/* -# show tables and views suggestion -"SELECT c.relname AS tablename FROM pg_class c - WHERE (c.relhasrules AND (EXISTS ( - SELECT r.rulename FROM pg_rewrite r WHERE r.ev_class = c.oid AND bpchar(r.ev_type) = '1' - ))) OR (c.relkind = 'v') AND c.relname NOT LIKE 'pg_%' -UNION -SELECT tablename FROM pg_tables WHERE tablename NOT LIKE 'pg_%' ORDER BY 1" -*/ - var $metaColumnsSQL = "SELECT a.attname,t.typname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,a.attnum FROM pg_class c, pg_attribute a,pg_type t WHERE relkind = 'r' AND c.relname='%s' AND a.attnum > 0 AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum"; - // get primary key etc -- from Freek Dijkstra - var $metaKeySQL = "SELECT ic.relname AS index_name, a.attname AS column_name,i.indisunique AS unique_key, i.indisprimary AS primary_key FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a WHERE bc.oid = i.indrelid AND ic.oid = i.indexrelid AND (i.indkey[0] = a.attnum OR i.indkey[1] = a.attnum OR i.indkey[2] = a.attnum OR i.indkey[3] = a.attnum OR i.indkey[4] = a.attnum OR i.indkey[5] = a.attnum OR i.indkey[6] = a.attnum OR i.indkey[7] = a.attnum) AND a.attrelid = bc.oid AND bc.relname = '%s'"; - - var $hasAffectedRows = true; - var $hasLimit = false; // set to true for pgsql 7 only. support pgsql/mysql SELECT * FROM TABLE LIMIT 10 - // below suggested by Freek Dijkstra - var $true = 't'; // string that represents TRUE for a database - var $false = 'f'; // string that represents FALSE for a database - var $fmtDate = "'Y-m-d'"; // used by DBDate() as the default date format used by the database - var $fmtTimeStamp = "'Y-m-d G:i:s'"; // used by DBTimeStamp as the default timestamp fmt. - var $hasMoveFirst = true; - var $hasGenID = true; - var $_genIDSQL = "SELECT NEXTVAL('%s')"; - var $_genSeqSQL = "CREATE SEQUENCE %s START %s"; - var $_dropSeqSQL = "DROP SEQUENCE %s"; - var $metaDefaultsSQL = "SELECT d.adnum as num, d.adsrc as def from pg_attrdef d, pg_class c where d.adrelid=c.oid and c.relname='%s' order by d.adnum"; - - - // The last (fmtTimeStamp is not entirely correct: - // PostgreSQL also has support for time zones, - // and writes these time in this format: "2001-03-01 18:59:26+02". - // There is no code for the "+02" time zone information, so I just left that out. - // I'm not familiar enough with both ADODB as well as Postgres - // to know what the concequences are. The other values are correct (wheren't in 0.94) - // -- Freek Dijkstra - - function ADODB_postgres64() - { - // changes the metaColumnsSQL, adds columns: attnum[6] - } - - function ServerInfo() - { - $arr['description'] = $this->GetOne("select version()"); - $arr['version'] = ADOConnection::_findvers($arr['description']); - return $arr; - } - - // get the last id - never tested - function pg_insert_id($tablename,$fieldname) - { - $result=pg_exec($this->_connectionID, "SELECT last_value FROM ${tablename}_${fieldname}_seq"); - if ($result) { - $arr = @pg_fetch_row($result,0); - pg_freeresult($result); - if (isset($arr[0])) return $arr[0]; - } - return false; - } - -/* Warning from http://www.php.net/manual/function.pg-getlastoid.php: -Using a OID as a unique identifier is not generally wise. -Unless you are very careful, you might end up with a tuple having -a different OID if a database must be reloaded. */ - function _insertid() - { - if (!is_resource($this->_resultid)) return false; - return pg_getlastoid($this->_resultid); - } - -// I get this error with PHP before 4.0.6 - jlim -// Warning: This compilation does not support pg_cmdtuples() in d:/inetpub/wwwroot/php/adodb/adodb-postgres.inc.php on line 44 - function _affectedrows() - { - if (!is_resource($this->_resultid)) return false; - return pg_cmdtuples($this->_resultid); - } - - - // returns true/false - function BeginTrans() - { - if ($this->transOff) return true; - $this->transCnt += 1; - return @pg_Exec($this->_connectionID, "begin"); - } - - function RowLock($tables,$where) - { - if (!$this->transCnt) $this->BeginTrans(); - return $this->GetOne("select 1 as ignore from $tables where $where for update"); - } - - // returns true/false. - function CommitTrans($ok=true) - { - if ($this->transOff) return true; - if (!$ok) return $this->RollbackTrans(); - - $this->transCnt -= 1; - return @pg_Exec($this->_connectionID, "commit"); - } - - // returns true/false - function RollbackTrans() - { - if ($this->transOff) return true; - $this->transCnt -= 1; - return @pg_Exec($this->_connectionID, "rollback"); - } - /* - // if magic quotes disabled, use pg_escape_string() - function qstr($s,$magic_quotes=false) - { - if (!$magic_quotes) { - if (ADODB_PHPVER >= 0x4200) { - return "'".pg_escape_string($s)."'"; - } - if ($this->replaceQuote[0] == '\\'){ - $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s); - } - return "'".str_replace("'",$this->replaceQuote,$s)."'"; - } - - // undo magic quotes for " - $s = str_replace('\\"','"',$s); - return "'$s'"; - } - */ - - // Format date column in sql string given an input format that understands Y M D - function SQLDate($fmt, $col=false) - { - if (!$col) $col = $this->sysDate; - $s = ''; - - $len = strlen($fmt); - for ($i=0; $i < $len; $i++) { - if ($s) $s .= '||'; - $ch = $fmt[$i]; - switch($ch) { - case 'Y': - case 'y': - $s .= "date_part('year',$col)"; - break; - case 'Q': - case 'q': - $s .= "date_part('quarter',$col)"; - break; - - case 'M': - case 'm': - $s .= "lpad(date_part('month',$col),2,'0')"; - break; - case 'D': - case 'd': - $s .= "lpad(date_part('day',$col),2,'0')"; - break; - default: - if ($ch == '\\') { - $i++; - $ch = substr($fmt,$i,1); - } - $s .= $this->qstr($ch); - break; - } - } - return $s; - } - - /* - * Load a Large Object from a file - * - the procedure stores the object id in the table and imports the object using - * postgres proprietary blob handling routines - * - * contributed by Mattia Rossi mattia@technologist.com - * modified for safe mode by juraj chlebec - */ - function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB') - { - pg_exec ($this->_connectionID, "begin"); - - $fd = fopen($path,'r'); - $contents = fread($fd,filesize($path)); - fclose($fd); - - $oid = pg_lo_create($this->_connectionID); - $handle = pg_lo_open($this->_connectionID, $oid, 'w'); - pg_lo_write($handle, $contents); - pg_lo_close($handle); - - // $oid = pg_lo_import ($path); - pg_exec ($this->_connectionID, "commit"); - $rs = ADOConnection::UpdateBlob($table,$column,$oid,$where,$blobtype); - $rez = !empty($rs); - return $rez; - } - - /* - * If an OID is detected, then we use pg_lo_* to open the oid file and read the - * real blob from the db using the oid supplied as a parameter. If you are storing - * blobs using bytea, we autodetect and process it so this function is not needed. - * - * contributed by Mattia Rossi mattia@technologist.com - * - * see http://www.postgresql.org/idocs/index.php?largeobjects.html - */ - function BlobDecode( $blob) - { - @pg_exec("begin"); - $fd = @pg_lo_open($blob,"r"); - if ($fd === false) { - @pg_exec("commit"); - return $blob; - } - $realblob = @pg_loreadall($fd); - @pg_loclose($fd); - @pg_exec("commit"); - return $realblob; - } - - /* - See http://www.postgresql.org/idocs/index.php?datatype-binary.html - - NOTE: SQL string literals (input strings) must be preceded with two backslashes - due to the fact that they must pass through two parsers in the PostgreSQL - backend. - */ - function BlobEncode($blob) - { // requires php 4.0.5 - $badch = array(chr(92),chr(0),chr(39)); # \ null ' - $fixch = array('\\\\134','\\\\000','\\\\047'); - return adodb_str_replace($badch,$fixch,$blob); - - // note that there is a pg_escape_bytea function only for php 4.2.0 or later - } - - function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') - { - return $this->Execute("UPDATE $table SET $column=? WHERE $where", - array($this->BlobEncode($val))) != false; - } - - function OffsetDate($dayFraction,$date=false) - { - if (!$date) $date = $this->sysDate; - return "($date+interval'$dayFraction days')"; - } - - - // converts field names to lowercase - function &MetaColumns($table) - { - global $ADODB_FETCH_MODE; - - if (strncmp(PHP_OS,"WIN",3) === 0) $table = strtolower($table); - - if (!empty($this->metaColumnsSQL)) { - $save = $ADODB_FETCH_MODE; - $ADODB_FETCH_MODE = ADODB_FETCH_NUM; - if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); - $rs = $this->Execute(sprintf($this->metaColumnsSQL,($table))); - if (isset($savem)) $this->SetFetchMode($savem); - $ADODB_FETCH_MODE = $save; - - if ($rs === false) return false; - - if (!empty($this->metaKeySQL)) { - // If we want the primary keys, we have to issue a separate query - // Of course, a modified version of the metaColumnsSQL query using a - // LEFT JOIN would have been much more elegant, but postgres does - // not support OUTER JOINS. So here is the clumsy way. - - $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; - - $rskey = $this->Execute(sprintf($this->metaKeySQL,($table))); - // fetch all result in once for performance. - $keys = $rskey->GetArray(); - if (isset($savem)) $this->SetFetchMode($savem); - $ADODB_FETCH_MODE = $save; - - $rskey->Close(); - unset($rskey); - } - - $rsdefa = array(); - if (!empty($this->metaDefaultsSQL)) { - $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; - $sql = sprintf($this->metaDefaultsSQL, ($table)); - $rsdef = $this->Execute($sql); - if (isset($savem)) $this->SetFetchMode($savem); - $ADODB_FETCH_MODE = $save; - - if ($rsdef) { - while (!$rsdef->EOF) { - $num = $rsdef->fields['num']; - $s = $rsdef->fields['def']; - if (substr($s, 0, 1) == "'") { /* quoted strings hack... for now... fixme */ - $s = substr($s, 1); - $s = substr($s, 0, strlen($s) - 1); - } - - $rsdefa[$num] = $s; - $rsdef->MoveNext(); - } - } else { - ADOConnection::outp( "==> SQL => " . $sql); - } - unset($rsdef); - } - - $retarr = array(); - while (!$rs->EOF) { - $fld = new ADOFieldObject(); - $fld->name = $rs->fields[0]; - $fld->type = $rs->fields[1]; - $fld->max_length = $rs->fields[2]; - if ($fld->max_length <= 0) $fld->max_length = $rs->fields[3]-4; - if ($fld->max_length <= 0) $fld->max_length = -1; - - // dannym - // 5 hasdefault; 6 num-of-column - $fld->has_default = ($rs->fields[5] == 't'); - if ($fld->has_default) { - $fld->default_value = $rsdefa[$rs->fields[6]]; - } - - //Freek - if ($rs->fields[4] == $this->true) { - $fld->not_null = true; - } - - // Freek - if (is_array($keys)) { - reset ($keys); - while (list($x,$key) = each($keys)) { - if ($fld->name == $key['column_name'] AND $key['primary_key'] == $this->true) - $fld->primary_key = true; - if ($fld->name == $key['column_name'] AND $key['unique_key'] == $this->true) - $fld->unique = true; // What name is more compatible? - } - } - - $retarr[strtoupper($fld->name)] = $fld; - - $rs->MoveNext(); - } - $rs->Close(); - return $retarr; - } - return false; - } - - function &MetaDatabases() - { - $arr = array(); - $sql="select datname from pg_database"; - $rs = $this->Execute($sql); - if (!$rs) return false; - while (!$rs->EOF) { - $arr[] = reset($rs->fields); - $rs->MoveNext(); - } - - return $arr; - } - - - // returns true or false - // - // examples: - // $db->Connect("host=host1 user=user1 password=secret port=4341"); - // $db->Connect('host1','user1','secret'); - function _connect($str,$user='',$pwd='',$db='',$persist=false) - { - if ($user || $pwd || $db) { - $str = adodb_addslashes($str); - $user = adodb_addslashes($user); - $pwd = adodb_addslashes($pwd); - $db = adodb_addslashes($db); - if ($str) { - $host = split(":", $str); - if ($host[0]) $str = "host=$host[0]"; - else $str = 'localhost'; - if (isset($host[1])) $str .= " port=$host[1]"; - } - if ($user) $str .= " user=".$user; - if ($pwd) $str .= " password=".$pwd; - if ($db) $str .= " dbname=".$db; - } - - //if ($user) $linea = "user=$user host=$linea password=$pwd dbname=$db port=5432"; - if ($persist) $this->_connectionID = pg_pconnect($str); - else $this->_connectionID = pg_connect($str); - if ($this->_connectionID === false) return false; - $this->Execute("set datestyle='ISO'"); - return true; - } - - // returns true or false - // - // examples: - // $db->PConnect("host=host1 user=user1 password=secret port=4341"); - // $db->PConnect('host1','user1','secret'); - function _pconnect($str,$user='',$pwd='',$db='') - { - return $this->_connect($str,$user,$pwd,$db,true); - } - - // returns queryID or false - function _query($sql,$inputarr) - { - $rez = pg_Exec($this->_connectionID,$sql); - // check if no data returned, then no need to create real recordset - if ($rez && pg_numfields($rez) <= 0) { - $this->_resultid = $rez; - return true; - } - return $rez; - } - - - /* Returns: the last error message from previous database operation */ - function ErrorMsg() - { - if (ADODB_PHPVER >= 0x4300) { - if (!empty($this->_resultid)) { - $this->_errorMsg = @pg_result_error($this->_resultid); - if ($this->_errorMsg) return $this->_errorMsg; - } - - if (!empty($this->_connectionID)) { - $this->_errorMsg = @pg_last_error($this->_connectionID); - } else $this->_errorMsg = @pg_last_error(); - } else { - if (empty($this->_connectionID)) $this->_errorMsg = @pg_errormessage(); - else $this->_errorMsg = @pg_errormessage($this->_connectionID); - } - return $this->_errorMsg; - } - - function ErrorNo() - { - $e = $this->ErrorMsg(); - return (strlen($e)) ? $e : 0; - } - - // returns true or false - function _close() - { - if ($this->transCnt) $this->RollbackTrans(); - $this->_resultid = false; - @pg_close($this->_connectionID); - $this->_connectionID = false; - return true; - } - - - /* - * Maximum size of C field - */ - function CharMax() - { - return 1000000000; // should be 1 Gb? - } - - /* - * Maximum size of X field - */ - function TextMax() - { - return 1000000000; // should be 1 Gb? - } - - -} - -/*-------------------------------------------------------------------------------------- - Class Name: Recordset ---------------------------------------------------------------------------------------*/ - -class ADORecordSet_postgres64 extends ADORecordSet{ - var $_blobArr; - var $databaseType = "postgres64"; - var $canSeek = true; - function ADORecordSet_postgres64($queryID,$mode=false) - { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - switch ($mode) - { - case ADODB_FETCH_NUM: $this->fetchMode = PGSQL_NUM; break; - case ADODB_FETCH_ASSOC:$this->fetchMode = PGSQL_ASSOC; break; - default: - case ADODB_FETCH_DEFAULT: - case ADODB_FETCH_BOTH:$this->fetchMode = PGSQL_BOTH; break; - } - $this->ADORecordSet($queryID); - } - - function &GetRowAssoc($upper=true) - { - if ($this->fetchMode == PGSQL_ASSOC && !$upper) return $this->fields; - return ADORecordSet::GetRowAssoc($upper); - } - - function _initrs() - { - global $ADODB_COUNTRECS; - $this->_numOfRows = ($ADODB_COUNTRECS)? @pg_numrows($this->_queryID):-1; - $this->_numOfFields = @pg_numfields($this->_queryID); - - // cache types for blob decode check - for ($i=0, $max = $this->_numOfFields; $i < $max; $i++) { - $f1 = $this->FetchField($i); - if ($f1->type == 'bytea') $this->_blobArr[$i] = $f1->name; - } - } - - /* Use associative array to get fields array */ - function Fields($colname) - { - if ($this->fetchMode != PGSQL_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)]]; - } - - function &FetchField($fieldOffset = 0) - { - $off=$fieldOffset; // offsets begin at 0 - - $o= new ADOFieldObject(); - $o->name = @pg_fieldname($this->_queryID,$off); - $o->type = @pg_fieldtype($this->_queryID,$off); - $o->max_length = @pg_fieldsize($this->_queryID,$off); - //print_r($o); - //print "off=$off name=$o->name type=$o->type len=$o->max_length
"; - return $o; - } - - function _seek($row) - { - return @pg_fetch_row($this->_queryID,$row); - } - - function _decode($blob) - { - - eval('$realblob="'.adodb_str_replace(array('"','$'),array('\"','\$'),$blob).'";'); - return $realblob; - - } - function _fixblobs() - { - if ($this->fetchMode == PGSQL_NUM || $this->fetchMode == PGSQL_BOTH) { - foreach($this->_blobArr as $k => $v) { - $this->fields[$k] = ADORecordSet_postgres64::_decode($this->fields[$k]); - } - } - if ($this->fetchMode == PGSQL_ASSOC || $this->fetchMode == PGSQL_BOTH) { - foreach($this->_blobArr as $k => $v) { - $this->fields[$v] = ADORecordSet_postgres64::_decode($this->fields[$v]); - } - } - } - - // 10% speedup to move MoveNext to child class - function MoveNext() - { - if (!$this->EOF) { - $this->_currentRow++; - if ($this->_numOfRows < 0 || $this->_numOfRows > $this->_currentRow) { - $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode); - - if (is_array($this->fields)) { - if (isset($this->_blobArr)) $this->_fixblobs(); - return true; - } - } - $this->fields = false; - $this->EOF = true; - } - return false; - } - - function _fetch() - { - if ($this->_currentRow >= $this->_numOfRows && $this->_numOfRows >= 0) - return false; - - $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode); - if (isset($this->_blobArr)) $this->_fixblobs(); - - return (is_array($this->fields)); - } - - function _close() - { - return @pg_freeresult($this->_queryID); - } - - function MetaType($t,$len=-1,$fieldobj=false) - { - if (is_object($t)) { - $fieldobj = $t; - $t = $fieldobj->type; - $len = $fieldobj->max_length; - } - switch (strtoupper($t)) { - case 'INTERVAL': - case 'CHAR': - case 'CHARACTER': - case 'VARCHAR': - case 'NAME': - case 'BPCHAR': - if ($len <= $this->blobSize) return 'C'; - - case 'TEXT': - return 'X'; - - case 'IMAGE': // user defined type - case 'BLOB': // user defined type - case 'BIT': // This is a bit string, not a single bit, so don't return 'L' - case 'VARBIT': - case 'BYTEA': - return 'B'; - - case 'BOOL': - case 'BOOLEAN': - return 'L'; - - case 'DATE': - return 'D'; - - case 'TIME': - case 'DATETIME': - case 'TIMESTAMP': - case 'TIMESTAMPTZ': - return 'T'; - - case 'SMALLINT': - case 'BIGINT': - case 'INTEGER': - case 'INT8': - case 'INT4': - case 'INT2': - if (isset($fieldobj) && - empty($fieldobj->primary_key) && empty($fieldobj->unique)) return 'I'; - - case 'OID': - case 'SERIAL': - return 'R'; - - default: - return 'N'; - } - } - -} -?> + + jlim - changed concat operator to || and data types to MetaType to match documented pgsql types + see http://www.postgresql.org/devel-corner/docs/postgres/datatype.htm + 22 Nov 2000 jlim - added changes to FetchField() and MetaTables() contributed by "raser"+ 27 Nov 2000 jlim - added changes to _connect/_pconnect from ideas by "Lennie" + 15 Dec 2000 jlim - added changes suggested by Additional code changes by "Eric G. Werk" egw@netguide.dk. + 31 Jan 2002 jlim - finally installed postgresql. testing + 01 Mar 2001 jlim - Freek Dijkstra changes, also support for text type +*/ + +function adodb_addslashes($s) +{ + $len = strlen($s); + if ($len == 0) return "''"; + if (substr($s,0,1) == "'" && substr(s,$len-1) == "'") return $s; /* already quoted */ + + return "'".addslashes($s)."'"; +} + +class ADODB_postgres64 extends ADOConnection{ + var $databaseType = 'postgres64'; + var $dataProvider = 'postgres'; + var $hasInsertID = true; + var $_resultid = false; + var $concat_operator='||'; + var $metaDatabasesSQL = "select datname from pg_database where datname not in ('template0','template1') order by 1"; + var $metaTablesSQL = "select tablename from pg_tables where tablename not like 'pg\_%' order by 1"; + /* "select tablename from pg_tables where tablename not like 'pg_%' order by 1"; */ + var $isoDates = true; /* accepts dates in ISO format */ + var $sysDate = "CURRENT_DATE"; + var $sysTimeStamp = "CURRENT_TIMESTAMP"; + var $blobEncodeType = 'C'; +/* +# show tables and views suggestion +"SELECT c.relname AS tablename FROM pg_class c + WHERE (c.relhasrules AND (EXISTS ( + SELECT r.rulename FROM pg_rewrite r WHERE r.ev_class = c.oid AND bpchar(r.ev_type) = '1' + ))) OR (c.relkind = 'v') AND c.relname NOT LIKE 'pg_%' +UNION +SELECT tablename FROM pg_tables WHERE tablename NOT LIKE 'pg_%' ORDER BY 1" +*/ + var $metaColumnsSQL = "SELECT a.attname,t.typname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,a.attnum + FROM pg_class c, pg_attribute a,pg_type t + WHERE relkind = 'r' AND c.relname='%s' AND a.attnum > 0 AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum"; + /* get primary key etc -- from Freek Dijkstra */ + var $metaKeySQL = "SELECT ic.relname AS index_name, a.attname AS column_name,i.indisunique AS unique_key, i.indisprimary AS primary_key FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a WHERE bc.oid = i.indrelid AND ic.oid = i.indexrelid AND (i.indkey[0] = a.attnum OR i.indkey[1] = a.attnum OR i.indkey[2] = a.attnum OR i.indkey[3] = a.attnum OR i.indkey[4] = a.attnum OR i.indkey[5] = a.attnum OR i.indkey[6] = a.attnum OR i.indkey[7] = a.attnum) AND a.attrelid = bc.oid AND bc.relname = '%s'"; + + var $hasAffectedRows = true; + var $hasLimit = false; /* set to true for pgsql 7 only. support pgsql/mysql SELECT * FROM TABLE LIMIT 10 */ + /* below suggested by Freek Dijkstra */ + var $true = 't'; /* string that represents TRUE for a database */ + var $false = 'f'; /* string that represents FALSE for a database */ + var $fmtDate = "'Y-m-d'"; /* used by DBDate() as the default date format used by the database */ + var $fmtTimeStamp = "'Y-m-d G:i:s'"; /* used by DBTimeStamp as the default timestamp fmt. */ + var $hasMoveFirst = true; + var $hasGenID = true; + var $_genIDSQL = "SELECT NEXTVAL('%s')"; + var $_genSeqSQL = "CREATE SEQUENCE %s START %s"; + var $_dropSeqSQL = "DROP SEQUENCE %s"; + var $metaDefaultsSQL = "SELECT d.adnum as num, d.adsrc as def from pg_attrdef d, pg_class c where d.adrelid=c.oid and c.relname='%s' order by d.adnum"; + + + /* The last (fmtTimeStamp is not entirely correct: */ + /* PostgreSQL also has support for time zones, */ + /* and writes these time in this format: "2001-03-01 18:59:26+02". */ + /* There is no code for the "+02" time zone information, so I just left that out. */ + /* I'm not familiar enough with both ADODB as well as Postgres */ + /* to know what the concequences are. The other values are correct (wheren't in 0.94) */ + /* -- Freek Dijkstra */ + + function ADODB_postgres64() + { + /* changes the metaColumnsSQL, adds columns: attnum[6] */ + } + + function ServerInfo() + { + $arr['description'] = $this->GetOne("select version()"); + $arr['version'] = ADOConnection::_findvers($arr['description']); + return $arr; + } + + /* get the last id - never tested */ + function pg_insert_id($tablename,$fieldname) + { + $result=pg_exec($this->_connectionID, "SELECT last_value FROM ${tablename}_${fieldname}_seq"); + if ($result) { + $arr = @pg_fetch_row($result,0); + pg_freeresult($result); + if (isset($arr[0])) return $arr[0]; + } + return false; + } + +/* Warning from http://www.php.net/manual/function.pg-getlastoid.php: +Using a OID as a unique identifier is not generally wise. +Unless you are very careful, you might end up with a tuple having +a different OID if a database must be reloaded. */ + function _insertid() + { + if (!is_resource($this->_resultid)) return false; + return pg_getlastoid($this->_resultid); + } + +/* I get this error with PHP before 4.0.6 - jlim */ +/* Warning: This compilation does not support pg_cmdtuples() in d:/inetpub/wwwroot/php/adodb/adodb-postgres.inc.php on line 44 */ + function _affectedrows() + { + if (!is_resource($this->_resultid)) return false; + return pg_cmdtuples($this->_resultid); + } + + + /* returns true/false */ + function BeginTrans() + { + if ($this->transOff) return true; + $this->transCnt += 1; + return @pg_Exec($this->_connectionID, "begin"); + } + + function RowLock($tables,$where) + { + if (!$this->transCnt) $this->BeginTrans(); + return $this->GetOne("select 1 as ignore from $tables where $where for update"); + } + + /* returns true/false. */ + function CommitTrans($ok=true) + { + if ($this->transOff) return true; + if (!$ok) return $this->RollbackTrans(); + + $this->transCnt -= 1; + return @pg_Exec($this->_connectionID, "commit"); + } + + /* returns true/false */ + function RollbackTrans() + { + if ($this->transOff) return true; + $this->transCnt -= 1; + return @pg_Exec($this->_connectionID, "rollback"); + } + /* + // if magic quotes disabled, use pg_escape_string() + function qstr($s,$magic_quotes=false) + { + if (!$magic_quotes) { + if (ADODB_PHPVER >= 0x4200) { + return "'".pg_escape_string($s)."'"; + } + if ($this->replaceQuote[0] == '\\'){ + $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s); + } + return "'".str_replace("'",$this->replaceQuote,$s)."'"; + } + + // undo magic quotes for " + $s = str_replace('\\"','"',$s); + return "'$s'"; + } + */ + + + /* 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; + + default: + /* handle escape characters... */ + if ($ch == '\\') { + $i++; + $ch = substr($fmt,$i,1); + } + if (strpos('-/.:;, ',$ch) !== false) $s .= $ch; + else $s .= '"'.$ch.'"'; + + } + } + return $s. "')"; + } + + + + /* + * Load a Large Object from a file + * - the procedure stores the object id in the table and imports the object using + * postgres proprietary blob handling routines + * + * contributed by Mattia Rossi mattia@technologist.com + * modified for safe mode by juraj chlebec + */ + function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB') + { + pg_exec ($this->_connectionID, "begin"); + + $fd = fopen($path,'r'); + $contents = fread($fd,filesize($path)); + fclose($fd); + + $oid = pg_lo_create($this->_connectionID); + $handle = pg_lo_open($this->_connectionID, $oid, 'w'); + pg_lo_write($handle, $contents); + pg_lo_close($handle); + + /* $oid = pg_lo_import ($path); */ + pg_exec($this->_connectionID, "commit"); + $rs = ADOConnection::UpdateBlob($table,$column,$oid,$where,$blobtype); + $rez = !empty($rs); + return $rez; + } + + /* + * If an OID is detected, then we use pg_lo_* to open the oid file and read the + * real blob from the db using the oid supplied as a parameter. If you are storing + * blobs using bytea, we autodetect and process it so this function is not needed. + * + * contributed by Mattia Rossi mattia@technologist.com + * + * see http://www.postgresql.org/idocs/index.php?largeobjects.html + */ + function BlobDecode( $blob) + { + if (strlen($blob) > 24) return $blob; + + @pg_exec("begin"); + $fd = @pg_lo_open($blob,"r"); + if ($fd === false) { + @pg_exec("commit"); + return $blob; + } + $realblob = @pg_loreadall($fd); + @pg_loclose($fd); + @pg_exec("commit"); + return $realblob; + } + + /* + See http://www.postgresql.org/idocs/index.php?datatype-binary.html + + NOTE: SQL string literals (input strings) must be preceded with two backslashes + due to the fact that they must pass through two parsers in the PostgreSQL + backend. + */ + function BlobEncode($blob) + { + if (ADODB_PHPVER >= 0x4200) return pg_escape_bytea($blob); + $badch = array(chr(92),chr(0),chr(39)); # \ null ' + $fixch = array('\\\\134','\\\\000','\\\\047'); + return adodb_str_replace($badch,$fixch,$blob); + + /* note that there is a pg_escape_bytea function only for php 4.2.0 or later */ + } + + function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') + { + return $this->Execute("UPDATE $table SET $column=? WHERE $where", + array($this->BlobEncode($val))) != false; + } + + function OffsetDate($dayFraction,$date=false) + { + if (!$date) $date = $this->sysDate; + return "($date+interval'$dayFraction days')"; + } + + + /* converts field names to lowercase */ + function &MetaColumns($table) + { + global $ADODB_FETCH_MODE; + + if (strncmp(PHP_OS,"WIN",3) === 0) $table = strtolower($table); + + if (!empty($this->metaColumnsSQL)) { + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); + $rs = $this->Execute(sprintf($this->metaColumnsSQL,($table))); + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + + if ($rs === false) return false; + + if (!empty($this->metaKeySQL)) { + /* If we want the primary keys, we have to issue a separate query */ + /* Of course, a modified version of the metaColumnsSQL query using a */ + /* LEFT JOIN would have been much more elegant, but postgres does */ + /* not support OUTER JOINS. So here is the clumsy way. */ + + $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; + + $rskey = $this->Execute(sprintf($this->metaKeySQL,($table))); + /* fetch all result in once for performance. */ + $keys =& $rskey->GetArray(); + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + + $rskey->Close(); + unset($rskey); + } + + $rsdefa = array(); + if (!empty($this->metaDefaultsSQL)) { + $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; + $sql = sprintf($this->metaDefaultsSQL, ($table)); + $rsdef = $this->Execute($sql); + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + + if ($rsdef) { + while (!$rsdef->EOF) { + $num = $rsdef->fields['num']; + $s = $rsdef->fields['def']; + if (substr($s, 0, 1) == "'") { /* quoted strings hack... for now... fixme */ + $s = substr($s, 1); + $s = substr($s, 0, strlen($s) - 1); + } + + $rsdefa[$num] = $s; + $rsdef->MoveNext(); + } + } else { + ADOConnection::outp( "==> SQL => " . $sql); + } + unset($rsdef); + } + + $retarr = array(); + while (!$rs->EOF) { + $fld = new ADOFieldObject(); + $fld->name = $rs->fields[0]; + $fld->type = $rs->fields[1]; + $fld->max_length = $rs->fields[2]; + if ($fld->max_length <= 0) $fld->max_length = $rs->fields[3]-4; + if ($fld->max_length <= 0) $fld->max_length = -1; + + /* dannym */ + /* 5 hasdefault; 6 num-of-column */ + $fld->has_default = ($rs->fields[5] == 't'); + if ($fld->has_default) { + $fld->default_value = $rsdefa[$rs->fields[6]]; + } + + /* Freek */ + if ($rs->fields[4] == $this->true) { + $fld->not_null = true; + } + + /* Freek */ + if (is_array($keys)) { + reset ($keys); + while (list($x,$key) = each($keys)) { + if ($fld->name == $key['column_name'] AND $key['primary_key'] == $this->true) + $fld->primary_key = true; + if ($fld->name == $key['column_name'] AND $key['unique_key'] == $this->true) + $fld->unique = true; /* What name is more compatible? */ + } + } + + $retarr[strtoupper($fld->name)] = $fld; + + $rs->MoveNext(); + } + $rs->Close(); + return $retarr; + } + return false; + } + + /* returns true or false */ + /* */ + /* examples: */ + /* $db->Connect("host=host1 user=user1 password=secret port=4341"); */ + /* $db->Connect('host1','user1','secret'); */ + function _connect($str,$user='',$pwd='',$db='',$persist=false) + { + if ($user || $pwd || $db) { + $user = adodb_addslashes($user); + $pwd = adodb_addslashes($pwd); + if (strlen($db) == 0) $db = 'template1'; + $db = adodb_addslashes($db); + if ($str) { + $host = split(":", $str); + if ($host[0]) $str = "host=".adodb_addslashes($host[0]); + else $str = 'host=localhost'; + if (isset($host[1])) $str .= " port=$host[1]"; + } else { + $str = 'host=localhost'; + } + if ($user) $str .= " user=".$user; + if ($pwd) $str .= " password=".$pwd; + if ($db) $str .= " dbname=".$db; + } + + /* if ($user) $linea = "user=$user host=$linea password=$pwd dbname=$db port=5432"; */ + if ($persist) $this->_connectionID = pg_pconnect($str); + else $this->_connectionID = pg_connect($str); + + if ($this->_connectionID === false) return false; + $this->Execute("set datestyle='ISO'"); + return true; + } + + /* returns true or false */ + /* */ + /* examples: */ + /* $db->PConnect("host=host1 user=user1 password=secret port=4341"); */ + /* $db->PConnect('host1','user1','secret'); */ + function _pconnect($str,$user='',$pwd='',$db='') + { + return $this->_connect($str,$user,$pwd,$db,true); + } + + /* returns queryID or false */ + function _query($sql,$inputarr) + { + $rez = pg_Exec($this->_connectionID,$sql); + /* print_r($rez); */ + /* check if no data returned, then no need to create real recordset */ + if ($rez && pg_numfields($rez) <= 0) { + if ($this->_resultid) pg_freeresult($this->_resultid); + $this->_resultid = $rez; + return true; + } + + return $rez; + } + + + /* Returns: the last error message from previous database operation */ + function ErrorMsg() + { + if (ADODB_PHPVER >= 0x4300) { + if (!empty($this->_resultid)) { + $this->_errorMsg = @pg_result_error($this->_resultid); + if ($this->_errorMsg) return $this->_errorMsg; + } + + if (!empty($this->_connectionID)) { + $this->_errorMsg = @pg_last_error($this->_connectionID); + } else $this->_errorMsg = @pg_last_error(); + } else { + if (empty($this->_connectionID)) $this->_errorMsg = @pg_errormessage(); + else $this->_errorMsg = @pg_errormessage($this->_connectionID); + } + return $this->_errorMsg; + } + + function ErrorNo() + { + $e = $this->ErrorMsg(); + return (strlen($e)) ? $e : 0; + } + + /* returns true or false */ + function _close() + { + if ($this->transCnt) $this->RollbackTrans(); + if ($this->_resultid) { + @pg_freeresult($this->_resultid); + $this->_resultid = false; + } + @pg_close($this->_connectionID); + $this->_connectionID = false; + return true; + } + + + /* + * Maximum size of C field + */ + function CharMax() + { + return 1000000000; /* should be 1 Gb? */ + } + + /* + * Maximum size of X field + */ + function TextMax() + { + return 1000000000; /* should be 1 Gb? */ + } + + +} + +/*-------------------------------------------------------------------------------------- + Class Name: Recordset +--------------------------------------------------------------------------------------*/ + +class ADORecordSet_postgres64 extends ADORecordSet{ + var $_blobArr; + var $databaseType = "postgres64"; + var $canSeek = true; + function ADORecordSet_postgres64($queryID,$mode=false) + { + if ($mode === false) { + global $ADODB_FETCH_MODE; + $mode = $ADODB_FETCH_MODE; + } + switch ($mode) + { + case ADODB_FETCH_NUM: $this->fetchMode = PGSQL_NUM; break; + case ADODB_FETCH_ASSOC:$this->fetchMode = PGSQL_ASSOC; break; + default: + case ADODB_FETCH_DEFAULT: + case ADODB_FETCH_BOTH:$this->fetchMode = PGSQL_BOTH; break; + } + $this->ADORecordSet($queryID); + } + + function &GetRowAssoc($upper=true) + { + if ($this->fetchMode == PGSQL_ASSOC && !$upper) return $this->fields; + return ADORecordSet::GetRowAssoc($upper); + } + + function _initrs() + { + global $ADODB_COUNTRECS; + $this->_numOfRows = ($ADODB_COUNTRECS)? @pg_numrows($this->_queryID):-1; + $this->_numOfFields = @pg_numfields($this->_queryID); + + /* cache types for blob decode check */ + for ($i=0, $max = $this->_numOfFields; $i < $max; $i++) { + $f1 = $this->FetchField($i); + if ($f1->type == 'bytea') $this->_blobArr[$i] = $f1->name; + } + } + + /* Use associative array to get fields array */ + function Fields($colname) + { + if ($this->fetchMode != PGSQL_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)]]; + } + + function &FetchField($fieldOffset = 0) + { + $off=$fieldOffset; /* offsets begin at 0 */ + + $o= new ADOFieldObject(); + $o->name = @pg_fieldname($this->_queryID,$off); + $o->type = @pg_fieldtype($this->_queryID,$off); + $o->max_length = @pg_fieldsize($this->_queryID,$off); + /* print_r($o); */ + /* print "off=$off name=$o->name type=$o->type len=$o->max_length
"; */ + return $o; + } + + function _seek($row) + { + return @pg_fetch_row($this->_queryID,$row); + } + + function _decode($blob) + { + eval('$realblob="'.adodb_str_replace(array('"','$'),array('\"','\$'),$blob).'";'); + return $realblob; + } + + function _fixblobs() + { + if ($this->fetchMode == PGSQL_NUM || $this->fetchMode == PGSQL_BOTH) { + foreach($this->_blobArr as $k => $v) { + $this->fields[$k] = ADORecordSet_postgres64::_decode($this->fields[$k]); + } + } + if ($this->fetchMode == PGSQL_ASSOC || $this->fetchMode == PGSQL_BOTH) { + foreach($this->_blobArr as $k => $v) { + $this->fields[$v] = ADORecordSet_postgres64::_decode($this->fields[$v]); + } + } + } + + /* 10% speedup to move MoveNext to child class */ + function MoveNext() + { + if (!$this->EOF) { + $this->_currentRow++; + if ($this->_numOfRows < 0 || $this->_numOfRows > $this->_currentRow) { + $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode); + + if (is_array($this->fields)) { + if (isset($this->_blobArr)) $this->_fixblobs(); + return true; + } + } + $this->fields = false; + $this->EOF = true; + } + return false; + } + + function _fetch() + { + if ($this->_currentRow >= $this->_numOfRows && $this->_numOfRows >= 0) + return false; + + $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode); + if (isset($this->_blobArr)) $this->_fixblobs(); + + return (is_array($this->fields)); + } + + function _close() + { + return @pg_freeresult($this->_queryID); + } + + function MetaType($t,$len=-1,$fieldobj=false) + { + if (is_object($t)) { + $fieldobj = $t; + $t = $fieldobj->type; + $len = $fieldobj->max_length; + } + switch (strtoupper($t)) { + case 'INTERVAL': + case 'CHAR': + case 'CHARACTER': + case 'VARCHAR': + case 'NAME': + case 'BPCHAR': + if ($len <= $this->blobSize) return 'C'; + + case 'TEXT': + return 'X'; + + case 'IMAGE': /* user defined type */ + case 'BLOB': /* user defined type */ + case 'BIT': /* This is a bit string, not a single bit, so don't return 'L' */ + case 'VARBIT': + case 'BYTEA': + return 'B'; + + case 'BOOL': + case 'BOOLEAN': + return 'L'; + + case 'DATE': + return 'D'; + + case 'TIME': + case 'DATETIME': + case 'TIMESTAMP': + case 'TIMESTAMPTZ': + return 'T'; + + case 'SMALLINT': + case 'BIGINT': + case 'INTEGER': + case 'INT8': + case 'INT4': + case 'INT2': + if (isset($fieldobj) && + empty($fieldobj->primary_key) && empty($fieldobj->unique)) return 'I'; + + case 'OID': + case 'SERIAL': + return 'R'; + + default: + return 'N'; + } + } + +} +?> diff --git a/lib/adodb/drivers/adodb-postgres7.inc.php b/lib/adodb/drivers/adodb-postgres7.inc.php index 28e083597a..c79d76062b 100644 --- a/lib/adodb/drivers/adodb-postgres7.inc.php +++ b/lib/adodb/drivers/adodb-postgres7.inc.php @@ -1,74 +1,74 @@ -ADODB_postgres64(); - } - - // the following should be compat with postgresql 7.2, - // which makes obsolete the LIMIT limit,offset syntax - function &SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$arg3=false,$secs2cache=0) - { - $offsetStr = ($offset >= 0) ? " OFFSET $offset" : ''; - $limitStr = ($nrows >= 0) ? " LIMIT $nrows" : ''; - return $secs2cache ? - $this->CacheExecute($secs2cache,$sql."$limitStr$offsetStr",$inputarr,$arg3) - : - $this->Execute($sql."$limitStr$offsetStr",$inputarr,$arg3); - } - - -} - -/*-------------------------------------------------------------------------------------- - Class Name: Recordset ---------------------------------------------------------------------------------------*/ - -class ADORecordSet_postgres7 extends ADORecordSet_postgres64{ - - var $databaseType = "postgres7"; - - function ADORecordSet_postgres7($queryID,$mode=false) - { - $this->ADORecordSet_postgres64($queryID,$mode); - } - - // 10% speedup to move MoveNext to child class - function MoveNext() - { - if (!$this->EOF) { - $this->_currentRow++; - if ($this->_numOfRows < 0 || $this->_numOfRows > $this->_currentRow) { - $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode); - - if (is_array($this->fields)) { - if (isset($this->_blobArr)) $this->_fixblobs(); - return true; - } - } - $this->fields = false; - $this->EOF = true; - } - return false; - } - -} +ADODB_postgres64(); + } + + /* the following should be compat with postgresql 7.2, */ + /* which makes obsolete the LIMIT limit,offset syntax */ + function &SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$arg3=false,$secs2cache=0) + { + $offsetStr = ($offset >= 0) ? " OFFSET $offset" : ''; + $limitStr = ($nrows >= 0) ? " LIMIT $nrows" : ''; + return $secs2cache ? + $this->CacheExecute($secs2cache,$sql."$limitStr$offsetStr",$inputarr,$arg3) + : + $this->Execute($sql."$limitStr$offsetStr",$inputarr,$arg3); + } + + +} + +/*-------------------------------------------------------------------------------------- + Class Name: Recordset +--------------------------------------------------------------------------------------*/ + +class ADORecordSet_postgres7 extends ADORecordSet_postgres64{ + + var $databaseType = "postgres7"; + + function ADORecordSet_postgres7($queryID,$mode=false) + { + $this->ADORecordSet_postgres64($queryID,$mode); + } + + /* 10% speedup to move MoveNext to child class */ + function MoveNext() + { + if (!$this->EOF) { + $this->_currentRow++; + if ($this->_numOfRows < 0 || $this->_numOfRows > $this->_currentRow) { + $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode); + + if (is_array($this->fields)) { + if (isset($this->_blobArr)) $this->_fixblobs(); + return true; + } + } + $this->fields = false; + $this->EOF = true; + } + return false; + } + +} ?> \ No newline at end of file diff --git a/lib/adodb/drivers/adodb-proxy.inc.php b/lib/adodb/drivers/adodb-proxy.inc.php index 9b69ef338b..8237fdb5dd 100644 --- a/lib/adodb/drivers/adodb-proxy.inc.php +++ b/lib/adodb/drivers/adodb-proxy.inc.php @@ -1,30 +1,30 @@ -ADORecordset($id,$mode); - } - }; -} // define - +ADORecordset($id,$mode); + } + }; +} /* define */ + ?> \ No newline at end of file diff --git a/lib/adodb/drivers/adodb-sqlanywhere.inc.php b/lib/adodb/drivers/adodb-sqlanywhere.inc.php index af44f5f330..85f14e2e17 100644 --- a/lib/adodb/drivers/adodb-sqlanywhere.inc.php +++ b/lib/adodb/drivers/adodb-sqlanywhere.inc.php @@ -1,166 +1,166 @@ -create_blobvar($blobVarName); - - b) load blob var from file. $filename must be complete path - - $dbcon->load_blobvar_from_file($blobVarName, $filename); - - c) Use the $blobVarName in SQL insert or update statement in the values - clause: - - $recordSet = $dbconn->Execute('INSERT INTO tabname (idcol, blobcol) ' - . - 'VALUES (\'test\', ' . $blobVarName . ')'); - - instead of loading blob from a file, you can also load from - an unformatted (raw) blob variable: - $dbcon->load_blobvar_from_var($blobVarName, $varName); - - d) drop blob variable on db server to free up resources: - $dbconn->drop_blobvar($blobVarName); - - Sybase_SQLAnywhere data driver. Requires ODBC. - -*/ - -if (!defined('_ADODB_ODBC_LAYER')) { - include(ADODB_DIR."/drivers/adodb-odbc.inc.php"); -} - -if (!defined('ADODB_SYBASE_SQLANYWHERE')){ - - define('ADODB_SYBASE_SQLANYWHERE',1); - - class ADODB_sqlanywhere extends ADODB_odbc { - var $databaseType = "sqlanywhere"; - var $hasInsertID = true; - - function ADODB_sqlanywhere() - { - $this->ADODB_odbc(); - } - - function _insertid() { - return $this->GetOne('select @@identity'); - } - - function create_blobvar($blobVarName) { - $this->Execute("create variable $blobVarName long binary"); - return; - } - - function drop_blobvar($blobVarName) { - $this->Execute("drop variable $blobVarName"); - return; - } - - function load_blobvar_from_file($blobVarName, $filename) { - $chunk_size = 1000; - - $fd = fopen ($filename, "rb"); - - $integer_chunks = (integer)filesize($filename) / $chunk_size; - $modulus = filesize($filename) % $chunk_size; - if ($modulus != 0){ - $integer_chunks += 1; - } - - for($loop=1;$loop<=$integer_chunks;$loop++){ - $contents = fread ($fd, $chunk_size); - $contents = bin2hex($contents); - - $hexstring = ''; - - for($loop2=0;$loop2qstr($hexstring); - - $this->Execute("set $blobVarName = $blobVarName || " . $hexstring); - } - - fclose ($fd); - return; - } - - function load_blobvar_from_var($blobVarName, &$varName) { - $chunk_size = 1000; - - $integer_chunks = (integer)strlen($varName) / $chunk_size; - $modulus = strlen($varName) % $chunk_size; - if ($modulus != 0){ - $integer_chunks += 1; - } - - for($loop=1;$loop<=$integer_chunks;$loop++){ - $contents = substr ($varName, (($loop - 1) * $chunk_size), $chunk_size); - $contents = bin2hex($contents); - - $hexstring = ''; - - for($loop2=0;$loop2 qstr($hexstring); - - $this->Execute("set $blobVarName = $blobVarName || " . $hexstring); - } - - return; - } - - /* - Insert a null into the blob field of the table first. - Then use UpdateBlob to store the blob. - - Usage: - - $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); - $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1'); - */ - function UpdateBlob($table,$column,&$val,$where,$blobtype='BLOB') - { - $blobVarName = 'hold_blob'; - $this->create_blobvar($blobVarName); - $this->load_blobvar_from_var($blobVarName, $val); - $this->Execute("UPDATE $table SET $column=$blobVarName WHERE $where"); - $this->drop_blobvar($blobVarName); - return true; - } - }; //class - - class ADORecordSet_sqlanywhere extends ADORecordSet_odbc { - - var $databaseType = "sqlanywhere"; - - function ADORecordSet_sqlanywhere($id,$mode=false) - { - $this->ADORecordSet_odbc($id,$mode); - } - - - }; //class - - -} //define -?> +create_blobvar($blobVarName); + + b) load blob var from file. $filename must be complete path + + $dbcon->load_blobvar_from_file($blobVarName, $filename); + + c) Use the $blobVarName in SQL insert or update statement in the values + clause: + + $recordSet = $dbconn->Execute('INSERT INTO tabname (idcol, blobcol) ' + . + 'VALUES (\'test\', ' . $blobVarName . ')'); + + instead of loading blob from a file, you can also load from + an unformatted (raw) blob variable: + $dbcon->load_blobvar_from_var($blobVarName, $varName); + + d) drop blob variable on db server to free up resources: + $dbconn->drop_blobvar($blobVarName); + + Sybase_SQLAnywhere data driver. Requires ODBC. + +*/ + +if (!defined('_ADODB_ODBC_LAYER')) { + include(ADODB_DIR."/drivers/adodb-odbc.inc.php"); +} + +if (!defined('ADODB_SYBASE_SQLANYWHERE')){ + + define('ADODB_SYBASE_SQLANYWHERE',1); + + class ADODB_sqlanywhere extends ADODB_odbc { + var $databaseType = "sqlanywhere"; + var $hasInsertID = true; + + function ADODB_sqlanywhere() + { + $this->ADODB_odbc(); + } + + function _insertid() { + return $this->GetOne('select @@identity'); + } + + function create_blobvar($blobVarName) { + $this->Execute("create variable $blobVarName long binary"); + return; + } + + function drop_blobvar($blobVarName) { + $this->Execute("drop variable $blobVarName"); + return; + } + + function load_blobvar_from_file($blobVarName, $filename) { + $chunk_size = 1000; + + $fd = fopen ($filename, "rb"); + + $integer_chunks = (integer)filesize($filename) / $chunk_size; + $modulus = filesize($filename) % $chunk_size; + if ($modulus != 0){ + $integer_chunks += 1; + } + + for($loop=1;$loop<=$integer_chunks;$loop++){ + $contents = fread ($fd, $chunk_size); + $contents = bin2hex($contents); + + $hexstring = ''; + + for($loop2=0;$loop2 qstr($hexstring); + + $this->Execute("set $blobVarName = $blobVarName || " . $hexstring); + } + + fclose ($fd); + return; + } + + function load_blobvar_from_var($blobVarName, &$varName) { + $chunk_size = 1000; + + $integer_chunks = (integer)strlen($varName) / $chunk_size; + $modulus = strlen($varName) % $chunk_size; + if ($modulus != 0){ + $integer_chunks += 1; + } + + for($loop=1;$loop<=$integer_chunks;$loop++){ + $contents = substr ($varName, (($loop - 1) * $chunk_size), $chunk_size); + $contents = bin2hex($contents); + + $hexstring = ''; + + for($loop2=0;$loop2 qstr($hexstring); + + $this->Execute("set $blobVarName = $blobVarName || " . $hexstring); + } + + return; + } + + /* + Insert a null into the blob field of the table first. + Then use UpdateBlob to store the blob. + + Usage: + + $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); + $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1'); + */ + function UpdateBlob($table,$column,&$val,$where,$blobtype='BLOB') + { + $blobVarName = 'hold_blob'; + $this->create_blobvar($blobVarName); + $this->load_blobvar_from_var($blobVarName, $val); + $this->Execute("UPDATE $table SET $column=$blobVarName WHERE $where"); + $this->drop_blobvar($blobVarName); + return true; + } + }; /* class */ + + class ADORecordSet_sqlanywhere extends ADORecordSet_odbc { + + var $databaseType = "sqlanywhere"; + + function ADORecordSet_sqlanywhere($id,$mode=false) + { + $this->ADORecordSet_odbc($id,$mode); + } + + + }; /* class */ + + +} /* define */ +?> diff --git a/lib/adodb/drivers/adodb-sybase.inc.php b/lib/adodb/drivers/adodb-sybase.inc.php index e76fd6e242..2d6ef059f3 100644 --- a/lib/adodb/drivers/adodb-sybase.inc.php +++ b/lib/adodb/drivers/adodb-sybase.inc.php @@ -1,316 +1,316 @@ -GetOne('select @@identity'); - } - // might require begintrans -- committrans - function _affectedrows() - { - return $this->GetOne('select @@rowcount'); - } - - - function BeginTrans() - { - - if ($this->transOff) return true; - $this->transCnt += 1; - - $this->Execute('BEGIN TRAN'); - return true; - } - - function CommitTrans($ok=true) - { - if ($this->transOff) return true; - - if (!$ok) return $this->RollbackTrans(); - - $this->transCnt -= 1; - $this->Execute('COMMIT TRAN'); - return true; - } - - function RollbackTrans() - { - if ($this->transOff) return true; - $this->transCnt -= 1; - $this->Execute('ROLLBACK TRAN'); - return true; - } - - // http://www.isug.com/Sybase_FAQ/ASE/section6.1.html#6.1.4 - function RowLock($tables,$where) - { - if (!$this->_hastrans) $this->BeginTrans(); - $tables = str_replace(',',' HOLDLOCK,',$tables); - return $this->GetOne("select top 1 null as ignore from $tables HOLDLOCK where $where"); - - } - - function SelectDB($dbName) { - $this->databaseName = $dbName; - if ($this->_connectionID) { - return @sybase_select_db($dbName); - } - else return false; - } - - /* Returns: the last error message from previous database operation - Note: This function is NOT available for Microsoft SQL Server. */ - - function ErrorMsg() { - $this->_errorMsg = sybase_get_last_message(); - return $this->_errorMsg; - } - - // returns true or false - function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) - { - $this->_connectionID = sybase_connect($argHostname,$argUsername,$argPassword); - if ($this->_connectionID === false) return false; - if ($argDatabasename) return $this->SelectDB($argDatabasename); - return true; - } - // returns true or false - function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) - { - $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) - { - global $ADODB_COUNTRECS; - - if ($ADODB_COUNTRECS == false && ADODB_PHPVER >= 0x4300) - return sybase_unbuffered_query($sql,$this->_connectionID); - else - return sybase_query($sql,$this->_connectionID); - } - - // See http://www.isug.com/Sybase_FAQ/ASE/section6.2.html#6.2.12 - function &SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$arg3=false,$secs2cache=0) - { - if ($secs2cache > 0) // we do not cache rowcount, so we have to load entire recordset - return ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$arg3,$secs2cache); - - $cnt = ($nrows > 0) ? $nrows : 0; - if ($offset > 0 && $cnt) $cnt += $offset; - - $this->Execute("set rowcount $cnt"); - $rs = &ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$arg3,$secs2cache); - $this->Execute("set rowcount 0"); - - return $rs; - } - - // returns true or false - function _close() - { - return @sybase_close($this->_connectionID); - } - - function UnixDate($v) - { - return ADORecordSet_array_sybase::UnixDate($v); - } - - function UnixTimeStamp($v) - { - return ADORecordSet_array_sybase::UnixTimeStamp($v); - } -} - -/*-------------------------------------------------------------------------------------- - Class Name: Recordset ---------------------------------------------------------------------------------------*/ -global $ADODB_sybase_mths; -$ADODB_sybase_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); - -class ADORecordset_sybase extends ADORecordSet { - - var $databaseType = "sybase"; - var $canSeek = true; - // _mths works only in non-localised system - var $_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); - - function ADORecordset_sybase($id,$mode=false) - { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - if (!$mode) $this->fetchMode = ADODB_FETCH_ASSOC; - else $this->fetchMode = $mode; - return $this->ADORecordSet($id,$mode); - } - - /* 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 ($fieldOffset != -1) { - $o = @sybase_fetch_field($this->_queryID, $fieldOffset); - } - else if ($fieldOffset == -1) { /* The $fieldOffset argument is not provided thus its -1 */ - $o = @sybase_fetch_field($this->_queryID); - } - // older versions of PHP did not support type, only numeric - if ($o && !isset($o->type)) $o->type = ($o->numeric) ? 'float' : 'varchar'; - return $o; - } - - function _initrs() - { - global $ADODB_COUNTRECS; - $this->_numOfRows = ($ADODB_COUNTRECS)? @sybase_num_rows($this->_queryID):-1; - $this->_numOfFields = @sybase_num_fields($this->_queryID); - } - - function _seek($row) - { - return @sybase_data_seek($this->_queryID, $row); - } - - function _fetch($ignore_fields=false) - { - if ($this->fetchMode == ADODB_FETCH_NUM) { - $this->fields = @sybase_fetch_row($this->_queryID); - } else if ($this->fetchMode == ADODB_FETCH_ASSOC) { - $this->fields = @sybase_fetch_row($this->_queryID); - if (is_array($this->fields)) { - $this->fields = $this->GetRowAssoc(ADODB_CASE_ASSOC); - return true; - } - return false; - } else { - $this->fields = @sybase_fetch_array($this->_queryID); - } - if ( is_array($this->fields)) { - return true; - } - - return false; - } - - /* 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() { - return @sybase_free_result($this->_queryID); - } - - // sybase/mssql uses a default date like Dec 30 2000 12:00AM - function UnixDate($v) - { - return ADORecordSet_array_sybase::UnixDate($v); - } - - function UnixTimeStamp($v) - { - return ADORecordSet_array_sybase::UnixTimeStamp($v); - } -} - -class ADORecordSet_array_sybase extends ADORecordSet_array { - function ADORecordSet_array_sybase($id=-1) - { - $this->ADORecordSet_array($id); - } - - // sybase/mssql uses a default date like Dec 30 2000 12:00AM - 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})" - ,$v, $rr)) return parent::UnixDate($v); - - if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0; - - $themth = substr(strtoupper($rr[1]),0,3); - $themth = $ADODB_sybase_mths[$themth]; - if ($themth <= 0) return false; - // h-m-s-MM-DD-YY - return mktime(0,0,0,$themth,$rr[2],$rr[3]); - } - - 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})" - ,$v, $rr)) return parent::UnixTimeStamp($v); - if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0; - - $themth = substr(strtoupper($rr[1]),0,3); - $themth = $ADODB_sybase_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,$rr[2],$rr[3]); - } -} -?> +GetOne('select @@identity'); + } + /* might require begintrans -- committrans */ + function _affectedrows() + { + return $this->GetOne('select @@rowcount'); + } + + + function BeginTrans() + { + + if ($this->transOff) return true; + $this->transCnt += 1; + + $this->Execute('BEGIN TRAN'); + return true; + } + + function CommitTrans($ok=true) + { + if ($this->transOff) return true; + + if (!$ok) return $this->RollbackTrans(); + + $this->transCnt -= 1; + $this->Execute('COMMIT TRAN'); + return true; + } + + function RollbackTrans() + { + if ($this->transOff) return true; + $this->transCnt -= 1; + $this->Execute('ROLLBACK TRAN'); + return true; + } + + /* http://www.isug.com/Sybase_FAQ/ASE/section6.1.html#6.1.4 */ + function RowLock($tables,$where) + { + if (!$this->_hastrans) $this->BeginTrans(); + $tables = str_replace(',',' HOLDLOCK,',$tables); + return $this->GetOne("select top 1 null as ignore from $tables HOLDLOCK where $where"); + + } + + function SelectDB($dbName) { + $this->databaseName = $dbName; + if ($this->_connectionID) { + return @sybase_select_db($dbName); + } + else return false; + } + + /* Returns: the last error message from previous database operation + Note: This function is NOT available for Microsoft SQL Server. */ + + function ErrorMsg() { + $this->_errorMsg = sybase_get_last_message(); + return $this->_errorMsg; + } + + /* returns true or false */ + function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + $this->_connectionID = sybase_connect($argHostname,$argUsername,$argPassword); + if ($this->_connectionID === false) return false; + if ($argDatabasename) return $this->SelectDB($argDatabasename); + return true; + } + /* returns true or false */ + function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) + { + $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) + { + global $ADODB_COUNTRECS; + + if ($ADODB_COUNTRECS == false && ADODB_PHPVER >= 0x4300) + return sybase_unbuffered_query($sql,$this->_connectionID); + else + return sybase_query($sql,$this->_connectionID); + } + + /* See http://www.isug.com/Sybase_FAQ/ASE/section6.2.html#6.2.12 */ + function &SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$arg3=false,$secs2cache=0) + { + if ($secs2cache > 0) /* we do not cache rowcount, so we have to load entire recordset */ + return ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$arg3,$secs2cache); + + $cnt = ($nrows > 0) ? $nrows : 0; + if ($offset > 0 && $cnt) $cnt += $offset; + + $this->Execute("set rowcount $cnt"); + $rs = &ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$arg3,$secs2cache); + $this->Execute("set rowcount 0"); + + return $rs; + } + + /* returns true or false */ + function _close() + { + return @sybase_close($this->_connectionID); + } + + function UnixDate($v) + { + return ADORecordSet_array_sybase::UnixDate($v); + } + + function UnixTimeStamp($v) + { + return ADORecordSet_array_sybase::UnixTimeStamp($v); + } +} + +/*-------------------------------------------------------------------------------------- + Class Name: Recordset +--------------------------------------------------------------------------------------*/ +global $ADODB_sybase_mths; +$ADODB_sybase_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); + +class ADORecordset_sybase extends ADORecordSet { + + var $databaseType = "sybase"; + var $canSeek = true; + /* _mths works only in non-localised system */ + var $_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); + + function ADORecordset_sybase($id,$mode=false) + { + if ($mode === false) { + global $ADODB_FETCH_MODE; + $mode = $ADODB_FETCH_MODE; + } + if (!$mode) $this->fetchMode = ADODB_FETCH_ASSOC; + else $this->fetchMode = $mode; + return $this->ADORecordSet($id,$mode); + } + + /* 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 ($fieldOffset != -1) { + $o = @sybase_fetch_field($this->_queryID, $fieldOffset); + } + else if ($fieldOffset == -1) { /* The $fieldOffset argument is not provided thus its -1 */ + $o = @sybase_fetch_field($this->_queryID); + } + /* older versions of PHP did not support type, only numeric */ + if ($o && !isset($o->type)) $o->type = ($o->numeric) ? 'float' : 'varchar'; + return $o; + } + + function _initrs() + { + global $ADODB_COUNTRECS; + $this->_numOfRows = ($ADODB_COUNTRECS)? @sybase_num_rows($this->_queryID):-1; + $this->_numOfFields = @sybase_num_fields($this->_queryID); + } + + function _seek($row) + { + return @sybase_data_seek($this->_queryID, $row); + } + + function _fetch($ignore_fields=false) + { + if ($this->fetchMode == ADODB_FETCH_NUM) { + $this->fields = @sybase_fetch_row($this->_queryID); + } else if ($this->fetchMode == ADODB_FETCH_ASSOC) { + $this->fields = @sybase_fetch_row($this->_queryID); + if (is_array($this->fields)) { + $this->fields = $this->GetRowAssoc(ADODB_CASE_ASSOC); + return true; + } + return false; + } else { + $this->fields = @sybase_fetch_array($this->_queryID); + } + if ( is_array($this->fields)) { + return true; + } + + return false; + } + + /* 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() { + return @sybase_free_result($this->_queryID); + } + + /* sybase/mssql uses a default date like Dec 30 2000 12:00AM */ + function UnixDate($v) + { + return ADORecordSet_array_sybase::UnixDate($v); + } + + function UnixTimeStamp($v) + { + return ADORecordSet_array_sybase::UnixTimeStamp($v); + } +} + +class ADORecordSet_array_sybase extends ADORecordSet_array { + function ADORecordSet_array_sybase($id=-1) + { + $this->ADORecordSet_array($id); + } + + /* sybase/mssql uses a default date like Dec 30 2000 12:00AM */ + 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})" + ,$v, $rr)) return parent::UnixDate($v); + + if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0; + + $themth = substr(strtoupper($rr[1]),0,3); + $themth = $ADODB_sybase_mths[$themth]; + if ($themth <= 0) return false; + /* h-m-s-MM-DD-YY */ + return mktime(0,0,0,$themth,$rr[2],$rr[3]); + } + + 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})" + ,$v, $rr)) return parent::UnixTimeStamp($v); + if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0; + + $themth = substr(strtoupper($rr[1]),0,3); + $themth = $ADODB_sybase_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,$rr[2],$rr[3]); + } +} +?> diff --git a/lib/adodb/drivers/adodb-vfp.inc.php b/lib/adodb/drivers/adodb-vfp.inc.php index 548a27f033..4e7a0ec0be 100644 --- a/lib/adodb/drivers/adodb-vfp.inc.php +++ b/lib/adodb/drivers/adodb-vfp.inc.php @@ -1,98 +1,98 @@ -ADODB_odbc(); - } - - function BeginTrans() { return false;} - - // quote string to be sent back to database - function qstr($s,$nofixquotes=false) - { - if (!$nofixquotes) return "'".str_replace("\r\n","'+chr(13)+'",str_replace("'",$this->replaceQuote,$s))."'"; - return "'".$s."'"; - } - - - // TOP requires ORDER BY for VFP - function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$arg3=false,$secs2cache=0) - { - if (!preg_match('/ORDER[ \t\r\n]+BY/i',$sql)) $sql .= ' ORDER BY 1'; - return ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$arg3,$secs2cache); - } - - -}; - - -class ADORecordSet_vfp extends ADORecordSet_odbc { - - var $databaseType = "vfp"; - - - function ADORecordSet_vfp($id,$mode=false) - { - return $this->ADORecordSet_odbc($id,$mode); - } - - function MetaType($t,$len=-1) - { - if (is_object($t)) { - $fieldobj = $t; - $t = $fieldobj->type; - $len = $fieldobj->max_length; - } - switch (strtoupper($t)) { - case 'C': - if ($len <= $this->blobSize) return 'C'; - case 'M': - return 'X'; - - case 'D': return 'D'; - - case 'T': return 'T'; - - case 'L': return 'L'; - - case 'I': return 'I'; - - default: return 'N'; - } - } -} - -} //define +ADODB_odbc(); + } + + function BeginTrans() { return false;} + + /* quote string to be sent back to database */ + function qstr($s,$nofixquotes=false) + { + if (!$nofixquotes) return "'".str_replace("\r\n","'+chr(13)+'",str_replace("'",$this->replaceQuote,$s))."'"; + return "'".$s."'"; + } + + + /* TOP requires ORDER BY for VFP */ + function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$arg3=false,$secs2cache=0) + { + if (!preg_match('/ORDER[ \t\r\n]+BY/is',$sql)) $sql .= ' ORDER BY 1'; + return ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$arg3,$secs2cache); + } + + +}; + + +class ADORecordSet_vfp extends ADORecordSet_odbc { + + var $databaseType = "vfp"; + + + function ADORecordSet_vfp($id,$mode=false) + { + return $this->ADORecordSet_odbc($id,$mode); + } + + function MetaType($t,$len=-1) + { + if (is_object($t)) { + $fieldobj = $t; + $t = $fieldobj->type; + $len = $fieldobj->max_length; + } + switch (strtoupper($t)) { + case 'C': + if ($len <= $this->blobSize) return 'C'; + case 'M': + return 'X'; + + case 'D': return 'D'; + + case 'T': return 'T'; + + case 'L': return 'L'; + + case 'I': return 'I'; + + default: return 'N'; + } + } +} + +} /* define */ ?> \ No newline at end of file diff --git a/lib/adodb/lang/adodb-en.inc.php b/lib/adodb/lang/adodb-en.inc.php new file mode 100644 index 0000000000..0cfcb4f45b --- /dev/null +++ b/lib/adodb/lang/adodb-en.inc.php @@ -0,0 +1,34 @@ + 'en', + DB_ERROR => 'unknown error', + DB_ERROR_ALREADY_EXISTS => 'already exists', + DB_ERROR_CANNOT_CREATE => 'can not create', + DB_ERROR_CANNOT_DELETE => 'can not delete', + DB_ERROR_CANNOT_DROP => 'can not drop', + DB_ERROR_CONSTRAINT => 'constraint violation', + DB_ERROR_DIVZERO => 'division by zero', + DB_ERROR_INVALID => 'invalid', + DB_ERROR_INVALID_DATE => 'invalid date or time', + DB_ERROR_INVALID_NUMBER => 'invalid number', + DB_ERROR_MISMATCH => 'mismatch', + DB_ERROR_NODBSELECTED => 'no database selected', + DB_ERROR_NOSUCHFIELD => 'no such field', + DB_ERROR_NOSUCHTABLE => 'no such table', + DB_ERROR_NOT_CAPABLE => 'DB backend not capable', + DB_ERROR_NOT_FOUND => 'not found', + DB_ERROR_NOT_LOCKED => 'not locked', + DB_ERROR_SYNTAX => 'syntax error', + DB_ERROR_UNSUPPORTED => 'not supported', + DB_ERROR_VALUE_COUNT_ON_ROW => 'value count on row', + DB_ERROR_INVALID_DSN => 'invalid DSN', + DB_ERROR_CONNECT_FAILED => 'connect failed', + 0 => 'no error', /* DB_OK */ + DB_ERROR_NEED_MORE_DATA => 'insufficient data supplied', + DB_ERROR_EXTENSION_NOT_FOUND=> 'extension not found', + DB_ERROR_NOSUCHDB => 'no such database', + DB_ERROR_ACCESS_VIOLATION => 'insufficient permissions' +); +?> + \ No newline at end of file diff --git a/lib/adodb/lang/adodb-fr.inc.php b/lib/adodb/lang/adodb-fr.inc.php new file mode 100644 index 0000000000..a3890d07c7 --- /dev/null +++ b/lib/adodb/lang/adodb-fr.inc.php @@ -0,0 +1,33 @@ + 'fr', + DB_ERROR => 'erreur inconnue', + DB_ERROR_ALREADY_EXISTS => 'existe déjà', + DB_ERROR_CANNOT_CREATE => 'crétion impossible', + DB_ERROR_CANNOT_DELETE => 'effacement impossible', + DB_ERROR_CANNOT_DROP => 'suppression impossible', + DB_ERROR_CONSTRAINT => 'violation de contrainte', + DB_ERROR_DIVZERO => 'division par zéro', + DB_ERROR_INVALID => 'invalide', + DB_ERROR_INVALID_DATE => 'date ou heure invalide', + DB_ERROR_INVALID_NUMBER => 'nombre invalide', + DB_ERROR_MISMATCH => 'erreur de concordance', + DB_ERROR_NODBSELECTED => 'pas de base de donnéessélectionnée', + DB_ERROR_NOSUCHFIELD => 'nom de colonne invalide', + DB_ERROR_NOSUCHTABLE => 'table ou vue inexistante', + DB_ERROR_NOT_CAPABLE => 'fonction optionnelle non installée', + DB_ERROR_NOT_FOUND => 'pas trouvé', + DB_ERROR_NOT_LOCKED => 'non verrouillé', + DB_ERROR_SYNTAX => 'erreur de syntaxe', + DB_ERROR_UNSUPPORTED => 'non supporté', + DB_ERROR_VALUE_COUNT_ON_ROW => 'valeur insérée trop grande pour colonne', + DB_ERROR_INVALID_DSN => 'DSN invalide', + DB_ERROR_CONNECT_FAILED => 'échec à la connexion', + 0 => "pas d'erreur", /* DB_OK */ + DB_ERROR_NEED_MORE_DATA => 'données fournies insuffisantes', + DB_ERROR_EXTENSION_NOT_FOUND=> 'extension non trouvée', + DB_ERROR_NOSUCHDB => 'base de données inconnue', + DB_ERROR_ACCESS_VIOLATION => 'droits ynsuffisants' +); +?> \ No newline at end of file diff --git a/lib/adodb/pivottable.inc.php b/lib/adodb/pivottable.inc.php index 2b449ab3f0..53230cabac 100644 --- a/lib/adodb/pivottable.inc.php +++ b/lib/adodb/pivottable.inc.php @@ -1,163 +1,163 @@ -GetCol("select distinct $colfield from $tables $where order by 1"); - if (!$aggfield) $hidecnt = false; - - $sel = "$rowfields, "; - if (is_array($colfield)) { - foreach ($colfield as $k => $v) { - if (!$hidecnt) $sel .= "\n\t$aggfn(CASE WHEN $v THEN 1 ELSE 0 END) AS \"$k\", "; - if ($aggfield) - $sel .= "\n\t$aggfn(CASE WHEN $v THEN $aggfield ELSE 0 END) AS \"$sumlabel$k\", "; - } - } else { - foreach ($colarr as $v) { - if (!is_numeric($v)) $vq = $db->qstr($v); - else $vq = $v; - if (strlen($v) == 0 ) $v = 'null'; - if (!$hidecnt) $sel .= "\n\t$aggfn(CASE WHEN $colfield=$vq THEN 1 ELSE 0 END) AS \"$v\", "; - if ($aggfield) { - if ($hidecnt) $label = $v; - else $label = "{$v}_$aggfield"; - $sel .= "\n\t$aggfn(CASE WHEN $colfield=$vq THEN $aggfield ELSE 0 END) AS \"$label\", "; - } - } - } - if ($aggfield && $aggfield != '1'){ - $agg = "$aggfn($aggfield)"; - $sel .= "\n\t$agg as \"$sumlabel$aggfield\", "; - } - - if ($showcount) - $sel .= "\n\tSUM(1) as Total"; - - - $sql = "SELECT $sel \nFROM $tables $where \nGROUP BY $rowfields"; - return $sql; - } - -/* EXAMPLES USING MS NORTHWIND DATABASE */ -if (0) { - -# example1 -# -# Query the main "product" table -# Set the rows to CompanyName and QuantityPerUnit -# and the columns to the Categories -# and define the joins to link to lookup tables -# "categories" and "suppliers" -# - - $sql = PivotTableSQL( - $gDB, # adodb connection - 'products p ,categories c ,suppliers s', # tables - 'CompanyName,QuantityPerUnit', # row fields - 'CategoryName', # column fields - 'p.CategoryID = c.CategoryID and s.SupplierID= p.SupplierID' # joins/where -); - print " $sql"; - $rs = $gDB->Execute($sql); - rs2html($rs); - -/* -Generated SQL: - -SELECT CompanyName,QuantityPerUnit, - SUM(CASE WHEN CategoryName='Beverages' THEN 1 ELSE 0 END) AS "Beverages", - SUM(CASE WHEN CategoryName='Condiments' THEN 1 ELSE 0 END) AS "Condiments", - SUM(CASE WHEN CategoryName='Confections' THEN 1 ELSE 0 END) AS "Confections", - SUM(CASE WHEN CategoryName='Dairy Products' THEN 1 ELSE 0 END) AS "Dairy Products", - SUM(CASE WHEN CategoryName='Grains/Cereals' THEN 1 ELSE 0 END) AS "Grains/Cereals", - SUM(CASE WHEN CategoryName='Meat/Poultry' THEN 1 ELSE 0 END) AS "Meat/Poultry", - SUM(CASE WHEN CategoryName='Produce' THEN 1 ELSE 0 END) AS "Produce", - SUM(CASE WHEN CategoryName='Seafood' THEN 1 ELSE 0 END) AS "Seafood", - SUM(1) as Total -FROM products p ,categories c ,suppliers s WHERE p.CategoryID = c.CategoryID and s.SupplierID= p.SupplierID -GROUP BY CompanyName,QuantityPerUnit -*/ -//===================================================================== - -# example2 -# -# Query the main "product" table -# Set the rows to CompanyName and QuantityPerUnit -# and the columns to the UnitsInStock for different ranges -# and define the joins to link to lookup tables -# "categories" and "suppliers" -# - $sql = PivotTableSQL( - $gDB, # adodb connection - 'products p ,categories c ,suppliers s', # tables - 'CompanyName,QuantityPerUnit', # row fields - # column ranges -array( -' 0 ' => 'UnitsInStock <= 0', -"1 to 5" => '0 < UnitsInStock and UnitsInStock <= 5', -"6 to 10" => '5 < UnitsInStock and UnitsInStock <= 10', -"11 to 15" => '10 < UnitsInStock and UnitsInStock <= 15', -"16+" =>'15 < UnitsInStock' -), - ' p.CategoryID = c.CategoryID and s.SupplierID= p.SupplierID', # joins/where - 'UnitsInStock', # sum this field - 'Sum' # sum label prefix -); - print "$sql"; - $rs = $gDB->Execute($sql); - rs2html($rs); - /* - Generated SQL: - -SELECT CompanyName,QuantityPerUnit, - SUM(CASE WHEN UnitsInStock <= 0 THEN UnitsInStock ELSE 0 END) AS "Sum 0 ", - SUM(CASE WHEN 0 < UnitsInStock and UnitsInStock <= 5 THEN UnitsInStock ELSE 0 END) AS "Sum 1 to 5", - SUM(CASE WHEN 5 < UnitsInStock and UnitsInStock <= 10 THEN UnitsInStock ELSE 0 END) AS "Sum 6 to 10", - SUM(CASE WHEN 10 < UnitsInStock and UnitsInStock <= 15 THEN UnitsInStock ELSE 0 END) AS "Sum 11 to 15", - SUM(CASE WHEN 15 < UnitsInStock THEN UnitsInStock ELSE 0 END) AS "Sum 16+", - SUM(UnitsInStock) AS "Sum UnitsInStock", - SUM(1) as Total -FROM products p ,categories c ,suppliers s WHERE p.CategoryID = c.CategoryID and s.SupplierID= p.SupplierID -GROUP BY CompanyName,QuantityPerUnit - */ -} +GetCol("select distinct $colfield from $tables $where order by 1"); + if (!$aggfield) $hidecnt = false; + + $sel = "$rowfields, "; + if (is_array($colfield)) { + foreach ($colfield as $k => $v) { + if (!$hidecnt) $sel .= "\n\t$aggfn(CASE WHEN $v THEN 1 ELSE 0 END) AS \"$k\", "; + if ($aggfield) + $sel .= "\n\t$aggfn(CASE WHEN $v THEN $aggfield ELSE 0 END) AS \"$sumlabel$k\", "; + } + } else { + foreach ($colarr as $v) { + if (!is_numeric($v)) $vq = $db->qstr($v); + else $vq = $v; + if (strlen($v) == 0 ) $v = 'null'; + if (!$hidecnt) $sel .= "\n\t$aggfn(CASE WHEN $colfield=$vq THEN 1 ELSE 0 END) AS \"$v\", "; + if ($aggfield) { + if ($hidecnt) $label = $v; + else $label = "{$v}_$aggfield"; + $sel .= "\n\t$aggfn(CASE WHEN $colfield=$vq THEN $aggfield ELSE 0 END) AS \"$label\", "; + } + } + } + if ($aggfield && $aggfield != '1'){ + $agg = "$aggfn($aggfield)"; + $sel .= "\n\t$agg as \"$sumlabel$aggfield\", "; + } + + if ($showcount) + $sel .= "\n\tSUM(1) as Total"; + + + $sql = "SELECT $sel \nFROM $tables $where \nGROUP BY $rowfields"; + return $sql; + } + +/* EXAMPLES USING MS NORTHWIND DATABASE */ +if (0) { + +# example1 +# +# Query the main "product" table +# Set the rows to CompanyName and QuantityPerUnit +# and the columns to the Categories +# and define the joins to link to lookup tables +# "categories" and "suppliers" +# + + $sql = PivotTableSQL( + $gDB, # adodb connection + 'products p ,categories c ,suppliers s', # tables + 'CompanyName,QuantityPerUnit', # row fields + 'CategoryName', # column fields + 'p.CategoryID = c.CategoryID and s.SupplierID= p.SupplierID' # joins/where +); + print "$sql"; + $rs = $gDB->Execute($sql); + rs2html($rs); + +/* +Generated SQL: + +SELECT CompanyName,QuantityPerUnit, + SUM(CASE WHEN CategoryName='Beverages' THEN 1 ELSE 0 END) AS "Beverages", + SUM(CASE WHEN CategoryName='Condiments' THEN 1 ELSE 0 END) AS "Condiments", + SUM(CASE WHEN CategoryName='Confections' THEN 1 ELSE 0 END) AS "Confections", + SUM(CASE WHEN CategoryName='Dairy Products' THEN 1 ELSE 0 END) AS "Dairy Products", + SUM(CASE WHEN CategoryName='Grains/Cereals' THEN 1 ELSE 0 END) AS "Grains/Cereals", + SUM(CASE WHEN CategoryName='Meat/Poultry' THEN 1 ELSE 0 END) AS "Meat/Poultry", + SUM(CASE WHEN CategoryName='Produce' THEN 1 ELSE 0 END) AS "Produce", + SUM(CASE WHEN CategoryName='Seafood' THEN 1 ELSE 0 END) AS "Seafood", + SUM(1) as Total +FROM products p ,categories c ,suppliers s WHERE p.CategoryID = c.CategoryID and s.SupplierID= p.SupplierID +GROUP BY CompanyName,QuantityPerUnit +*/ +/* ===================================================================== */ + +# example2 +# +# Query the main "product" table +# Set the rows to CompanyName and QuantityPerUnit +# and the columns to the UnitsInStock for different ranges +# and define the joins to link to lookup tables +# "categories" and "suppliers" +# + $sql = PivotTableSQL( + $gDB, # adodb connection + 'products p ,categories c ,suppliers s', # tables + 'CompanyName,QuantityPerUnit', # row fields + # column ranges +array( +' 0 ' => 'UnitsInStock <= 0', +"1 to 5" => '0 < UnitsInStock and UnitsInStock <= 5', +"6 to 10" => '5 < UnitsInStock and UnitsInStock <= 10', +"11 to 15" => '10 < UnitsInStock and UnitsInStock <= 15', +"16+" =>'15 < UnitsInStock' +), + ' p.CategoryID = c.CategoryID and s.SupplierID= p.SupplierID', # joins/where + 'UnitsInStock', # sum this field + 'Sum' # sum label prefix +); + print "