[code]
<?php
	/*
	 * This class will parse and execute Postgresql .sql files
	 * Original importer code written by the phpPgAdmin Project.
	 * 
	 * Modified by Arend van Waart, 2008
	 * Copyright (c) 2002, 2003, 2004, 2005 The phpPgAdmin Project

		This program is free software; you can redistribute it and/or modify
		it under the terms of the GNU General Public License as published by
		the Free Software Foundation; either version 2 of the License, or
		(at your option) any later version.
		This program is distributed in the hope that it will be useful,
		but WITHOUT ANY WARRANTY; without even the implied warranty of
		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
		GNU General Public License <http://www.opensource.org/gpl-license.html>
		for more details.
	 * 
	 */

ini_set('display_errors','On');
error_reporting (E_ALL | E_STRICT);
	
class ImportSQL {
	private $connection;
	private $lang;
	function __construct ($dsn) {
		$this->initlang();
		$this->connect($dsn);
	}
	function connect ($dsn) {
		if (is_resource($this->connection)) {
			pg_close($this->connection);
			$this->connection = null;
		}
		
		$this->connection = pg_connect($dsn) or die('Could not connect');
	}
	
	private function initLang() {
		$lang['strrows'] = 'row(s)';
		$lang['strellipsis'] = '...';
		$lang['strellipsis'] = '...';
		$lang['stryes'] = 'Yes';
		$lang['strno'] = 'No';
		$lang['strtrue'] = 'TRUE';
		$lang['strfalse'] = 'FALSE';
		$lang['strbytes'] = 'bytes';
		$lang['strkb'] = 'kB';
		$lang['strmb'] = 'MB';
		$lang['strgb'] = 'GB';
		$lang['strtb'] = 'TB';
		$lang['strhealthy'] = 'Healthy';
		$lang['stroutofsync'] = 'Out of sync';
		$lang['strunknown'] = 'Unknown';
		$this->lang = $lang;
	}
	
	/**
	 * A private helper method for executeScript that advances the
	 * character by 1.  In psql this is careful to take into account
	 * multibyte languages, but we don't at the moment, so this function
	 * is someone redundant, since it will always advance by 1
	 * @param &$i The current character position in the line
	 * @param &$prevlen Length of previous character (ie. 1)
	 * @param &$thislen Length of current character (ie. 1)
	 */
	function advance_1(&$i, &$prevlen, &$thislen) {
		$prevlen = $thislen;
		$i += $thislen;
		$thislen = 1;
	}

	/**
	 * Executes an SQL script as a series of SQL statements.  Returns
	 * the result of the final step.  This is a very complicated lexer
	 * based on the REL7_4_STABLE src/bin/psql/mainloop.c lexer in
	 * the PostgreSQL source code.
	 * XXX: It does not handle multibyte languages properly.
	 * @param $name Entry in $_FILES to use
	 * @param $callback (optional) Callback function to call with each query,
	                               its result and line number.
	 * @return True for general success, false on any failure.
	 */
	function executeScript($file, $bIsString = false, $callback = "sqlCallback") {
		$conn = $this->connection;
		if (!is_resource($conn)) {
			throw new Exception("No valid postgres connector!");
		}
		// CREATE A PSEUDO STREAM FOR READING FROM VARIABLES
		if ($bIsString == true) {
			$fd = fopen("php://temp/maxmemory:" . 5 * 1024 * 1024, 'r+');

			fputs($fd, $file);
			
			// read what we have written
			rewind($fd);
		} else {
		// This whole function isn't very encapsulated, but hey...
			$fd = fopen($file,"r") or die("Could not open $file");
		}
		// Build up each SQL statement, they can be multiline
		$query_buf = null;
		$query_start = 0;
		$in_quote = 0;
		$in_xcomment = 0;
		$bslash_count = 0;
		$dol_quote = null;
		$paren_level = 0;
		$len = 0;
		$i = 0;
		$prevlen = 0;
		$thislen = 0;
		$lineno = 0;

		// Loop over each line in the file
		while (!feof($fd)) {
			$line = fgets($fd, 32768);
			$lineno++;
			// Nothing left on line? Then ignore...
			if (trim($line) == '') continue;

		    $len = strlen($line);
		    $query_start = 0;

    		/*
    		 * Parse line, looking for command separators.
    		 *
    		 * The current character is at line[i], the prior character at line[i
    		 * - prevlen], the next character at line[i + thislen].
    		 */
    		$prevlen = 0;
    		$thislen = ($len > 0) ? 1 : 0;

    		for ($i = 0; $i < $len; $this->advance_1($i, $prevlen, $thislen)) {

    			/* was the previous character a backslash? */
    			if ($i > 0 && substr($line, $i - $prevlen, 1) == '\\')
    				$bslash_count++;
    			else
    				$bslash_count = 0;

    			/*
    			 * It is important to place the in_* test routines before the
    			 * in_* detection routines. i.e. we have to test if we are in
    			 * a quote before testing for comments.
    			 */

    			/* in quote? */
    			if ($in_quote != 0)
    			{
    				/*
    				 * end of quote if matching non-backslashed character.
    				 * backslashes don't count for double quotes, though.
    				 */
    				if (substr($line, $i, 1) == $in_quote &&
    					($bslash_count % 2 == 0 || $in_quote == '"'))
    					$in_quote = 0;
    			}

				/* in or end of $foo$ type quote? */
				else if ($dol_quote) {
					if (strncmp(substr($line, $i), $dol_quote, strlen($dol_quote)) == 0) {
						$this->advance_1($i, $prevlen, $thislen);
						while(substr($line, $i, 1) != '$')
							$this->advance_1($i, $prevlen, $thislen);
						$dol_quote = null;
					}
				}

    			/* start of extended comment? */
    			else if (substr($line, $i, 2) == '/*')
    			{
    				$in_xcomment++;
    				if ($in_xcomment == 1)
    					$this->advance_1($i, $prevlen, $thislen);
    			}

    			/* in or end of extended comment? */
    			else if ($in_xcomment)
    			{
    				if (substr($line, $i, 2) == '*/' && !--$in_xcomment)
    					$this->advance_1($i, $prevlen, $thislen);
    			}

    			/* start of quote? */
    			else if (substr($line, $i, 1) == '\'' || substr($line, $i, 1) == '"') {
    				$in_quote = substr($line, $i, 1);
    		    }

				/*
				 * start of $foo$ type quote?
				 */
				else if (!$dol_quote && $this->valid_dolquote(substr($line, $i))) {
					$dol_end = strpos(substr($line, $i + 1), '$');
					$dol_quote = substr($line, $i, $dol_end + 1);
					$this->advance_1($i, $prevlen, $thislen);
					while (substr($line, $i, 1) != '$') {
						$this->advance_1($i, $prevlen, $thislen);
					}

				}

    			/* single-line comment? truncate line */
    			else if (substr($line, $i, 2) == '--')
    			{
    			    $line = substr($line, 0, $i); /* remove comment */
    				break;
    			}

    			/* count nested parentheses */
    			else if (substr($line, $i, 1) == '(') {
    				$paren_level++;
    			}

    			else if (substr($line, $i, 1) == ')' && $paren_level > 0) {
    				$paren_level--;
    			}

    			/* semicolon? then send query */
    			else if (substr($line, $i, 1) == ';' && !$bslash_count && !$paren_level)
    			{
    			    $subline = substr(substr($line, 0, $i), $query_start);
    				/* is there anything else on the line? */
    				if (strspn($subline, " \t\n\r") != strlen($subline))
    				{
    					/*
    					 * insert a cosmetic newline, if this is not the first
    					 * line in the buffer
    					 */
    					if (strlen($query_buf) > 0)
    					    $query_buf .= "\n";
    					/* append the line to the query buffer */
    					$query_buf .= $subline;
    					$query_buf .= ';';

            			// Execute the query (supporting 4.1.x PHP...). PHP cannot execute
            			// empty queries, unlike libpq
            			//if (function_exists('pg_query'))
            				$res = @pg_query($conn, $query_buf);
            			//else
            				//$res = pg_exec($conn, $query_buf);
						// Call the callback function for display
						if ($callback !== null) $this->$callback($query_buf, $res, $lineno);
            			// Check for COPY request
            			if (pg_result_status($res) == 4) { // 4 == PGSQL_COPY_FROM
            				while (!feof($fd)) {
            					$copy = fgets($fd, 32768);
            					$lineno++;
            					pg_put_line($conn, $copy);
            					if ($copy == "\\.\n" || $copy == "\\.\r\n") {
            						@pg_end_copy($conn);
            						if ($callback !== null) $this->$callback($query_buf, $res, $lineno);
            						break;
            					}
            				}
            			}
    				}

					$query_buf = null;
					$query_start = $i + $thislen;
    			}

    			/*
				 * keyword or identifier?
				 * We grab the whole string so that we don't
				 * mistakenly see $foo$ inside an identifier as the start
				 * of a dollar quote.
				 */
				// XXX: multibyte here
				else if (ereg('^[_[:alpha:]]$', substr($line, $i, 1))) {
					$sub = substr($line, $i, $thislen);
					while (ereg('^[\$_A-Za-z0-9]$', $sub)) {
						/* keep going while we still have identifier chars */
						$this->advance_1($i, $prevlen, $thislen);
						$sub = substr($line, $i, $thislen);
					}
					// Since we're now over the next character to be examined, it is necessary
					// to move back one space.
					$i-=$prevlen;
				}
    	    } // end for

    		/* Put the rest of the line in the query buffer. */
    		$subline = substr($line, $query_start);
    		if ($in_quote || $dol_quote || strspn($subline, " \t\n\r") != strlen($subline))
    		{
    			if (strlen($query_buf) > 0)
    			    $query_buf .= "\n";
    			$query_buf .= $subline;
    		}

    		$line = null;

    	} // end while

    	/*
    	 * Process query at the end of file without a semicolon, so long as
    	 * it's non-empty.
    	 */
    	if (strlen($query_buf) > 0 && strspn($query_buf, " \t\n\r") != strlen($query_buf))
    	{
			// Execute the query (supporting 4.1.x PHP...)
			if (function_exists('pg_query')) {
				$res = @pg_query($conn, $query_buf);
			} else {
				$res = @pg_exec($conn, $query_buf);
			}
			// Call the callback function for display
			if ($callback !== null) $this->$callback($query_buf, $res, $lineno);
			// Check for COPY request
			if (pg_result_status($res) == 4) { // 4 == PGSQL_COPY_FROM
				while (!feof($fd)) {
					$copy = fgets($fd, 32768);
					$lineno++;
					pg_put_line($conn, $copy);
					if ($copy == "\\.\n" || $copy == "\\.\r\n") {
						@pg_end_copy($conn);
						if ($callback !== null) $this->$callback($query_buf, $res, $lineno);
						break;
					}
				}
			}
    	}

		fclose($fd);
		return true;
	}
	
	function sqlCallback($query, $rs, $lineno) {
		$lang = $this->lang;
		$_connection = $this->connection;
		// Check if $rs is false, if so then there was a fatal error
		if ($rs === false) {
			echo '<span style="color:red">Line:' . $lineno, ': ', nl2br(htmlspecialchars(pg_last_error($_connection))), "</span><br />\n";
		}
		else {
			// Print query results
			switch (pg_result_status($rs)) {
				case PGSQL_TUPLES_OK:
					// If rows returned, then display the results
					$num_fields = pg_numfields($rs);
					echo "<p><table>\n<tr>";
					for ($k = 0; $k < $num_fields; $k++) {
						echo "<th class=\"data\">", $this->printVal(pg_fieldname($rs, $k)), "</th>";
					}
		
					$i = 0;
					$row = pg_fetch_row($rs);
					while ($row !== false) {
						$id = (($i % 2) == 0 ? '1' : '2');
						echo "<tr>\n";
						foreach ($row as $k => $v) {
							echo "<td class=\"data{$id}\" style=\"white-space:nowrap;\">", $this->printVal($v, pg_fieldtype($rs, $k), array('null' => true)), "</td>";
						}							
						echo "</tr>\n";
						$row = pg_fetch_row($rs);
						$i++;
					};
					echo "</table><br/>\n";
					echo $i, " {$lang['strrows']}</p>\n";
					break;
				case PGSQL_COMMAND_OK:
					// If we have the command completion tag
					if (version_compare(phpversion(), '4.3', '>=')) {
						echo htmlspecialchars(pg_result_status($rs, PGSQL_STATUS_STRING)), "<br/>\n";
					}
					// Otherwise if any rows have been affected
					// Otherwise output nothing...
					break;
				case PGSQL_EMPTY_QUERY:
					break;
				default:
					break;
			}
		}
	}
	
		/**
		 * Render a value into HTML using formatting rules specified
		 * by a type name and parameters.
		 *
		 * @param $str The string to change
		 *
		 * @param $type Field type (optional), this may be an internal PostgreSQL type, or:
		 *			yesno    - same as bool, but renders as 'Yes' or 'No'.
		 *			pre      - render in a <pre> block.
		 *			nbsp     - replace all spaces with &nbsp;'s
		 *			verbatim - render exactly as supplied, no escaping what-so-ever.
		 *			callback - render using a callback function supplied in the 'function' param.
		 *
		 * @param $params Type parameters (optional), known parameters:
		 *			null     - string to display if $str is null, or set to TRUE to use a default 'NULL' string,
		 *			           otherwise nothing is rendered.
		 *			clip     - if true, clip the value to a fixed length, and append an ellipsis...
		 *			cliplen  - the maximum length when clip is enabled (defaults to $conf['max_chars'])
		 *			ellipsis - the string to append to a clipped value (defaults to $lang['strellipsis'])
		 *			tag      - an HTML element name to surround the value.
		 *			class    - a class attribute to apply to any surrounding HTML element.
		 *			align    - an align attribute ('left','right','center' etc.)
		 *			true     - (type='bool') the representation of true.
		 *			false    - (type='bool') the representation of false.
		 *			function - (type='callback') a function name, accepts args ($str, $params) and returns a rendering.
		 *			lineno   - prefix each line with a line number.
		 *			map      - an associative array.
		 *
		 * @return The HTML rendered value
		 */
		function printVal($str, $type = null, $params = array()) {
			$lang = $this->lang;

			// Shortcircuit for a NULL value
			if (is_null($str))
				return isset($params['null'])
						? ($params['null'] === true ? '<i>NULL</i>' : $params['null'])
						: '';

			// no clipping!
			/*
			if (isset($params['map']) && isset($params['map'][$str])) $str = $params['map'][$str];

			// Clip the value if the 'clip' parameter is true.
			if (isset($params['clip']) && $params['clip'] === true) {
				$maxlen = isset($params['cliplen']) && is_integer($params['cliplen']) ? $params['cliplen'] : $conf['max_chars'];
				$ellipsis = isset($params['ellipsis']) ? $params['ellipsis'] : $lang['strellipsis'];
				if (strlen($str) > $maxlen) {
					$str = substr($str, 0, $maxlen-1) . $ellipsis;
				}
			}
			*/
			$out = '';

			switch ($type) {
				case 'int2':
				case 'int4':
				case 'int8':
				case 'float4':
				case 'float8':
				case 'money':
				case 'numeric':
				case 'oid':
				case 'xid':
				case 'cid':
				case 'tid':
					$align = 'right';
					$out = nl2br(htmlspecialchars($str));
					break;
				case 'yesno':
					if (!isset($params['true'])) $params['true'] = $lang['stryes'];
					if (!isset($params['false'])) $params['false'] = $lang['strno'];
					// No break - fall through to boolean case.
				case 'bool':
				case 'boolean':
					if (is_bool($str)) $str = $str ? 't' : 'f';
					switch ($str) {
						case 't':
							$out = (isset($params['true']) ? $params['true'] : $lang['strtrue']);
							$align = 'center';
							break;
						case 'f':
							$out = (isset($params['false']) ? $params['false'] : $lang['strfalse']);
							$align = 'center';
							break;
						default:
							$out = htmlspecialchars($str);
					}
					break;
				case 'bytea':
					$out = stripslashes(pg_escape_bytea($str));
					break;
				case 'pre':
					$tag = 'pre';
					$out = htmlspecialchars($str);
					break;
				case 'prenoescape':
					$tag = 'pre';
					$out = $str;
					break;
				case 'nbsp':
					$out = nl2br(str_replace(' ', '&nbsp;', htmlspecialchars($str)));
					break;
				case 'verbatim':
					$out = $str;
					break;
				case 'callback':
					$out = $params['function']($str, $params);
					break;
				case 'prettysize':
					$mult = 1;
					$limit = 10 * 1024;

					if ($str < $limit * $mult)
						$out = $str.' '.$lang['strbytes'];
					else
					{
						$mult *= 1024;
						if ($str < $limit * $mult)
							$out = floor(($str + $mult / 2) / $mult).' '.$lang['strkb'];
						else
						{
							$mult *= 1024;
							if ($str < $limit * $mult)
								$out = floor(($str + $mult / 2) / $mult).' '.$lang['strmb'];
							else
							{
								$mult *= 1024;
								if ($str < $limit * $mult)
									$out = floor(($str + $mult / 2) / $mult).' '.$lang['strgb'];
								else
								{
									$mult *= 1024;
									if ($str < $limit * $mult)
										$out = floor(($str + $mult / 2) / $mult).' '.$lang['strtb'];
								}
							}
						}
					}
					break;
				case 'slonystatus':
					switch ($str) {
					case 'insync':
						$out = $lang['strhealthy'];
						break;
					case 'outofsync':
						$out = $lang['stroutofsync'];
						break;
					default:
						$out = $lang['strunknown'];
					}
					break;
				default:
					// If the string contains at least one instance of >1 space in a row, a tab
					// character, a space at the start of a line, or a space at the start of
					// the whole string then render within a pre-formatted element (<pre>).
					if (preg_match('/(^ |  |\t|\n )/m', $str)) {
						$tag = 'pre';
						$class = 'data';
						$out = htmlspecialchars($str);
					} else {
						$out = nl2br(htmlspecialchars($str));
					}
			}

			if (isset($params['class'])) $class = $params['class'];
			if (isset($params['align'])) $align = $params['align'];

			if (!isset($tag) && (isset($class) || isset($align))) $tag = 'div';

			if (isset($tag)) {
				$alignattr = isset($align) ? " style=\"text-align: {$align}\"" : '';
				$classattr = isset($class) ? " class=\"{$class}\"" : '';
				$out = "<{$tag}{$alignattr}{$classattr}>{$out}</{$tag}>";
			}

			// Add line numbers if 'lineno' param is true
			if (isset($params['lineno']) && $params['lineno'] === true) {
				$lines = explode("\n", $str);
				$num = count($lines);
				if ($num > 0) {
					$temp = "<table>\n<tr><td class=\"{$class}\" style=\"vertical-align: top; padding-right: 10px;\"><pre class=\"{$class}\">";
					for ($i = 1; $i <= $num; $i++) {
						$temp .= $i . "\n";
					}
					$temp .= "</pre></td><td class=\"{$class}\" style=\"vertical-align: top;\">{$out}</td></tr></table>\n";
					$out = $temp;
				}
				unset($lines);
			}

			return $out;
		}
	function valid_dolquote($dquote) {
		// XXX: support multibyte
		return (ereg('^[$][$]', $dquote) || ereg('^[$][_[:alpha:]][_[:alnum:]]*[$]', $dquote));
	}
}
?>
[/code]