use strict;
use warnings;
-use vars qw[ $DEBUG $VERSION $GRAMMAR @EXPORT_OK ];
-$VERSION = '1.59';
+
+our $VERSION = '1.59';
+
+our $DEBUG;
$DEBUG = 0 unless defined $DEBUG;
use Data::Dumper;
-use Parse::RecDescent;
-use Exporter;
-use base qw(Exporter);
+use SQL::Translator::Utils qw/ddl_parser_instance/;
-@EXPORT_OK = qw(parse);
-
-# Enable warnings within the Parse::RecDescent module.
-$::RD_ERRORS = 1; # Make sure the parser dies when it encounters an error
-$::RD_WARN = 1; # Enable warnings. This will warn on unused rules &c.
-$::RD_HINT = 1; # Give out hints to help fix problems.
+use base qw(Exporter);
+our @EXPORT_OK = qw(parse);
-$GRAMMAR = q!
+our $GRAMMAR = <<'END_OF_GRAMMAR';
-{ my ( %tables, @views, $table_order, $field_order, @table_comments) }
+{ my ( %tables, @views, @triggers, $table_order, $field_order, @table_comments) }
#
# The "eofile" rule makes the parser fail if any "statement" rule
# won't cause the failure needed to know that the parse, as a whole,
# failed. -ky
#
-startrule : statement(s) eofile { { tables => \%tables, views => \@views } }
+startrule : statement(s) eofile {
+ {
+ tables => \%tables,
+ views => \@views,
+ triggers => \@triggers,
+ }
+}
eofile : /^\Z/
-
statement : create
| comment_on_table
| comment_on_column
commit : /commit/i ';'
-connect : /^\s*\\\connect.*\n/
+connect : /^\s*\\connect.*\n/
set : /set/i /[^;]*/ ';'
drop : /drop/i /[^;]*/ ';'
string :
- /'(\\.|''|[^\\\'])*'/
+ /'(\.|''|[^\\'])*'/
nonstring : /[^;\'"]+/
}
}
+trigger_name : name_with_opt_quotes
+
+trigger_scope : /FOR/i /EACH/i /(ROW|STATEMENT)/i { $return = lc $1 }
+
+before_or_after : /(before|after)/i { $return = lc $1 }
+
+trigger_action : /.+/
+
+database_event : /insert|update|delete/i
+database_events : database_event(s /OR/)
+
+create : CREATE /TRIGGER/i trigger_name before_or_after database_events /ON/i table_id trigger_scope(?) trigger_action
+ {
+ # Hack to pass roundtrip tests which have trigger statements terminated by double semicolon
+ # and expect the returned data to have the same
+ my $action = $item{trigger_action};
+ $action =~ s/;$//;
+
+ push @triggers, {
+ name => $item{trigger_name},
+ perform_action_when => $item{before_or_after},
+ database_events => $item{database_events},
+ on_table => $item{table_id}{table_name},
+ scope => $item{'trigger_scope(?)'}[0],
+ action => $action,
+ }
+ }
+
#
# Create anything else (e.g., domain, etc.)
#
index_name : name_with_opt_quotes
+
data_type : pg_data_type parens_value_list(?)
{
my $data_type = $item[1];
};
}
|
- /(bit|box|cidr|circle|date|inet|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|json|hstore)/i
{
$return = { type => $item[1] };
}
storage_type : /(plain|external|extended|main)/i
-temporary : /temp(orary)?\\b/i
+temporary : /temp(orary)?\b/i
{
1;
}
create_index : CREATE /index/i
-default_val : DEFAULT /(\d+|'[^']*'|\w+\(.*\))|\w+/
+default_val : DEFAULT /(\d+|'[^']*'|\w+\(.*\))|\w+|\(\d+\)/ ( '::' data_type )(?)
{
my $val = defined $item[2] ? $item[2] : '';
$val =~ s/^'|'$//g;
+ $val =~ s/^\((\d+)\)\z/$1/; # for example (0)::smallint
$return = {
supertype => 'constraint',
type => 'default',
| /null/i
{ 'NULL' }
-!;
+END_OF_GRAMMAR
sub parse {
my ( $translator, $data ) = @_;
- my $parser = Parse::RecDescent->new($GRAMMAR);
- $::RD_TRACE = $translator->trace ? 1 : undef;
- $DEBUG = $translator->debug;
+ # Enable warnings within the Parse::RecDescent module.
+ local $::RD_ERRORS = 1 unless defined $::RD_ERRORS; # Make sure the parser dies when it encounters an error
+ local $::RD_WARN = 1 unless defined $::RD_WARN; # Enable warnings. This will warn on unused rules &c.
+ local $::RD_HINT = 1 unless defined $::RD_HINT; # Give out hints to help fix problems.
- unless (defined $parser) {
- return $translator->error("Error instantiating Parse::RecDescent ".
- "instance: Bad grammer");
- }
+ local $::RD_TRACE = $translator->trace ? 1 : undef;
+ local $DEBUG = $translator->debug;
+
+ my $parser = ddl_parser_instance('PostgreSQL');
my $result = $parser->startrule($data);
die "Parse failed.\n" unless defined $result;
$view->extra ( temporary => 1 ) if $vinfo->{is_temporary};
}
+ for my $trigger (@{ $result->{triggers} }) {
+ $schema->add_trigger( %$trigger );
+ }
+
return 1;
}