Allow skipped insert statements and trigger bodies to contain quoted semi-colons
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Parser / PostgreSQL.pm
index 70b40a1..b666ea0 100644 (file)
@@ -1,7 +1,7 @@
 package SQL::Translator::Parser::PostgreSQL;
 
 # -------------------------------------------------------------------
-# $Id: PostgreSQL.pm,v 1.40 2004-08-30 18:54:58 kycl4rk Exp $
+# $Id: PostgreSQL.pm,v 1.47 2006-06-09 13:56:58 schiffbruechige Exp $
 # -------------------------------------------------------------------
 # Copyright (C) 2002-4 SQLFairy Authors
 #
@@ -108,7 +108,7 @@ View table:
 
 use strict;
 use vars qw[ $DEBUG $VERSION $GRAMMAR @EXPORT_OK ];
-$VERSION = sprintf "%d.%02d", q$Revision: 1.40 $ =~ /(\d+)\.(\d+)/;
+$VERSION = sprintf "%d.%02d", q$Revision: 1.47 $ =~ /(\d+)\.(\d+)/;
 $DEBUG   = 0 unless defined $DEBUG;
 
 use Data::Dumper;
@@ -139,10 +139,12 @@ $GRAMMAR = q!
 startrule : statement(s) eofile { \%tables }
 
 eofile : /^\Z/
+   
 
 statement : create
   | comment_on_table
   | comment_on_column
+  | comment_on_other
   | comment
   | alter
   | grant
@@ -180,14 +182,21 @@ grant : /grant/i WORD(s /,/) /on/i TABLE(?) table_name /to/i name_with_opt_quote
 
 drop : /drop/i /[^;]*/ ';'
 
-insert : /insert/i /[^;]*/ ';'
+string :
+   /'(\\.|''|[^\\\'])*'/ 
 
-update : /update/i /[^;]*/ ';'
+nonstring : /[^;\'"]+/
+
+statement_body : (string | nonstring)(s?)
+
+insert : /insert/i statement_body ';'
+
+update : /update/i statement_body ';'
 
 #
 # Create table.
 #
-create : create_table table_name '(' create_definition(s /,/) ')' table_option(s?) ';'
+create : create_table table_name '(' create_definition(s? /,/) ')' table_option(s?) ';'
     {
         my $table_name                       = $item{'table_name'};
         $tables{ $table_name }{'order'}      = ++$table_order;
@@ -273,14 +282,55 @@ comment_on_column : /comment/i /on/i /column/i column_name /is/i comment_phrase
     {
         my $table_name = $item[4]->{'table'};
         my $field_name = $item[4]->{'field'};
-        push @{ $tables{ $table_name }{'fields'}{ $field_name }{'comments'} }, 
-            $item{'comment_phrase'};
+        if ($tables{ $table_name }{'fields'}{ $field_name } ) {
+          push @{ $tables{ $table_name }{'fields'}{ $field_name }{'comments'} }, 
+              $item{'comment_phrase'};
+        }
+        else {
+           die "No such column as $table_name.$field_name";
+        }
     }
 
+comment_on_other : /comment/i /on/i /\w+/ /\w+/ /is/i comment_phrase ';'
+    {
+        push(@table_comments, $item{'comment_phrase'});
+    }
+
+# [added by cjm 20041019]
+# [TODO: other comment-on types]
+# for now we just have a general mechanism for handling other
+# kinds of comments than table/column; I'm not sure of the best
+# way to incorporate these into the datamodel
+#
+# this is the exhaustive list of types of comment:
+#COMMENT ON DATABASE my_database IS 'Development Database';
+#COMMENT ON INDEX my_index IS 'Enforces uniqueness on employee id';
+#COMMENT ON RULE my_rule IS 'Logs UPDATES of employee records';
+#COMMENT ON SEQUENCE my_sequence IS 'Used to generate primary keys';
+#COMMENT ON TABLE my_table IS 'Employee Information';
+#COMMENT ON TYPE my_type IS 'Complex Number support';
+#COMMENT ON VIEW my_view IS 'View of departmental costs';
+#COMMENT ON COLUMN my_table.my_field IS 'Employee ID number';
+#COMMENT ON TRIGGER my_trigger ON my_table IS 'Used for R.I.';
+#
+# this is tested by test 08
+
 column_name : NAME '.' NAME
     { $return = { table => $item[1], field => $item[3] } }
 
-comment_phrase : /'.*?'|NULL/ 
+comment_phrase : /null/i
+    { $return = 'NULL' }
+
+comment_phrase : /'/ comment_phrase_unquoted(s) /'/
+    { my $phrase = join(' ', @{ $item[2] });
+      $return = $phrase}
+
+# [cjm TODO: double-single quotes in a comment_phrase]
+comment_phrase_unquoted : /[^\']*/
+    { $return = $item[1] }
+
+
+xxxcomment_phrase : /'.*?'|NULL/ 
     { 
         my $val = $item[1] || '';
         $val =~ s/^'|'$//g;
@@ -351,8 +401,8 @@ column_constraint : constraint_name(?) column_constraint_type deferrable(?) defe
             reference_table  => $desc->{'reference_table'},
             reference_fields => $desc->{'reference_fields'},
             match_type       => $desc->{'match_type'},
-            on_delete_do     => $desc->{'on_delete_do'},
-            on_update_do     => $desc->{'on_update_do'},
+            on_delete        => $desc->{'on_delete'} || $desc->{'on_delete_do'},
+            on_update        => $desc->{'on_update'} || $desc->{'on_update_do'},
         } 
     }
 
@@ -385,8 +435,8 @@ column_constraint_type : /not null/i { $return = { type => 'not_null' } }
             reference_table  => $item[2],
             reference_fields => $item[3][0],
             match_type       => $item[4][0],
-            on_delete_do     => $on_delete,
-            on_update_do     => $on_update,
+            on_delete        => $on_delete,
+            on_update        => $on_update,
         }
     }
 
@@ -431,6 +481,11 @@ pg_data_type :
             };
         }
     |
+    /interval/i
+        {
+            $return = { type => 'interval' };
+        }
+    |
     /(integer|int4?)/i # interval must come before this
         { 
             $return = {
@@ -498,7 +553,7 @@ pg_data_type :
             $return = { type => 'bytea' };
         }
     |
-    /(timestamptz|timestamp)( without time zone)?/i
+    /(timestamptz|timestamp)( with(out)? time zone)?/i
         { 
             $return = { type => 'timestamp' };
         }
@@ -511,7 +566,7 @@ pg_data_type :
             };
         }
     |
-    /(bit|box|cidr|circle|date|inet|interval|line|lseg|macaddr|money|numeric|decimal|path|point|polygon|timetz|time|varchar)/i
+    /(bit|box|cidr|circle|date|inet|line|lseg|macaddr|money|numeric|decimal|path|point|polygon|timetz|time|varchar)/i
         { 
             $return = { type => $item[1] };
         }
@@ -548,8 +603,8 @@ table_constraint : comment(s?) constraint_name(?) table_constraint_type deferrab
             reference_table  => $desc->{'reference_table'},
             reference_fields => $desc->{'reference_fields'},
             match_type       => $desc->{'match_type'}[0],
-            on_delete_do     => $desc->{'on_delete_do'},
-            on_update_do     => $desc->{'on_update_do'},
+            on_delete        => $desc->{'on_delete'} || $desc->{'on_delete_do'},
+            on_update        => $desc->{'on_update'} || $desc->{'on_update_do'},
             comments         => [ @comments ],
         } 
     }
@@ -593,8 +648,8 @@ table_constraint_type : /primary key/i '(' name_with_opt_quotes(s /,/) ')'
             reference_table  => $item[6],
             reference_fields => $item[7][0],
             match_type       => $item[8][0],
-            on_delete_do     => $on_delete || '',
-            on_update_do     => $on_update || '',
+            on_delete     => $on_delete || '',
+            on_update     => $on_update || '',
         }
     }
 
@@ -926,8 +981,8 @@ sub parse {
                 reference_table  => $cdata->{'reference_table'},
                 reference_fields => $cdata->{'reference_fields'},
                 match_type       => $cdata->{'match_type'} || '',
-                on_delete        => $cdata->{'on_delete_do'},
-                on_update        => $cdata->{'on_update_do'},
+                on_delete        => $cdata->{'on_delete'} || $cdata->{'on_delete_do'},
+                on_update        => $cdata->{'on_update'} || $cdata->{'on_update_do'},
                 expression       => $cdata->{'expression'},
             ) or die "Can't add constraint of type '" .
                 $cdata->{'type'} .  "' to table '" . $table->name .