@@ -7,9 +7,7 @@ class Base
77
88 def self . sqlserver_connection ( config ) #:nodoc:
99 require_library_or_gem 'dbi' unless self . class . const_defined? ( :DBI )
10-
1110 config = config . symbolize_keys
12-
1311 mode = config [ :mode ] ? config [ :mode ] . to_s . upcase : 'ADO'
1412 username = config [ :username ] ? config [ :username ] . to_s : 'sa'
1513 password = config [ :password ] ? config [ :password ] . to_s : ''
@@ -107,44 +105,23 @@ module ConnectionAdapters
107105
108106 class SQLServerColumn < Column #:nodoc:
109107
110- attr_reader :identity , :is_special , :is_utf8
111-
112- def initialize ( info )
113- if info [ :type ] =~ /numeric|decimal/i
114- type = "#{ info [ :type ] } (#{ info [ :numeric_precision ] } ,#{ info [ :numeric_scale ] } )"
115- else
116- type = "#{ info [ :type ] } (#{ info [ :length ] } )"
117- end
118- super ( info [ :name ] , info [ :default_value ] , type , info [ :is_nullable ] == 1 )
119- @identity = info [ :is_identity ]
120- # TODO: Not sure if these should also be special: varbinary(max), nchar, nvarchar(max)
121- @is_special = [ "text" , "ntext" , "image" ] . include? ( info [ :type ] )
122- # Added nchar and nvarchar(max) for unicode types
123- # http://www.teratrax.com/sql_guide/data_types/sql_server_data_types.html
124- @is_utf8 = type =~ /nvarchar|ntext|nchar|nvarchar(max)/i
125- # TODO: check ok to remove @scale = scale_value
126- @limit = nil unless limitable? ( type )
108+ def initialize ( name , default , sql_type = nil , null = true , sqlserver_options = { } )
109+ super ( name , default , sql_type , null )
110+ @sqlserver_options = sqlserver_options
111+ @limit = nil unless limitable?
127112 end
128113
129- def identity ?
130- @identity
114+ def is_identity ?
115+ @sqlserver_options [ :is_identity ]
131116 end
132117
133- def limitable? ( type )
134- # SQL Server only supports limits on *char and float types
135- # although for schema dumping purposes it's useful to know that (big|small)int are 2|8 respectively.
136- @type == :float || @type == :string || ( @type == :integer && type =~ /^(big|small)int/ )
118+ def is_special?
119+ # TODO: Not sure if these should be added: varbinary(max), nchar, nvarchar(max)
120+ sql_type =~ /^text|ntext|image$/
137121 end
138-
139- def simplified_type ( field_type )
140- case field_type
141- when /real/i then :float
142- when /money/i then :decimal
143- when /image/i then :binary
144- when /bit/i then :boolean
145- when /uniqueidentifier/i then :string
146- else super
147- end
122+
123+ def is_utf8?
124+ sql_type =~ /nvarchar|ntext|nchar|nvarchar(max)/i
148125 end
149126
150127 def type_cast ( value )
@@ -168,7 +145,6 @@ def type_cast_code(var_name)
168145 end
169146 end
170147
171-
172148 class << self
173149
174150 def cast_to_datetime ( value )
@@ -217,6 +193,25 @@ def new_time(year, mon, mday, hour, min, sec, microsec = 0)
217193
218194 end #class << self
219195
196+ private
197+
198+ def limitable?
199+ # SQL Server only supports limits on *char and float types. Although for schema dumping purposes
200+ # it is useful to know what others are like bigint and smallint. So we trump #extract_limit.
201+ [ :float , :string ] . include? ( type ) || ( type == :integer && sql_type =~ /^(big|small)int/i )
202+ end
203+
204+ def simplified_type ( field_type )
205+ case field_type
206+ when /real/i then :float
207+ when /money/i then :decimal
208+ when /image/i then :binary
209+ when /bit/i then :boolean
210+ when /uniqueidentifier/i then :string
211+ else super
212+ end
213+ end
214+
220215 end #SQLServerColumn
221216
222217 # In ADO mode, this adapter will ONLY work on Windows systems,
@@ -582,52 +577,14 @@ def columns(table_name, name = nil)
582577 table_name = table_names [ -1 ]
583578 table_name = table_name . gsub ( /[\[ \] ]/ , '' )
584579 db_name = "#{ table_names [ 0 ] } ." if table_names . length ==3
585-
586- # COL_LENGTH returns values that do not reflect how much data can be stored in certain data types.
587- # COL_LENGTH returns -1 for varchar(max), nvarchar(max), and varbinary(max)
588- # COL_LENGTH returns 16 for ntext, text, image types
589- # My sessions.data column was varchar(max) and resulted in the following error:
590- # Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data.
591- sql = %{
592- SELECT
593- columns.COLUMN_NAME as name,
594- columns.DATA_TYPE as type,
595- CASE
596- WHEN columns.COLUMN_DEFAULT = '(null)' OR columns.COLUMN_DEFAULT = '(NULL)' THEN NULL
597- ELSE columns.COLUMN_DEFAULT
598- END default_value,
599- columns.NUMERIC_SCALE as numeric_scale,
600- columns.NUMERIC_PRECISION as numeric_precision,
601- CASE
602- WHEN columns.DATA_TYPE IN ('nvarchar') AND COL_LENGTH(columns.TABLE_NAME, columns.COLUMN_NAME) = -1 THEN 1073741823
603- WHEN columns.DATA_TYPE IN ('varchar', 'varbinary') AND COL_LENGTH(columns.TABLE_NAME, columns.COLUMN_NAME) = -1 THEN 2147483647
604- WHEN columns.DATA_TYPE IN ('ntext') AND COL_LENGTH(columns.TABLE_NAME, columns.COLUMN_NAME) = 16 THEN 1073741823
605- WHEN columns.DATA_TYPE IN ('text', 'image') AND COL_LENGTH(columns.TABLE_NAME, columns.COLUMN_NAME) = 16 THEN 2147483647
606- ELSE COL_LENGTH(columns.TABLE_NAME, columns.COLUMN_NAME)
607- END as length,
608- CASE
609- WHEN columns.IS_NULLABLE = 'YES' THEN 1
610- ELSE NULL
611- end is_nullable,
612- CASE
613- WHEN COLUMNPROPERTY(OBJECT_ID(columns.TABLE_NAME), columns.COLUMN_NAME, 'IsIdentity') = 0 THEN NULL
614- ELSE 1
615- END is_identity
616- FROM #{ db_name } INFORMATION_SCHEMA.COLUMNS columns
617- WHERE columns.TABLE_NAME = '#{ table_name } '
618- ORDER BY columns.ordinal_position
619- } . gsub ( /[ \t \r \n ]+/ , ' ' )
620- result = select ( sql , name , true )
621- result . collect do |column_info |
622- # Remove brackets and outer quotes (if quoted) of default value returned by db, i.e:
623- # "(1)" => "1", "('1')" => "1", "((-1))" => "-1", "('(-1)')" => "(-1)"
624- # Unicode strings will be prefixed with an N. Remove that too.
625- column_info . symbolize_keys!
626- column_info [ :default_value ] = column_info [ :default_value ] . match ( /\A \( +N?'?(.*?)'?\) +\Z / ) [ 1 ] if column_info [ :default_value ]
627- SQLServerColumn . new ( column_info )
580+ column_definitions ( table_name , db_name ) . collect do |ci |
581+ sqlserver_options = ci . except ( :name , :default_value , :type , :null )
582+ SQLServerColumn . new ci [ :name ] , ci [ :default_value ] , ci [ :type ] , ci [ :null ] , sqlserver_options
628583 end
629584 end
630585
586+
587+
631588 def rename_table ( name , new_name )
632589 execute "EXEC sp_rename '#{ name } ', '#{ new_name } '"
633590 end
@@ -898,13 +855,60 @@ def identity_column(table_name)
898855 @table_columns ||= { }
899856 @table_columns [ table_name ] = columns ( table_name ) if @table_columns [ table_name ] == nil
900857 @table_columns [ table_name ] . each do |col |
901- return col . name if col . identity ?
858+ return col . name if col . is_identity ?
902859 end
903860 return nil
904861 end
905862
906863 # SQL UTILITY METHODS ======================================#
907864
865+ def column_definitions ( table_name , db_name = nil )
866+ # COL_LENGTH returns values that do not reflect how much data can be stored in certain data types.
867+ # COL_LENGTH returns -1 for varchar(max), nvarchar(max), and varbinary(max)
868+ # COL_LENGTH returns 16 for ntext, text, image types
869+ sql = %{
870+ SELECT
871+ columns.COLUMN_NAME as name,
872+ columns.DATA_TYPE as type,
873+ CASE
874+ WHEN columns.COLUMN_DEFAULT = '(null)' OR columns.COLUMN_DEFAULT = '(NULL)' THEN NULL
875+ ELSE columns.COLUMN_DEFAULT
876+ END as default_value,
877+ columns.NUMERIC_SCALE as numeric_scale,
878+ columns.NUMERIC_PRECISION as numeric_precision,
879+ CASE
880+ WHEN columns.DATA_TYPE IN ('nvarchar') AND COL_LENGTH(columns.TABLE_NAME, columns.COLUMN_NAME) = -1 THEN 1073741823
881+ WHEN columns.DATA_TYPE IN ('varchar', 'varbinary') AND COL_LENGTH(columns.TABLE_NAME, columns.COLUMN_NAME) = -1 THEN 2147483647
882+ WHEN columns.DATA_TYPE IN ('ntext') AND COL_LENGTH(columns.TABLE_NAME, columns.COLUMN_NAME) = 16 THEN 1073741823
883+ WHEN columns.DATA_TYPE IN ('text', 'image') AND COL_LENGTH(columns.TABLE_NAME, columns.COLUMN_NAME) = 16 THEN 2147483647
884+ ELSE COL_LENGTH(columns.TABLE_NAME, columns.COLUMN_NAME)
885+ END as length,
886+ CASE
887+ WHEN columns.IS_NULLABLE = 'YES' THEN 1
888+ ELSE NULL
889+ end as is_nullable,
890+ CASE
891+ WHEN COLUMNPROPERTY(OBJECT_ID(columns.TABLE_NAME), columns.COLUMN_NAME, 'IsIdentity') = 0 THEN NULL
892+ ELSE 1
893+ END as is_identity
894+ FROM #{ db_name } INFORMATION_SCHEMA.COLUMNS columns
895+ WHERE columns.TABLE_NAME = '#{ table_name } '
896+ ORDER BY columns.ordinal_position
897+ } . gsub ( /[ \t \r \n ]+/ , ' ' )
898+ results = select ( sql , nil , true )
899+ results . collect do |ci |
900+ ci . symbolize_keys!
901+ ci [ :type ] = if ci [ :type ] =~ /numeric|decimal/i
902+ "#{ ci [ :type ] } (#{ ci [ :numeric_precision ] } ,#{ ci [ :numeric_scale ] } )"
903+ else
904+ "#{ ci [ :type ] } (#{ ci [ :length ] } )"
905+ end
906+ ci [ :default_value ] = ci [ :default_value ] . match ( /\A \( +N?'?(.*?)'?\) +\Z / ) [ 1 ] if ci [ :default_value ]
907+ ci [ :null ] = ci [ :is_nullable ] == 1 ; ci . delete ( :is_nullable )
908+ ci
909+ end
910+ end
911+
908912 def get_table_name ( sql )
909913 if sql =~ /^\s *insert\s +into\s +([^\( \s ]+)\s *|^\s *update\s +([^\( \s ]+)\s */i
910914 $1 || $2
@@ -915,8 +919,10 @@ def get_table_name(sql)
915919 end
916920 end
917921
922+
923+
918924
919-
925+
920926 def change_order_direction ( order )
921927 order . split ( "," ) . collect { |fragment |
922928 case fragment
@@ -932,7 +938,7 @@ def get_special_columns(table_name)
932938 @table_columns ||= { }
933939 @table_columns [ table_name ] ||= columns ( table_name )
934940 @table_columns [ table_name ] . each do |col |
935- special << col . name if col . is_special
941+ special << col . name if col . is_special?
936942 end
937943 special
938944 end
@@ -951,7 +957,7 @@ def get_utf8_columns(table_name)
951957 @table_columns ||= { }
952958 @table_columns [ table_name ] ||= columns ( table_name )
953959 @table_columns [ table_name ] . each do |col |
954- utf8 << col . name if col . is_utf8
960+ utf8 << col . name if col . is_utf8?
955961 end
956962 utf8
957963 end
0 commit comments