Permalink
Browse files

Parse triggers

This happens in a very ad-hoc fashion, but seems to work for
the test cases at hand.
  • Loading branch information...
1 parent a895d8b commit dcf30089af358e3760f963e56e9408c8d462231a Max Maischein committed Jun 11, 2012
Showing with 131 additions and 0 deletions.
  1. +4 −0 Changes
  2. +6 −0 MANIFEST
  3. +33 −0 lib/DBIx/RunSQL.pm
  4. 0 t/02-trigger.sql
  5. +28 −0 t/02-trigger.t
  6. +15 −0 t/rt77378.sql
  7. +29 −0 t/rt77378.t
  8. +16 −0 t/trigger.sql
View
@@ -1,3 +1,7 @@
+0.09 20120611
+ + Parse triggers. This fixes RT #77378
+ Test and report by Gabor Szabo
+
0.08 20120518
. Add a test for warnings raised while executing SQL
(contributed by David Golden)
View
@@ -8,9 +8,15 @@ README
README.mkdn
t/00-use.t
t/01-force.t
+t/02-trigger.sql
+t/02-trigger.t
+t/03-comment.t
t/99-changes.t
t/99-manifest.t
t/99-pod.t
t/99-todo.t
t/99-unix-text.t
t/99-versions.t
+t/rt77378.sql
+t/rt77378.t
+t/trigger.sql
View
@@ -161,10 +161,27 @@ sub run_sql_file {
};
my $status = delete $args{ verbose_handler };
+ # Because we blindly split above on /;\n/
+ # we need to reconstruct multi-line CREATE TRIGGER statements here again
+ my $trigger;
for my $statement (@sql) {
$statement =~ s/^\s*--.*$//mg;
next unless $statement =~ /\S/; # skip empty lines
+ if( $statement =~ /^\s*CREATE\s+TRIGGER\b/i ) {
+ $trigger = $statement;
+ next
+ if( $statement !~ /END$/i );
+ $statement = $trigger;
+ undef $trigger;
+ } elsif( $trigger ) {
+ $trigger .= ";\n$statement";
+ next
+ if( $statement !~ /END$/i );
+ $statement = $trigger;
+ undef $trigger;
+ };
+
$status->($statement) if $args{verbose};
if (! $args{dbh}->do($statement)) {
$errors++;
@@ -302,6 +319,22 @@ up a database from an SQL file.
=head1 NOTES
+=head2 TRIGGER HANDLING
+
+This module uses a very simplicistic approach to recognize triggers.
+Triggers are problematic because they consist of multiple SQL statements
+and this module does not implement a full SQL parser. An trigger is
+recognized by the following sequence of lines
+
+ CREATE TRIGGER
+ ...
+ END;
+
+If your SQL dialect uses a different syntax, it might still work to put
+the whole trigger on a single line in the input file.
+
+=head2 OTHER APPROACHES
+
If you find yourself wanting to write SELECT statements,
consider looking at L<Querylet> instead, which is geared towards that
and even has an interface for Excel or HTML output.
View
No changes.
View
@@ -0,0 +1,28 @@
+#!perl -w
+use strict;
+use Test::More;
+
+use DBIx::RunSQL;
+
+my $can_run = eval {
+ require DBD::SQLite;
+ 1
+};
+
+if (not $can_run) {
+ plan skip_all => "SQLite not installed";
+}
+
+plan tests => 1;
+my $lives = eval {
+ my $test_dbh = DBIx::RunSQL->create(
+ dsn => 'dbi:SQLite:dbname=:memory:',
+ sql => 't/trigger.sql',
+ #verbose => 1,
+ );
+ 1;
+};
+my $err = $@;
+ok $lives, "We can parse triggers"
+ or diag $err;
+
View
@@ -0,0 +1,15 @@
+CREATE TABLE user (
+ id INTEGER PRIMARY KEY,
+ name VARCHAR(255)
+);
+CREATE TABLE emails (
+ uid INTEGER NOT NULL,
+ name VARCHAR(255) UNIQUE NOT NULL,
+ FOREIGN KEY (uid) REFERENCES user(id)
+);
+
+CREATE TRIGGER user_cleanup
+ BEFORE DELETE ON user FOR EACH ROW
+ BEGIN
+ DELETE FROM email WHERE uid=OLD.id;
+ END;
View
@@ -0,0 +1,29 @@
+#!perl -w
+use strict;
+use Test::More;
+
+use DBIx::RunSQL;
+
+my $can_run = eval {
+ require DBD::SQLite;
+ 1
+};
+
+if (not $can_run) {
+ plan skip_all => "SQLite not installed";
+}
+
+plan tests => 1;
+
+my $dsn = 'dbi:SQLite:dbname=:memory:';
+
+my $lives = eval {
+ DBIx::RunSQL->create(
+ verbose => 0,
+ dsn => $dsn,
+ sql => 't/rt77378.sql',
+ );
+ 1
+};
+ok $lives, "We can parse triggers (RT 77378)"
+ or diag $@;
View
@@ -0,0 +1,16 @@
+-- This commented-out statement will not get passed through;
+-- SECRET PRAGMA #foo will get passed through with the next statement
+create table test (
+ id integer unique not null,
+ descr text default '',
+ ts text
+);
+
+CREATE TRIGGER trg_test_1 AFTER INSERT ON test
+ BEGIN
+ UPDATE test SET ts = DATETIME('NOW') WHERE rowid = new.rowid;
+ END;
+
+CREATE TRIGGER trg_test_2 AFTER INSERT ON test BEGIN
+ UPDATE test SET ts = DATETIME('NOW') WHERE rowid = new.rowid;
+END;

0 comments on commit dcf3008

Please sign in to comment.