Changeset 6772

Show
Ignore:
Timestamp:
02/16/07 17:29:30
Author:
jan
Message:

schema validator in ExportSchemaTask?: it now validates for sql keywords and activerecord conventions

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/README

    r6140 r6772  
    150150or implied. See the License for the specific language governing permissions and limitations under  
    151151the License.  
     152 
     153== 3dparty code / MIT License 
     154 
     155This product contains portions of Rails (activesupport/inflector.rb, 
     156activesupport/inflections.rb) which are licensed as follows: 
     157 
     158Copyright (c) 2004-2006 David Heinemeier Hansson 
     159 
     160Permission is hereby granted, free of charge, to any person obtaining 
     161a copy of this software and associated documentation files (the 
     162"Software"), to deal in the Software without restriction, including 
     163without limitation the rights to use, copy, modify, merge, publish, 
     164distribute, sublicense, and/or sell copies of the Software, and to 
     165permit persons to whom the Software is furnished to do so, subject to 
     166the following conditions: 
     167 
     168The above copyright notice and this permission notice shall be 
     169included in all copies or substantial portions of the Software. 
     170 
     171THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
     172EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
     173MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
     174NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
     175LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
     176OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
     177WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.Copyright (c) 2004-2006 David Heinemeier Hansson 
     178 
  • trunk/Rakefile

    r6138 r6772  
    77CLEAN.include('pkg') 
    88 
    9 FILES     = FileList['lib/**/*', 'test/*.rb', 'classloader/*', 'LICENSE', 'TODO', 'CHANGES', 'README'] 
     9FILES     = FileList['lib/**/*', 'test/*.rb', 'classloader/*', 'sql_reserved_words/*', 'LICENSE', 'TODO', 'CHANGES', 'README'] 
    1010FULLFILES = FILES.clone.include('buildsupport/**/*', 'example/**/*' ) 
    1111TESTFILES = FileList['test/test_java_helper.rb'] 
  • trunk/example/Rakefile

    r6145 r6772  
    103103end 
    104104 
     105desc "validate schema success" 
     106Jerbil::Hibernate::ExportSchemaTask.new(:validate_schema_success) do |t| 
     107  t.schemafile = DB_SCHEMA 
     108  t.entities_yml = ENTITIES_YML 
     109        t.validate :all 
     110  t.filter { |classname| classname =~ /^jerbil\.example\.JerbilEntity/ } 
     111   
     112  # not used, just for testing 
     113  t.inflections { |inflect| inflect.irregular( 'data' , 'data') } 
     114end 
     115 
     116desc "validate schema failure" 
     117Jerbil::Hibernate::ExportSchemaTask.new(:validate_schema_failure) do |t| 
     118  t.schemafile = DB_SCHEMA 
     119  t.entities_yml = ENTITIES_YML 
     120        t.validate :all 
     121  t.filter { |classname| classname =~ /^jerbil\.example\.EntityWithValidationErrors/ }  
     122end 
     123 
    105124Jerbil::Hibernate::ExportSchemaTask.new(:export_schema_filtered) do |t| 
    106125  t.schemafile = DB_SCHEMA 
  • trunk/example/src/jerbil/example/JerbilEntity.java

    r6174 r6772  
    33import org.hibernate.annotations.Index; 
    44 
     5 
    56import javax.persistence.Entity; 
     7import javax.persistence.Column; 
    68import javax.persistence.Id; 
    79import javax.persistence.Table; 
     
    1517 
    1618    private Long id; 
    17     private String name; 
     19    private String name, lastName; 
     20 
     21                private boolean primary; 
    1822 
    1923    @Id 
     
    3438        this.name = name; 
    3539    } 
     40 
     41                // not activerecord conformant, but reserved 
     42                @Column(name = "is_primary" ) 
     43                public boolean isPrimary() { 
     44                        return primary; 
     45                } 
     46 
     47                public void setPrimary(boolean b) { 
     48                        primary = b; 
     49                } 
     50 
     51                @Column(name = "last_name") 
     52                public String getLastName() { 
     53                        return lastName; 
     54                } 
     55 
     56                public void setLastName(String n) { 
     57                        this.lastName = n; 
     58                } 
    3659} 
  • trunk/lib/jerbil/hibernate_task.rb

    r6298 r6772  
    22require 'rake/tasklib' 
    33require 'jerbil/java_helper' 
     4require 'jerbil/inflector' 
    45require 'yaml' 
    56require 'set' 
     
    1112    # gather a list of entities which then gets serialized to a YAML file. 
    1213    # ExportSchemaTask then reads this file and uses Hibernate's schema exporting  
    13     # features to generate SQL 
     14    # features to generate SQL. Optionally the schema can be validated, to check  
     15    # whether any column or table names are reserved keywords (validate :sql) or 
     16    # conform to ActiveRecord convention (validate :rails). 
    1417    # 
    1518    # == Example 
    1619    #   Jerbil::Hibernate::ExportSchemaTask.new(:export_schema) do |t| 
    1720    #       t.schemafile = "schema.sql" 
    18     #       t.entities_yml = ENTITIES_YML       
     21    #       t.entities_yml = ENTITIES_YML    
     22    #       t.validate = :all 
    1923    #   end 
    2024    class ExportSchemaTask < Rake::TaskLib 
     
    4751       
    4852      # Pretty printing of generated SQL (default: true) 
    49       attr_accessor :prettyprint 
     53      attr_accessor :prettyprint             
    5054       
    5155      def initialize(name=:export_schema) 
     
    5458        @classfilter = nil 
    5559        @prettyprint = true 
     60        @validate = [] 
    5661        @schemafile = "schema.sql" 
    5762        @entities_yml = "entities.yml" 
    5863        @properties_yml = nil 
    5964        @dialect = "org.hibernate.dialect.MySQL5Dialect" 
     65        @sql_reserved = nil 
    6066                 
    6167        yield self if block_given? 
    6268        define 
    63       end 
     69      end            
    6470       
    6571      def define # :nodoc: 
     
    7783 
    7884          cfg = get_config(entity_classes, properties, package) 
     85           
     86          validate_config(cfg) unless validate.empty? 
    7987          sql = cfg.generateSchemaCreationScript(Rjb::import(dialect).new) 
    8088           
     
    104112      def filter(*args, &block) 
    105113          @classfilter = block 
     114      end 
     115       
     116      # Validate configuration (options: :all, :sql, :rails) 
     117      def validate(*what) 
     118        @validate.concat(what) 
     119      end 
     120       
     121      # Exposes the inflections instance so exceptions can be registered. 
     122      def inflections 
     123        yield Inflector::Inflections.instance if block_given? 
    106124      end 
    107125         
     
    130148      def format(sql) 
    131149        Rjb::import('org.hibernate.pretty.DDLFormatter').new(sql).format       
    132       end 
    133     end 
    134   end 
     150      end        
     151 
     152      def reserved_words 
     153        if @sql_reserved.nil? 
     154          @sql_reserved = {}           
     155          Dir[File.join(File.dirname(__FILE__), "..", "..", "sql_reserved_words", "**")].each do |file| 
     156            @sql_reserved[File.basename(file)] = File.readlines(file).map{|s|s.chomp} 
     157          end 
     158        end 
     159        @sql_reserved 
     160      end 
     161 
     162      def check_reserved_word(word) 
     163        offending_dialects = [] 
     164              reserved_words.each do |dialect, wordlist| 
     165                      offending_dialects << dialect.to_s if wordlist.include?(word.upcase)       
     166        end 
     167              offending_dialects 
     168      end 
     169 
     170      def validate?(what) 
     171        @validate.include?(:all) || @validate.include?(what) 
     172      end 
     173       
     174      def validate_config(cfg) 
     175        cfg.buildMappings 
     176        class_mappings = cfg.getClassMappings 
     177 
     178        invalid_tables  = [] 
     179        invalid_columns = [] 
     180        invalid_words   = [] 
     181                
     182        while class_mappings.hasNext          
     183          cmap = class_mappings.next 
     184         
     185          simple_name = cmap.getMappedClass.getSimpleName 
     186          table_name  = cmap.getTable.getName                  
     187             
     188          #only check table semantics for toplevel classes 
     189          if cmap.getRootClass.equals(cmap)                                
     190            if validate?(:sql) 
     191              dialect_probs = check_reserved_word(table_name)         
     192              unless dialect_probs.empty? 
     193                $stderr << "[#{simple_name}] table name '#{table_name}' is a reserved keyword in dialects: #{dialect_probs.join(', ')}\n" if verbose 
     194                invalid_words << table_name 
     195              end 
     196            end 
     197         
     198            if validate?(:rails) 
     199              expected_table_name = Inflector::tableize(simple_name).sub(/^hibernate_/, '') 
     200              if expected_table_name != table_name && check_reserved_word(expected_table_name).empty? 
     201                $stderr << "[#{simple_name}] invalid table: '#{table_name}', should be '#{expected_table_name}'\n" if verbose 
     202                invalid_tables << table_name 
     203              end 
     204            end 
     205          end 
     206         
     207          # column checks 
     208          property_it = cmap.getPropertyIterator 
     209          while property_it.hasNext 
     210            prop = property_it.next 
     211            prop_name = prop.getName 
     212         
     213            column_it = prop.getColumnIterator 
     214            next unless column_it.hasNext 
     215         
     216            col_name = column_it.next.getName 
     217         
     218            if validate?(:sql) 
     219              dialect_probs = check_reserved_word(col_name) 
     220              unless dialect_probs.empty? 
     221                $stderr << "[#{simple_name}] column name '#{col_name}' in '#{table_name}' is a reserved keyword in dialects: #{dialect_probs.join(', ')}\n" if verbose 
     222                invalid_words << col_name 
     223              end 
     224            end 
     225         
     226            #ignore association types for now (TODO) 
     227            next if prop.getType.isAssociationType 
     228         
     229            if validate?(:rails) 
     230              expected_col_name = Inflector::underscore(prop_name) 
     231           
     232              if expected_col_name != col_name && check_reserved_word(expected_col_name).empty? 
     233                $stderr << "[#{simple_name}] invalid column: '#{table_name}.#{col_name}', should be '#{expected_col_name}'\n" if verbose 
     234                invalid_columns << col_name 
     235              end 
     236            end 
     237          end 
     238        end 
     239                 
     240        raise "ExportSchemaTask: validation errors, not exporting" if !invalid_tables.empty? || !invalid_columns.empty? || !invalid_words.empty? 
     241      end #validate config 
     242       
     243    end #export schema task 
     244  end # Hibernate 
    135245end 
    136246 
  • trunk/test/test_build.rb

    r6145 r6772  
    8989  end 
    9090   
     91  def test_validate_schema_success 
     92    run_rake_clean(:validate_schema_success) do |ok,res| 
     93      assert ok     
     94      assert File.exists?(DB_SCHEMA) 
     95      #make sure file is not empty 
     96      assert File.size(DB_SCHEMA) >= 200 
     97    end 
     98  end 
     99   
     100  def test_validate_schema_failure 
     101    run_rake_clean(:validate_schema_failure) do |ok,res| 
     102      assert !ok      
     103    end 
     104  end 
     105   
    91106   
    92107  def test_run_no_fork 
  • trunk/test/test_java_helper.rb

    r6133 r6772  
    4444       
    4545      classnames = flist.to_classnames 
    46       assert_equal 5, classnames.length, classnames 
     46      assert_equal 6, classnames.length, classnames 
    4747      assert_equal 1, flist.resources.length, flist.resources 
    4848