mega improvements to the sql script parser, it can now deal with loading pg_dump...
authorchriskl <chriskl>
Mon, 7 Mar 2005 09:36:19 +0000 (09:36 +0000)
committerchriskl <chriskl>
Mon, 7 Mar 2005 09:36:19 +0000 (09:36 +0000)
HISTORY
classes/database/Postgres.php

diff --git a/HISTORY b/HISTORY
index 2486969993f97a7d370b24bbd5a3faf8f601b4c1..389f112441ce12b672699b21e1990ab0b486ce16 100644 (file)
--- a/HISTORY
+++ b/HISTORY
@@ -8,7 +8,9 @@ Features
 * Allow current database to be at the top
 * Allow base URL of PostgreSQL documentation to be configured
 * Allow variable size textarea when editing values (Juergen Weigert)
-
+* Allow SQL script upload to parse arbitrary SQL, including multiline
+  SQL statements
+  
 Bugs
 * Tree Icons are displayed middle instead of top
 * Ensure login frame is always at top level (Russell Smith)
@@ -28,14 +30,36 @@ Translations
 * Japanese from Tadashi Jokagi
 * Danish from Arne Eckmann
 
+Version 3.5.2
+-------------
+
+Bugs
+* Fix export to work with release candidates and beta releases as well as finals
+ (Russell Smith)
+* Fix port selection for local connections (Russell Smith)
+* Fix timeouts on long running operations (Adrian Nida)
+* Allow Multiline character and character varying editing and inserting
+* Do browser language detection for all languages
+
+Translations
+* Japanese from Tadashi
+* Danish from Arne
+
 Version 3.5.1
 -------------
 
 Bugs
 * Support 8.0beta5 schema tablespace changes
+* Help link fixes
+* Estimated row count in 7.0 and 7.1 fixes
+* Priviliges nav fix
+* Function privileges fix
+* Search path fix
+* pg_dump on win32 8.0 native fix
 
 Translations
 * Romanian from Alin
+* Italian updates from Nicola
 
 Version 3.5
 -----------
index 6f58847284eed4fbcdaac671c458efff3411020b..ee7b00910d1bc2c10151893bcf18c4290d35cdda 100755 (executable)
@@ -4,7 +4,7 @@
  * A class that implements the DB interface for Postgres
  * Note: This class uses ADODB and returns RecordSets.
  *
- * $Id: Postgres.php,v 1.254 2005/02/20 04:47:46 mr-russ Exp $
+ * $Id: Postgres.php,v 1.255 2005/03/07 09:36:19 chriskl Exp $
  */
 
 // @@@ THOUGHT: What about inherits? ie. use of ONLY???
@@ -4103,15 +4103,33 @@ class Postgres extends ADODB_base {
                return $this->selectSet($sql);
        }
 
+    /**
+     * 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.
+        * 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
         * @return Result of final query, false on any failure.
         */
        function executeScript($name) {
                global $data;
-               
+
                // This whole function isn't very encapsulated, but hey...
                $conn = $data->conn->_connectionID;
                if (!is_uploaded_file($_FILES[$name]['tmp_name'])) return false;
@@ -4119,16 +4137,163 @@ class Postgres extends ADODB_base {
                $fd = fopen($_FILES[$name]['tmp_name'], 'r');
                if (!$fd) return false;
                
+               // Build up each SQL statement, they can be multiline
+               $query_buf = null;
+               $query_start = 0;
+               $in_quote = 0;
+               $in_xcomment = 0;
+               $bslash_count = 0;
+               $paren_level = 0;
+               $len = 0;
+               $i = 0;
+               $prevlen = 0;
+               $thislen = 0;
+               
                // Loop over each line in the file
                while (!feof($fd)) {
-                       $sql = fgets($fd, 32768);
-                       // Check that the query is something...
-                       if (trim($sql) == '') continue;
+                       $line = fgets($fd, 32768);
+                       
+                       // 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;
+                       }
+    
+                       /* 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);
+                   }
+    
+                       /* 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);      
+                               // Check for COPY request
+                               if (pg_result_status($res) == 4) { // 4 == PGSQL_COPY_FROM
+                                       while (!feof($fd)) {
+                                               $copy = fgets($fd, 32768);
+                                               pg_put_line($conn, $copy);
+                                               if ($copy == "\\.\n" || $copy == "\\.\r\n") {
+                                                       pg_end_copy($conn);
+                                                       break;
+                                               }
+                                       }
+                               }                               
+                               }
+        
+                                       $query_buf = null;
+                                       $query_start = $i + $thislen;
+                       }
+           } // end for
+
+               /* Put the rest of the line in the query buffer. */
+               $subline = substr($line, $query_start);
+               if ($in_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, $sql);
+                               $res = pg_query($conn, $query_buf);
                        else
-                               $res = pg_exec($conn, $sql);
+                               $res = pg_exec($conn, $query_buf);
                        // Check for COPY request
                        if (pg_result_status($res) == 4) { // 4 == PGSQL_COPY_FROM
                                while (!feof($fd)) {
@@ -4139,8 +4304,8 @@ class Postgres extends ADODB_base {
                                                break;
                                        }
                                }
-                       }
-               }
+                       }                               
+       }
                
                fclose($fd);