Tab/WS crusade
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Parser / SQLServer.pm
index 34b4299..bfe427d 100644 (file)
@@ -1,23 +1,5 @@
 package SQL::Translator::Parser::SQLServer;
 
-# -------------------------------------------------------------------
-# Copyright (C) 2002-2009 SQLFairy Authors
-#
-# 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; version 2.
-#
-# 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 for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-# 02111-1307  USA
-# -------------------------------------------------------------------
-
 =head1 NAME
 
 SQL::Translator::Parser::SQLServer - parser for SQL Server
@@ -35,23 +17,20 @@ should probably be considered a work in progress.
 =cut
 
 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);
-
-@EXPORT_OK = qw(parse);
+use SQL::Translator::Utils qw/ddl_parser_instance/;
 
-$::RD_ERRORS = 1;
-$::RD_WARN   = 1;
-$::RD_HINT   = 1;
+use base qw(Exporter);
+our @EXPORT_OK = qw(parse);
 
-$GRAMMAR = q{
+our $GRAMMAR = <<'END_OF_GRAMMAR';
 
 {
     my ( %tables, @table_comments, $table_order, %procedures, $proc_order, %views, $view_order );
@@ -84,6 +63,7 @@ statement : create_table
     | create_index
     | create_constraint
     | comment
+    | disable_constraints
     | drop
     | use
     | setuser
@@ -106,6 +86,8 @@ if_command : grant
 
 object_not_null : /object_id/i '(' ident ')' /is not null/i
 
+field_not_null : /where/i field_name /is \s+ not \s+ null/ix
+
 print : /\s*/ /print/i /.*/
 
 else : /else/i /.*/
@@ -145,7 +127,7 @@ comment_middle : m{([^*]+|\*(?!/))*}
 
 drop : if_exists(?) /drop/i tbl_drop END_STATEMENT
 
-tbl_drop : /table/i NAME
+tbl_drop : /table/i ident
 
 if_exists : /if exists/i '(' /select/i 'name' /from/i 'sysobjects' /[^\)]+/ ')'
 
@@ -191,17 +173,29 @@ create_table : /create/i /table/i ident '(' create_def(s /,/) ')' lock(?) on_sys
         }
     }
 
+disable_constraints : if_exists(?) /alter/i /table/i ident /nocheck/i /constraint/i /all/i END_STATEMENT
+
+# this is for the normal case
+create_constraint : /create/i constraint END_STATEMENT
+    {
+        @table_comments = ();
+        push @{ $tables{ $item[2]{'table'} }{'constraints'} }, $item[2];
+    }
+
+# and this is for the BEGIN/END case
 create_constraint : /create/i constraint
     {
         @table_comments = ();
         push @{ $tables{ $item[2]{'table'} }{'constraints'} }, $item[2];
     }
 
+
 create_constraint : /alter/i /table/i ident /add/i foreign_key_constraint END_STATEMENT
     {
         push @{ $tables{ $item[3]{name} }{constraints} }, $item[5];
     }
 
+
 create_index : /create/i index
     {
         @table_comments = ();
@@ -299,10 +293,19 @@ constraint : primary_key_constraint
     | unique_constraint
 
 field_name : WORD
+   { $return = $item[1] }
+   | LQUOTE WORD RQUOTE
+   { $return = $item[2] }
 
 index_name : WORD
+   { $return = $item[1] }
+   | LQUOTE WORD RQUOTE
+   { $return = $item[2] }
 
 table_name : WORD
+ { $return = $item[1] }
+ | LQUOTE WORD RQUOTE
+ { $return = $item[2] }
 
 data_type : WORD field_size(?)
     {
@@ -372,7 +375,7 @@ unique_constraint : /constraint/i index_name(?) /unique/i parens_field_list
         }
     }
 
-unique_constraint : /unique/i clustered(?) INDEX(?) index_name(?) on_table(?) parens_field_list
+unique_constraint : /unique/i clustered(?) INDEX(?) index_name(?) on_table(?) parens_field_list field_not_null(?)
     {
         $return = {
             supertype => 'constraint',
@@ -423,8 +426,14 @@ index : clustered(?) INDEX index_name(?) on_table(?) parens_field_list END_STATE
 parens_field_list : '(' field_name(s /,/) ')'
     { $item[2] }
 
-ident : QUOTE(?) WORD '.' WORD QUOTE(?)
+ident : QUOTE WORD '.' WORD QUOTE | LQUOTE WORD '.' WORD RQUOTE
     { $return = { owner => $item[2], name => $item[4] } }
+    | LQUOTE WORD RQUOTE '.' LQUOTE WORD RQUOTE
+    { $return = { owner => $item[2], name => $item[6] } }
+    | LQUOTE WORD RQUOTE
+    { $return = { name  => $item[2] } }
+    | WORD '.' WORD
+    { $return = { owner => $item[1], name => $item[3] } }
     | WORD
     { $return = { name  => $item[1] } }
 
@@ -444,20 +453,24 @@ COMMA : ','
 
 QUOTE : /'/
 
-};
+LQUOTE : '['
+
+RQUOTE : ']'
+
+END_OF_GRAMMAR
 
-# -------------------------------------------------------------------
 sub parse {
     my ( $translator, $data ) = @_;
-    my $parser = Parse::RecDescent->new($GRAMMAR);
+
+    # 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.
 
     local $::RD_TRACE  = $translator->trace ? 1 : undef;
     local $DEBUG       = $translator->debug;
 
-    unless (defined $parser) {
-        return $translator->error("Error instantiating Parse::RecDescent ".
-            "instance: Bad grammer");
-    }
+    my $parser = ddl_parser_instance('SQLServer');
 
     my $result = $parser->startrule($data);
     return $translator->error( "Parse failed." ) unless defined $result;