php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #6202 Global variables get corrupted while retrieving from a query result
Submitted: 2000-08-16 16:30 UTC Modified: 2000-11-30 21:43 UTC
From: gsohl at freedomgroup dot com Assigned:
Status: Closed Package: MSSQL related
PHP Version: 4.0.2 OS: Windows NT 4.0 Workstation
Private report: No CVE-ID: None
 [2000-08-16 16:30 UTC] gsohl at freedomgroup dot com
I've discovered a sequence of code that causes some global variables to become corrupted while retrieving results from a query done through the native MSSQL interface. It happened while porting the "Phorum" code to MSSQL (something I've done before, but am now doing again to the latest Phorum release). When this occurs, the script never stops executing, however the output does get flushed to the browser.

There is one file required to be included with this source. It is the MSSQL access class for Phorum that I've coded. I'll include it at the bottom of this message.

Please contact me if you need any assistance in testing or any additional info. Thanks.

Greg Sohl
gsohl@freedomgroup.com


Here is the code that causes the error condition (with several debug echos included):

<?PHP
require('mssql.php');

// initialize database variables
  $dbName='Support';
  $dbUser='sa';
  $dbPass='xxxxxxxx';
  $dbServer='TFG_WWW';

// create database classes
  if ( defined( "_DB_LAYER" ) && $dbName!=''){
    $DB = new db();
    $DB->open($dbName, $dbServer, $dbUser, $dbPass);
    $q = new query($DB); //dummy query for generic operations
  }


$sSQL = "Select thread from testforum where thread > 0 order by thread desc";
$thread_list = new query($DB, $sSQL);



echo "max = $max, min = $min<BR>";

echo 'Pos 1<BR>';

  $rec = array();
  $rec['thread'] = 5;

  $rec=$thread_list->getrow();
  if(isset($rec['thread'])){
    $max=$rec["thread"];

echo "Pos 2 - max = $max<BR>";

  	while (is_array($rec)){

echo "Pos 3 - min = $min<BR>";
echo "1 - max = $max, min = $min<BR>";

      $min=$rec["thread"];

echo "2 - max = $max, min = $min<BR>";

      $rec=$thread_list->getrow();

echo "3 - max = $max, min = $min<BR>";

    }

echo "4 - max = $max, min = $min<BR>";

  }
  else{
    $max=0;
    $min=0;
  }

echo "5 - max = $max, min = $min<BR>";
?>




Here is the output from the code:

max = , min = 
Pos 1
Pos 2 - max = 5
Pos 3 - min = 
1 - max = 5, min = 
2 - max = 5, min = 5
3 - max = 5, min = 5
Pos 3 - min = 5
1 - max = 5, min = 5
2 - max = 0, min = 5
3 - max = 0, min = 5713248
4 - max = , min = 5713248
5 - max = , min = 5713248




Here is the required file, mssql.php

<?PHP

$debug = 1;

if ( !defined( "_DB_LAYER" ) )
	{
	define("_DB_LAYER", 1 );

	class db
		{
		var $connect_id;
		var $type;

		function db($database_type="mssql")
			{
			$this->type=$database_type;
			}

		function open($database, $host, $user, $password)
			{
			$this->connect_id=mssql_connect($host, $user, $password);
			if ($this->connect_id)
				{
				$result=mssql_select_db($database);
				if (!$result)
					{
					mssql_close($this->connect_id);
					$this->connect_id=$result;
					}
				}
			return $this->connect_id;
			}

		function drop_sequence($sequence)
			{
			$esequence=$sequence."_seq";
			$sSQL="DROP TABLE $esequence";
			$query=new query($this, $sSQL);
			return $query->error();
			}

		function reset_sequence($sequence, $newval)
			{
			$this->nextid($sequence);
			$esequence=$sequence."_seq";
			$sSQL="Replace into $esequence values ('', $newval)";
			$query=new query($this, $sSQL);
			return $query->error();
			}

		function nextid($sequence)
			{
			// Not needed for MS SQL as we use an Identity field

			// Function returns the next available id for $sequence, if it's not
			// already defined, the first id will start at 1.
			// This function will create a table for each sequence called
			// '{sequence_name}_seq' in the current database.
			/*
			$esequence=ereg_replace("'","''",$sequence)."_seq";
			$query=new query($this, "Select * from $esequence limit 1");
			$query->query($this, "REPLACE INTO $esequence values ('', nextval+1)");
			if ($query->result)
				{
				$result=mssql_insert_id($query->result);
				}
			else
				{
				$query->query($this, "CREATE TABLE $esequence ( seq char(1) DEFAULT '' NOT NULL, nextval bigint(20) unsigned DEFAULT '0' NOT NULL auto_increment, PRIMARY KEY (seq), KEY (nextval) )");
				$query->query($this, "REPLACE INTO $esequence values ('', nextval+1)");
				$result=mssql_insert_id($query->result);
				}
			*/
			return 1;
			}

		function close()
			{
			// Closes the database connection and frees any query results left.

			if ($this->query_id && is_array($this->query_id))
				{
				while (list($key,$val)=each($this->query_id))
					{
					mssql_free_result($val);
					}
				}
			$result=mssql_close($this->connect_id);
			return $result;
			}

		};


	/************************************** QUERY ***************************/

	class query
		{
		var $result;
		var $row;
		var $error = 0;

		function query(&$db, $query="")
			{
			global $debug;
			// Constructor of the query object.
			// executes the query, notifies the db object of the query result to clean
			// up later
			if ($query!="")
				{
				//      if ($this->result) {
				//        $this->free(); // query not called as constructor therefore there may
				// be something to clean up.
				//      }
				if ($debug==1) echo "\n<!--$query-->\n";

				$this->error=0;

				$this->result=mssql_query($query, $db->connect_id);
				if (!$this->result)
					$this->error=1;

				return $this->result;
				}
			}

		function getrow()
			{
			// Gets the next row for processing with $this->field function later.

			$this->row=mssql_fetch_array($this->result);
			return $this->row;
			}

		function numrows()
			{
			// Gets the number of rows returned in the query

			$result=mssql_num_rows($this->result);
			return $result;
			}

		function error()
			{
			// Gets the last error message reported for this query

			// $result="";
			return $this->error;
			}

		function field($field, $row="-1")
			{
			// get the value of the field with name $field
			// in the current row or in row $row if supplied

			if ($row!=-1)
				{
				$result=mssql_result($this->result, $row, $field);
				}
			else{
				$result=$this->row[$field];
				}

		    if(isset($result))
		    	{
				return $result;
		    	}
		    else
		    	{
		      	return '0';
		    	}
			}

		function firstrow()
			{
			// return the current row pointer to the first row
			// (CAUTION: other versions may execute the query again!! (e.g. for oracle))

			$result=mssql_data_seek($this->result,0);
			if ($result)
				{
				$result=$this->getrow();
				}
			return $this->row;
			}

		function free()
			{
			// free the mssql result tables

			return mssql_free_result($this->result);
			}

		}; // End class


	// Custom Create Table Section

		function create_table(&$DB, $table, $table_name)
			{
			GLOBAL $q, $debug;
			$errormsg = '';

			if($table=="main")
				{
				$sSQL="CREATE TABLE $table_name (id int DEFAULT '0' NOT NULL, datestamp datetime DEFAULT '0000-00-00 00:00:00' NOT NULL, thread int DEFAULT '0' NOT NULL, parent int DEFAULT '0' NOT NULL, author char(37) DEFAULT '' NOT NULL, subject char(50) DEFAULT '' NOT NULL, email char(50) DEFAULT '' NOT NULL, host char(50) DEFAULT '' NOT NULL, email_reply char(1) NOT NULL DEFAULT 'N', approved char(1) NOT NULL DEFAULT 'N', PRIMARY KEY (id))";
				if ($debug==1) echo "\n<!--$sSQL-->\n";
				$q->query($DB, $sSQL);

				if(!$q->error())
					{
					$sSQL="CREATE INDEX author ON $table_name (author)";
					if ($debug==1) echo "\n<!--$sSQL-->\n";
					$q->query($DB, $sSQL);

					if(!$q->error())
						{
						$sSQL="CREATE INDEX datestamp ON $table_name (datestamp)";
						if ($debug==1) echo "\n<!--$sSQL-->\n";
						$q->query($DB, $sSQL);

						if(!$q->error())
							{
							$sSQL="CREATE INDEX subject ON $table_name (subject)";
							if ($debug==1) echo "\n<!--$sSQL-->\n";
							$q->query($DB, $sSQL);

							if(!$q->error())
								{
								$sSQL="CREATE INDEX thread ON $table_name (thread)";
								if ($debug==1) echo "\n<!--$sSQL-->\n";
								$q->query($DB, $sSQL);

								if(!$q->error())
									{
									$sSQL="CREATE INDEX parent ON $table_name (parent)";
									if ($debug==1) echo "\n<!--$sSQL-->\n";
									$q->query($DB, $sSQL);
									}
								else
									$errormsg = $q->error();
								}
							else
								$errormsg = $q->error();
							}
						else
							$errormsg = $q->error();
						}
					else
						$errormsg = $q->error();


					$sSQL="CREATE TABLE ".$table_name."_bodies (id int IDENTITY NOT NULL, body text DEFAULT '' NOT NULL, thread int DEFAULT '0' NOT NULL, PRIMARY KEY (id))";

					if ($debug==1) echo "\n<!--$sSQL-->\n";

					$q->query($DB, $sSQL);
					if($q->error())
						{
						$errormsg = $q->error();
						$sSQL="DROP TABLE ".$table_name;

						if ($debug==1) echo "\n<!--$sSQL-->\n";

						$q->query($DB, $sSQL);
						return $errormsg;
						}
					else
						{
						$sSQL="CREATE INDEX thread ON ".$table_name."_bodies (thread)";
						echo "\n<!--$sSQL-->\n";
						$q->query($DB, $sSQL);

						if($q->error())
							{
							$errormsg = $q->error();
							$sSQL="DROP TABLE ".$table_name;
							if ($debug==1) echo "\n<!--$sSQL-->\n";
							$q->query($DB, $sSQL);

							$sSQL="DROP TABLE ".$table_name."_bodies";
							if ($debug==1) echo "\n<!--$sSQL-->\n";
							$q->query($DB, $sSQL);

							return $errormsg;
							}

						return "";
						}
					}
				else
					$errormsg = $q->error();
				}
			elseif($table=="forums")
				{
				echo 'Creating the main Forums table';
				$sSQL="CREATE TABLE ".$table_name." (
					id int DEFAULT '0' NOT NULL,
					name char(50) DEFAULT '' NOT NULL,
					active smallint DEFAULT 0 NOT NULL,
					description char(255) DEFAULT '' NOT NULL,
					config_suffix char(50) DEFAULT '' NOT NULL,
					folder char(1) DEFAULT '0' NOT NULL,
					parent int DEFAULT 0 NOT NULL,
					display int DEFAULT 0 NOT NULL,
					table_name char(50) DEFAULT '' NOT NULL,
					moderation char(1) DEFAULT 'n' NOT NULL,
					mod_email char(50) DEFAULT '' NOT NULL,
					mod_pass char(50) DEFAULT '' NOT NULL,
					email_list char(50) DEFAULT '' NOT NULL,
					email_return char(50) DEFAULT '' NOT NULL,
					check_dup smallint DEFAULT 0 NOT NULL,
					multi_level smallint DEFAULT 0 NOT NULL,
					collapse smallint DEFAULT 0 NOT NULL,
					flat smallint DEFAULT 0 NOT NULL,
					staff_host char(50) DEFAULT '' NOT NULL,
					lang char(50) DEFAULT '' NOT NULL,
					html char(40) DEFAULT 'N' NOT NULL,
					table_width char(4) DEFAULT '' NOT NULL,
					table_header_color char(7) DEFAULT '' NOT NULL,
					table_header_font_color char(7) DEFAULT '' NOT NULL,
					table_body_color_1 char(7) DEFAULT '' NOT NULL,
					table_body_color_2 char(7) DEFAULT '' NOT NULL,
					table_body_font_color_1 char(7) DEFAULT '' NOT NULL,
					table_body_font_color_2 char(7) DEFAULT '' NOT NULL,
					nav_color char(7) DEFAULT '' NOT NULL,
					nav_font_color char(7) DEFAULT '' NOT NULL,
					PRIMARY KEY (id) )";

				if ($debug==1) echo "\n<!--$sSQL-->\n";

				$q->query($DB, $sSQL);

				if (!$q->error())
					{
					$sSQL="CREATE INDEX name ON $table_name (name)";
					if ($debug==1) echo "\n<!--$sSQL-->\n";
					$q->query($DB, $sSQL);

					if (!$q->error())
						{
						$sSQL="CREATE INDEX active ON $table_name (active)";
						if ($debug==1) echo "\n<!--$sSQL-->\n";
						$q->query($DB, $sSQL);

						if (!$q->error())
							{
							$sSQL="CREATE INDEX parent ON $table_name (parent)";
							if ($debug==1) echo "\n<!--$sSQL-->\n";
							$q->query($DB, $sSQL);

							$errormsg = $q->error();
							}
						else
							$errormsg = $q->error();
						}
					else
						$errormsg = $q->error();
					}
				else
					$errormsg = $q->error();
				}

			return $errormsg;
			}

	}

?>





Here is the SQL to create the table:

CREATE TABLE dbo.testforum (
	id int NOT NULL ,
	datestamp datetime NOT NULL ,
	thread int NOT NULL ,
	parent int NOT NULL ,
	author char (37) NOT NULL ,
	subject char (50) NOT NULL ,
	email char (50) NOT NULL ,
	host char (50) NOT NULL ,
	email_reply char (1) NOT NULL ,
	approved char (1) NOT NULL 
)


And finally, here's a script to insert some data:

insert into testforum (id,datestamp,thread,parent,author,subject,email,host,email_reply,approved)
values(5,'Aug 16 2000 12:39PM',5,0,'Greg Sohl','Test Posting','gsohl@freedomgroup.com','GSOHL1','','Y')
GO
insert into testforum (id,datestamp,thread,parent,author,subject,email,host,email_reply,approved)
values(6,'Aug 16 2000 12:41PM',5,5,'Greg Sohl','RE: Test Posting','gsohl@freedomgroup.com','GSOHL1','','Y')
GO

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2000-08-16 17:26 UTC] gsohl at freedomgroup dot com
This was against a MS SQL 6.5 server. My PHP.INI contains

extension=php_mssql70.dll


 [2000-09-06 15:07 UTC] gsohl at freedomgroup dot com
Still happening in 4.0.2.

 [2000-09-12 22:30 UTC] brianlmoon@php.net
It sounds like you are using the MSSQL 7 library to access MSSQL 6.5.  This does not work reliably.  You need to compile in the sybase libs to talk to 6.5.
 [2000-09-12 22:48 UTC] brianlmoon@php.net
Should not have been closed.
 [2000-09-20 23:43 UTC] gsohl at freedomgroup dot com
Here is the trimmed down example code:

<?PHP
$dbSesssion = mssql_connect('TFG_WWW', 'sa', 'xxxxxxx');
mssql_select_db('Support');

$sSQL = "Select thread from testforum where thread > 0 order by thread desc";
$thread_list = mssql_query($sSQL, $dbSesssion);
// $row=mssql_fetch_array($thread_list);	// Put this in and the error doesn't occur (I did it by accident)
echo "max = $max, min = $min<BR>";

echo 'Pos 1<BR>';

$row=mssql_fetch_array($thread_list);

$max=$row["thread"];

echo "Pos 2 - max = $max<BR>";

while (is_array($row))
  	{
    echo "Pos 3 - min = $min<BR>";
    echo "1 - max = $max, min = $min<BR>";

    $min=$row["thread"];

    echo "2 - max = $max, min = $min<BR>";

    $row=mssql_fetch_array($thread_list);

    echo "3 - max = $max, min = $min<BR>";
    }

 echo "4 - max = $max, min = $min<BR>";
?>


And again, the script to create the table and insert test data:

CREATE TABLE testforum (
	id int NOT NULL ,
	datestamp datetime NOT NULL ,
	thread int NOT NULL ,
	parent int NOT NULL ,
	author char (37) NOT NULL ,
	subject char (50) NOT NULL ,
	email char (50) NOT NULL ,
	host char (50) NOT NULL ,
	email_reply char (1) NOT NULL ,
	approved char (1) NOT NULL 
)
GO

insert into testforum
(id,datestamp,thread,parent,author,subject,email,host,email_reply,approved)
values(5,'Aug 16 2000 12:39PM',5,0,'Greg Sohl','Test
Posting','gsohl@freedomgroup.com','GSOHL1','','Y')
GO

insert into testforum
(id,datestamp,thread,parent,author,subject,email,host,email_reply,approved)
values(6,'Aug 16 2000 12:41PM',5,5,'Greg Sohl','RE: Test
Posting','gsohl@freedomgroup.com','GSOHL1','','Y')
GO

 [2000-11-03 09:13 UTC] gsohl at freedomgroup dot com
I've tested the bug in php 4.0.3pl1 at the prompting of Frank Kromann. It appears that the problem has been resolved. I tried it with both the php_mssql65.dll and the php_mssql70.dll. It worked with both. I also went back to the original code I found the problem with (phorum's EasyAdmin module) and verified that it also worked.

My testing was under Win NT 4.0 Workstation, running IIS 4.0 (or PWS, however you look at it). I have both the MS SQL 6.5 and the 7.0 clients (Libraries) installed. I was always going against a 6.5 database.

So by virtue of the fact that I can't recreate it (which doesn't necessarily represent sufficient testing), I'd call it fixed.

- Greg

 [2000-11-30 21:43 UTC] fmk@php.net
This error has been fixed in php4.0.3.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Dec 21 16:01:28 2024 UTC