@@ -37,18 +37,16 @@ def indexes(table_name)
37
37
data = select ( "EXEC sp_helpindex #{ quote ( table_name ) } " , "SCHEMA" ) rescue [ ]
38
38
39
39
data . reduce ( [ ] ) do |indexes , index |
40
- index = index . with_indifferent_access
41
-
42
- if index [ :index_description ] . match? ( /primary key/ )
40
+ if index [ 'index_description' ] . match? ( /primary key/ )
43
41
indexes
44
42
else
45
- name = index [ : index_name]
46
- unique = index [ : index_description] . match? ( /unique/ )
43
+ name = index [ ' index_name' ]
44
+ unique = index [ ' index_description' ] . match? ( /unique/ )
47
45
where = select_value ( "SELECT [filter_definition] FROM sys.indexes WHERE name = #{ quote ( name ) } " , "SCHEMA" )
48
46
orders = { }
49
47
columns = [ ]
50
48
51
- index [ : index_keys] . split ( "," ) . each do |column |
49
+ index [ ' index_keys' ] . split ( "," ) . each do |column |
52
50
column . strip!
53
51
54
52
if column . end_with? ( "(-)" )
@@ -480,16 +478,15 @@ def initialize_native_database_types
480
478
end
481
479
482
480
def column_definitions ( table_name )
483
- identifier = database_prefix_identifier ( table_name )
484
- database = identifier . fully_qualified_database_quoted
485
- view_exists = view_exists? ( table_name )
486
- view_tblnm = view_table_name ( table_name ) if view_exists
481
+ identifier = database_prefix_identifier ( table_name )
482
+ database = identifier . fully_qualified_database_quoted
483
+ view_exists = view_exists? ( table_name )
487
484
488
485
if view_exists
489
486
sql = <<~SQL
490
487
SELECT LOWER(c.COLUMN_NAME) AS [name], c.COLUMN_DEFAULT AS [default]
491
488
FROM #{ database } .INFORMATION_SCHEMA.COLUMNS c
492
- WHERE c.TABLE_NAME = #{ quote ( view_tblnm ) }
489
+ WHERE c.TABLE_NAME = #{ quote ( view_table_name ( table_name ) ) }
493
490
SQL
494
491
results = internal_exec_query ( sql , "SCHEMA" )
495
492
default_functions = results . each . with_object ( { } ) { |row , out | out [ row [ "name" ] ] = row [ "default" ] } . compact
@@ -498,71 +495,93 @@ def column_definitions(table_name)
498
495
sql = column_definitions_sql ( database , identifier )
499
496
500
497
binds = [ ]
501
- nv128 = SQLServer ::Type ::UnicodeVarchar . new limit : 128
498
+ nv128 = SQLServer ::Type ::UnicodeVarchar . new ( limit : 128 )
502
499
binds << Relation ::QueryAttribute . new ( "TABLE_NAME" , identifier . object , nv128 )
503
500
binds << Relation ::QueryAttribute . new ( "TABLE_SCHEMA" , identifier . schema , nv128 ) unless identifier . schema . blank?
501
+
504
502
results = internal_exec_query ( sql , "SCHEMA" , binds )
503
+ raise ActiveRecord ::StatementInvalid , "Table '#{ table_name } ' doesn't exist" if results . empty?
505
504
506
505
columns = results . map do |ci |
507
- ci = ci . symbolize_keys
508
- ci [ :_type ] = ci [ :type ]
509
- ci [ :table_name ] = view_tblnm || table_name
510
- ci [ :type ] = case ci [ :type ]
511
- when /^bit|image|text|ntext|datetime$/
512
- ci [ :type ]
513
- when /^datetime2|datetimeoffset$/i
514
- "#{ ci [ :type ] } (#{ ci [ :datetime_precision ] } )"
515
- when /^time$/i
516
- "#{ ci [ :type ] } (#{ ci [ :datetime_precision ] } )"
517
- when /^numeric|decimal$/i
518
- "#{ ci [ :type ] } (#{ ci [ :numeric_precision ] } ,#{ ci [ :numeric_scale ] } )"
519
- when /^float|real$/i
520
- "#{ ci [ :type ] } "
521
- when /^char|nchar|varchar|nvarchar|binary|varbinary|bigint|int|smallint$/
522
- ci [ :length ] . to_i == -1 ? "#{ ci [ :type ] } (max)" : "#{ ci [ :type ] } (#{ ci [ :length ] } )"
523
- else
524
- ci [ :type ]
525
- end
526
- ci [ :default_value ] ,
527
- ci [ :default_function ] = begin
528
- default = ci [ :default_value ]
529
- if default . nil? && view_exists
530
- view_column = views_real_column_name ( table_name , ci [ :name ] ) . downcase
531
- default = default_functions [ view_column ] if view_column . present?
532
- end
533
- case default
534
- when nil
535
- [ nil , nil ]
536
- when /\A \( (\w +\( \) )\) \Z /
537
- default_function = Regexp . last_match [ 1 ]
538
- [ nil , default_function ]
539
- when /\A \( N'(.*)'\) \Z /m
540
- string_literal = SQLServer ::Utils . unquote_string ( Regexp . last_match [ 1 ] )
541
- [ string_literal , nil ]
542
- when /CREATE DEFAULT/mi
543
- [ nil , nil ]
544
- else
545
- type = case ci [ :type ]
546
- when /smallint|int|bigint/ then ci [ :_type ]
547
- else ci [ :type ]
548
- end
549
- value = default . match ( /\A \( (.*)\) \Z /m ) [ 1 ]
550
- value = select_value ( "SELECT CAST(#{ value } AS #{ type } ) AS value" , "SCHEMA" )
551
- [ value , nil ]
552
- end
506
+ col = {
507
+ name : ci [ "name" ] ,
508
+ numeric_scale : ci [ "numeric_scale" ] ,
509
+ numeric_precision : ci [ "numeric_precision" ] ,
510
+ datetime_precision : ci [ "datetime_precision" ] ,
511
+ collation : ci [ "collation" ] ,
512
+ ordinal_position : ci [ "ordinal_position" ] ,
513
+ length : ci [ "length" ]
514
+ }
515
+
516
+ col [ :table_name ] = view_table_name ( table_name ) || table_name
517
+ col [ :type ] = column_type ( ci : ci )
518
+ col [ :default_value ] , col [ :default_function ] = default_value_and_function ( default : ci [ 'default_value' ] ,
519
+ name : ci [ 'name' ] ,
520
+ type : col [ :type ] ,
521
+ original_type : ci [ 'type' ] ,
522
+ view_exists : view_exists ,
523
+ table_name : table_name ,
524
+ default_functions : default_functions )
525
+
526
+ col [ :null ] = ci [ 'is_nullable' ] . to_i == 1
527
+ col [ :is_primary ] = ci [ 'is_primary' ] . to_i == 1
528
+
529
+ if [ true , false ] . include? ( ci [ 'is_identity' ] )
530
+ col [ :is_identity ] = ci [ 'is_identity' ]
531
+ else
532
+ col [ :is_identity ] = ci [ 'is_identity' ] . to_i == 1
553
533
end
554
- ci [ :null ] = ci [ :is_nullable ] . to_i == 1
555
- ci . delete ( :is_nullable )
556
- ci [ :is_primary ] = ci [ :is_primary ] . to_i == 1
557
- ci [ :is_identity ] = ci [ :is_identity ] . to_i == 1 unless [ TrueClass , FalseClass ] . include? ( ci [ :is_identity ] . class )
558
- ci
534
+
535
+ col
559
536
end
560
537
561
- # Since Rails 7, it's expected that all adapter raise error when table doesn't exists.
562
- # I'm not aware of the possibility of tables without columns on SQL Server (postgres have those).
563
- # Raise error if the method return an empty array
564
- columns . tap do |result |
565
- raise ActiveRecord ::StatementInvalid , "Table '#{ table_name } ' doesn't exist" if result . empty?
538
+ columns
539
+ end
540
+
541
+ def default_value_and_function ( default :, name :, type :, original_type :, view_exists :, table_name :, default_functions :)
542
+ if default . nil? && view_exists
543
+ view_column = views_real_column_name ( table_name , name ) . downcase
544
+ default = default_functions [ view_column ] if view_column . present?
545
+ end
546
+
547
+ case default
548
+ when nil
549
+ [ nil , nil ]
550
+ when /\A \( (\w +\( \) )\) \Z /
551
+ default_function = Regexp . last_match [ 1 ]
552
+ [ nil , default_function ]
553
+ when /\A \( N'(.*)'\) \Z /m
554
+ string_literal = SQLServer ::Utils . unquote_string ( Regexp . last_match [ 1 ] )
555
+ [ string_literal , nil ]
556
+ when /CREATE DEFAULT/mi
557
+ [ nil , nil ]
558
+ else
559
+ type = case type
560
+ when /smallint|int|bigint/ then original_type
561
+ else type
562
+ end
563
+ value = default . match ( /\A \( (.*)\) \Z /m ) [ 1 ]
564
+ value = select_value ( "SELECT CAST(#{ value } AS #{ type } ) AS value" , "SCHEMA" )
565
+ [ value , nil ]
566
+ end
567
+ end
568
+
569
+ def column_type ( ci :)
570
+ case ci [ 'type' ]
571
+ when /^bit|image|text|ntext|datetime$/
572
+ ci [ 'type' ]
573
+ when /^datetime2|datetimeoffset$/i
574
+ "#{ ci [ 'type' ] } (#{ ci [ 'datetime_precision' ] } )"
575
+ when /^time$/i
576
+ "#{ ci [ 'type' ] } (#{ ci [ 'datetime_precision' ] } )"
577
+ when /^numeric|decimal$/i
578
+ "#{ ci [ 'type' ] } (#{ ci [ 'numeric_precision' ] } ,#{ ci [ 'numeric_scale' ] } )"
579
+ when /^float|real$/i
580
+ "#{ ci [ 'type' ] } "
581
+ when /^char|nchar|varchar|nvarchar|binary|varbinary|bigint|int|smallint$/
582
+ ci [ 'length' ] . to_i == -1 ? "#{ ci [ 'type' ] } (max)" : "#{ ci [ 'type' ] } (#{ ci [ 'length' ] } )"
583
+ else
584
+ ci [ 'type' ]
566
585
end
567
586
end
568
587
@@ -701,25 +720,26 @@ def lowercase_schema_reflection_sql(node)
701
720
702
721
def view_table_name ( table_name )
703
722
view_info = view_information ( table_name )
704
- view_info ? get_table_name ( view_info [ "VIEW_DEFINITION" ] ) : table_name
723
+ view_info . present? ? get_table_name ( view_info [ "VIEW_DEFINITION" ] ) : table_name
705
724
end
706
725
707
726
def view_information ( table_name )
708
727
@view_information ||= { }
728
+
709
729
@view_information [ table_name ] ||= begin
710
730
identifier = SQLServer ::Utils . extract_identifiers ( table_name )
711
731
information_query_table = identifier . database . present? ? "[#{ identifier . database } ].[INFORMATION_SCHEMA].[VIEWS]" : "[INFORMATION_SCHEMA].[VIEWS]"
712
- view_info = select_one "SELECT * FROM #{ information_query_table } WITH (NOLOCK) WHERE TABLE_NAME = #{ quote ( identifier . object ) } " , "SCHEMA"
713
-
714
- if view_info
715
- view_info = view_info . with_indifferent_access
716
- if view_info [ : VIEW_DEFINITION] . blank? || view_info [ : VIEW_DEFINITION] . length == 4000
717
- view_info [ : VIEW_DEFINITION] = begin
718
- select_values ( "EXEC sp_helptext #{ identifier . object_quoted } " , "SCHEMA" ) . join
719
- rescue
720
- warn "No view definition found, possible permissions problem.\n Please run GRANT VIEW DEFINITION TO your_user;"
721
- nil
722
- end
732
+
733
+ view_info = select_one ( "SELECT * FROM #{ information_query_table } WITH (NOLOCK) WHERE TABLE_NAME = #{ quote ( identifier . object ) } " , "SCHEMA" ) . to_h
734
+
735
+ if view_info . present?
736
+ if view_info [ ' VIEW_DEFINITION' ] . blank? || view_info [ ' VIEW_DEFINITION' ] . length == 4000
737
+ view_info [ ' VIEW_DEFINITION' ] = begin
738
+ select_values ( "EXEC sp_helptext #{ identifier . object_quoted } " , "SCHEMA" ) . join
739
+ rescue
740
+ warn "No view definition found, possible permissions problem.\n Please run GRANT VIEW DEFINITION TO your_user;"
741
+ nil
742
+ end
723
743
end
724
744
end
725
745
@@ -728,8 +748,8 @@ def view_information(table_name)
728
748
end
729
749
730
750
def views_real_column_name ( table_name , column_name )
731
- view_definition = view_information ( table_name ) [ : VIEW_DEFINITION]
732
- return column_name unless view_definition
751
+ view_definition = view_information ( table_name ) [ ' VIEW_DEFINITION' ]
752
+ return column_name if view_definition . blank?
733
753
734
754
# Remove "CREATE VIEW ... AS SELECT ..." and then match the column name.
735
755
match_data = view_definition . sub ( /CREATE\s +VIEW.*AS\s +SELECT\s / , '' ) . match ( /([\w -]*)\s +AS\s +#{ column_name } \W /im )
0 commit comments