ActiveRecord-JDBC 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,208 @@
1
+ module JdbcSpec
2
+ module MsSQL
3
+ module Column
4
+ def simplified_type(field_type)
5
+ case field_type
6
+ when /int|bigint|smallint|tinyint/i then :integer
7
+ when /float|double|decimal|money|numeric|real|smallmoney/i then @scale == 0 ? :integer : :float
8
+ when /datetime|smalldatetime/i then :datetime
9
+ when /timestamp/i then :timestamp
10
+ when /time/i then :time
11
+ when /text|ntext/i then :text
12
+ when /binary|image|varbinary/i then :binary
13
+ when /char|nchar|nvarchar|string|varchar/i then :string
14
+ when /bit/i then :boolean
15
+ when /uniqueidentifier/i then :string
16
+ end
17
+ end
18
+
19
+ def type_cast(value)
20
+ return nil if value.nil? || value =~ /^\s*null\s*$/i
21
+ case type
22
+ when :string then value
23
+ when :integer then value == true || value == false ? value == true ? 1 : 0 : value.to_i
24
+ when :primary_key then value == true || value == false ? value == true ? 1 : 0 : value.to_i
25
+ when :float then value.to_f
26
+ when :datetime then cast_to_datetime(value)
27
+ when :timestamp then cast_to_time(value)
28
+ when :time then cast_to_time(value)
29
+ when :date then cast_to_datetime(value)
30
+ when :boolean then value == true or (value =~ /^t(rue)?$/i) == 0 or value.to_s == '1'
31
+ else value
32
+ end
33
+ end
34
+
35
+ def cast_to_time(value)
36
+ return value if value.is_a?(Time)
37
+ time_array = ParseDate.parsedate(value)
38
+ time_array[0] ||= 2000
39
+ time_array[1] ||= 1
40
+ time_array[2] ||= 1
41
+ Time.send(Base.default_timezone, *time_array) rescue nil
42
+ end
43
+
44
+ def cast_to_datetime(value)
45
+ if value.is_a?(Time)
46
+ if value.year != 0 and value.month != 0 and value.day != 0
47
+ return value
48
+ else
49
+ return Time.mktime(2000, 1, 1, value.hour, value.min, value.sec) rescue nil
50
+ end
51
+ end
52
+ return cast_to_time(value) if value.is_a?(Date) or value.is_a?(String) rescue nil
53
+ value
54
+ end
55
+
56
+ # These methods will only allow the adapter to insert binary data with a length of 7K or less
57
+ # because of a SQL Server statement length policy.
58
+ def self.string_to_binary(value)
59
+ value.gsub(/(\r|\n|\0|\x1a)/) do
60
+ case $1
61
+ when "\r" then "%00"
62
+ when "\n" then "%01"
63
+ when "\0" then "%02"
64
+ when "\x1a" then "%03"
65
+ end
66
+ end
67
+ end
68
+
69
+ def self.binary_to_string(value)
70
+ value.gsub(/(%00|%01|%02|%03)/) do
71
+ case $1
72
+ when "%00" then "\r"
73
+ when "%01" then "\n"
74
+ when "%02\0" then "\0"
75
+ when "%03" then "\x1a"
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ def modify_types(tp)
82
+ tp[:primary_key] = "int NOT NULL IDENTITY(1, 1) PRIMARY KEY"
83
+ tp[:integer][:limit] = nil
84
+ tp[:boolean][:limit] = nil
85
+ tp[:binary] = { :name => "image"}
86
+ tp
87
+ end
88
+
89
+ def quote(value, column = nil)
90
+ if column && column.type == :primary_key
91
+ return value.to_s
92
+ end
93
+ case value
94
+ when String
95
+ if column && column.type == :binary && column.class.respond_to?(:string_to_binary)
96
+ val = quote_string(column.class.string_to_binary(value))
97
+ "'#{val}'"
98
+ else
99
+ "'#{quote_string(value)}'"
100
+ end
101
+ when NilClass then "NULL"
102
+ when TrueClass then '1'
103
+ when FalseClass then '0'
104
+ when Float, Fixnum, Bignum then value.to_s
105
+ when Date then "'#{value.to_s}'"
106
+ when Time, DateTime then "'#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
107
+ else "'#{quote_string(value.to_yaml)}'"
108
+ end
109
+ end
110
+
111
+ def quote_string(string)
112
+ string.gsub(/\'/, "''")
113
+ end
114
+
115
+ def quoted_true
116
+ "1"
117
+ end
118
+
119
+ def quoted_false
120
+ "0"
121
+ end
122
+
123
+ def quote_column_name(name)
124
+ "[#{name}]"
125
+ end
126
+
127
+ def add_limit_offset!(sql, options)
128
+ if options[:limit] and options[:offset]
129
+ total_rows = @connection.select_all("SELECT count(*) as TotalRows from (#{sql.gsub(/\bSELECT\b/i, "SELECT TOP 1000000000")}) tally")[0][:TotalRows].to_i
130
+ if (options[:limit] + options[:offset]) >= total_rows
131
+ options[:limit] = (total_rows - options[:offset] >= 0) ? (total_rows - options[:offset]) : 0
132
+ end
133
+ sql.sub!(/^\s*SELECT/i, "SELECT * FROM (SELECT TOP #{options[:limit]} * FROM (SELECT TOP #{options[:limit] + options[:offset]} ")
134
+ sql << ") AS tmp1"
135
+ if options[:order]
136
+ options[:order] = options[:order].split(',').map do |field|
137
+ parts = field.split(" ")
138
+ tc = parts[0]
139
+ if sql =~ /\.\[/ and tc =~ /\./ # if column quoting used in query
140
+ tc.gsub!(/\./, '\\.\\[')
141
+ tc << '\\]'
142
+ end
143
+ if sql =~ /#{tc} AS (t\d_r\d\d?)/
144
+ parts[0] = $1
145
+ end
146
+ parts.join(' ')
147
+ end.join(', ')
148
+ sql << " ORDER BY #{change_order_direction(options[:order])}) AS tmp2 ORDER BY #{options[:order]}"
149
+ else
150
+ sql << " ) AS tmp2"
151
+ end
152
+ elsif sql !~ /^\s*SELECT (@@|COUNT\()/i
153
+ sql.sub!(/^\s*SELECT([\s]*distinct)?/i) do
154
+ "SELECT#{$1} TOP #{options[:limit]}"
155
+ end unless options[:limit].nil?
156
+ end
157
+ end
158
+
159
+ def recreate_database(name)
160
+ drop_database(name)
161
+ create_database(name)
162
+ end
163
+
164
+ def drop_database(name)
165
+ execute "DROP DATABASE #{name}"
166
+ end
167
+
168
+ def create_database(name)
169
+ execute "CREATE DATABASE #{name}"
170
+ end
171
+
172
+ def rename_table(name, new_name)
173
+ execute "EXEC sp_rename '#{name}', '#{new_name}'"
174
+ end
175
+
176
+ def rename_column(table, column, new_column_name)
177
+ execute "EXEC sp_rename '#{table}.#{column}', '#{new_column_name}'"
178
+ end
179
+
180
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
181
+ sql_commands = ["ALTER TABLE #{table_name} ALTER COLUMN #{column_name} #{type_to_sql(type, options[:limit])}"]
182
+ if options[:default]
183
+ remove_default_constraint(table_name, column_name)
184
+ sql_commands << "ALTER TABLE #{table_name} ADD CONSTRAINT DF_#{table_name}_#{column_name} DEFAULT #{options[:default]} FOR #{column_name}"
185
+ end
186
+ sql_commands.each {|c|
187
+ execute(c)
188
+ }
189
+ end
190
+
191
+ def remove_column(table_name, column_name)
192
+ remove_default_constraint(table_name, column_name)
193
+ execute "ALTER TABLE #{table_name} DROP COLUMN #{column_name}"
194
+ end
195
+
196
+ def remove_default_constraint(table_name, column_name)
197
+ defaults = select "select def.name from sysobjects def, syscolumns col, sysobjects tab where col.cdefault = def.id and col.name = '#{column_name}' and tab.name = '#{table_name}' and col.id = tab.id"
198
+ defaults.each {|constraint|
199
+ execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["name"]}"
200
+ }
201
+ end
202
+
203
+ def remove_index(table_name, options = {})
204
+ execute "DROP INDEX #{table_name}.#{index_name(table_name, options)}"
205
+ end
206
+ end
207
+ end
208
+
@@ -0,0 +1,119 @@
1
+ module JdbcSpec
2
+ module MySQL
3
+ def modify_types(tp)
4
+ tp[:primary_key] = "int(11) DEFAULT NULL auto_increment PRIMARY KEY"
5
+ tp[:decimal] = { :name => "decimal" }
6
+ tp
7
+ end
8
+
9
+ # QUOTING ==================================================
10
+
11
+ def quote(value, column = nil)
12
+ if column && column.type == :primary_key
13
+ return value.to_s
14
+ end
15
+ super
16
+ end
17
+
18
+ def quote_column_name(name) #:nodoc:
19
+ "`#{name}`"
20
+ end
21
+
22
+ # from active_record/vendor/mysql.rb
23
+ def quote_string(str) #:nodoc:
24
+ str.gsub(/([\0\n\r\032\'\"\\])/) do
25
+ case $1
26
+ when "\0" then "\\0"
27
+ when "\n" then "\\n"
28
+ when "\r" then "\\r"
29
+ when "\032" then "\\Z"
30
+ else "\\"+$1
31
+ end
32
+ end
33
+ end
34
+
35
+ def quoted_true
36
+ "1"
37
+ end
38
+
39
+ def quoted_false
40
+ "0"
41
+ end
42
+
43
+ # SCHEMA STATEMENTS ========================================
44
+
45
+ def structure_dump #:nodoc:
46
+ if supports_views?
47
+ sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
48
+ else
49
+ sql = "SHOW TABLES"
50
+ end
51
+
52
+ select_all(sql).inject("") do |structure, table|
53
+ table.delete('Table_type')
54
+ structure += select_one("SHOW CREATE TABLE #{table.to_a.first.last}")["Create Table"] + ";\n\n"
55
+ end
56
+ end
57
+
58
+ def recreate_database(name) #:nodoc:
59
+ drop_database(name)
60
+ create_database(name)
61
+ end
62
+
63
+ def create_database(name) #:nodoc:
64
+ execute "CREATE DATABASE `#{name}`"
65
+ end
66
+
67
+ def drop_database(name) #:nodoc:
68
+ execute "DROP DATABASE IF EXISTS `#{name}`"
69
+ end
70
+
71
+ def current_database
72
+ select_one("SELECT DATABASE() as db")["db"]
73
+ end
74
+
75
+ def indexes(table_name, name = nil)#:nodoc:
76
+ indexes = []
77
+ current_index = nil
78
+ execute("SHOW KEYS FROM #{table_name}", name).each do |row|
79
+ if current_index != row[2]
80
+ next if row[2] == "PRIMARY" # skip the primary key
81
+ current_index = row[2]
82
+ indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", [])
83
+ end
84
+
85
+ indexes.last.columns << row[4]
86
+ end
87
+ indexes
88
+ end
89
+
90
+ def create_table(name, options = {}) #:nodoc:
91
+ super(name, {:options => "ENGINE=InnoDB"}.merge(options))
92
+ end
93
+
94
+ def rename_table(name, new_name)
95
+ execute "RENAME TABLE #{name} TO #{new_name}"
96
+ end
97
+
98
+ def change_column_default(table_name, column_name, default) #:nodoc:
99
+ current_type = select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["type"]
100
+
101
+ type, limit = native_sql_to_type(current_type)
102
+
103
+ change_column(table_name, column_name, type, { :default => default, :limit => limit })
104
+ end
105
+
106
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
107
+ options[:default] ||= select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["default"]
108
+
109
+ change_column_sql = "ALTER TABLE #{table_name} CHANGE #{column_name} #{column_name} #{type_to_sql(type, options[:limit])}"
110
+ add_column_options!(change_column_sql, options)
111
+ execute(change_column_sql)
112
+ end
113
+
114
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
115
+ current_type = select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["type"]
116
+ execute "ALTER TABLE #{table_name} CHANGE #{column_name} #{new_column_name} #{current_type}"
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,237 @@
1
+ module JdbcSpec
2
+ module Oracle
3
+ module Column
4
+ def type_cast(value)
5
+ return nil if value.nil? || value =~ /^\s*null\s*$/i
6
+ case type
7
+ when :string then value
8
+ when :integer then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
9
+ when :primary_key then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
10
+ when :float then value.to_f
11
+ when :datetime then cast_to_date_or_time(value)
12
+ when :time then cast_to_time(value)
13
+ else value
14
+ end
15
+ end
16
+
17
+ private
18
+ def simplified_type(field_type)
19
+ case field_type
20
+ when /char/i : :string
21
+ when /num|float|double|dec|real|int/i : @scale == 0 ? :integer : :float
22
+ when /date|time/i : @name =~ /_at$/ ? :time : :datetime
23
+ when /clob/i : :text
24
+ when /blob/i : :binary
25
+ end
26
+ end
27
+
28
+ def cast_to_date_or_time(value)
29
+ return value if value.is_a? Date
30
+ return nil if value.blank?
31
+ guess_date_or_time (value.is_a? Time) ? value : cast_to_time(value)
32
+ end
33
+
34
+ def cast_to_time(value)
35
+ return value if value.is_a? Time
36
+ time_array = ParseDate.parsedate value
37
+ time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1;
38
+ Time.send(Base.default_timezone, *time_array) rescue nil
39
+ end
40
+
41
+ def guess_date_or_time(value)
42
+ (value.hour == 0 and value.min == 0 and value.sec == 0) ?
43
+ Date.new(value.year, value.month, value.day) : value
44
+ end
45
+ end
46
+
47
+ def default_sequence_name(table, column) #:nodoc:
48
+ "#{table}_seq"
49
+ end
50
+
51
+ def create_table(name, options = {}) #:nodoc:
52
+ super(name, options)
53
+ execute "CREATE SEQUENCE #{name}_seq START WITH 10000" unless options[:id] == false
54
+ end
55
+
56
+ def rename_table(name, new_name) #:nodoc:
57
+ execute "RENAME #{name} TO #{new_name}"
58
+ execute "RENAME #{name}_seq TO #{new_name}_seq" rescue nil
59
+ end
60
+
61
+ def drop_table(name) #:nodoc:
62
+ super(name)
63
+ execute "DROP SEQUENCE #{name}_seq" rescue nil
64
+ end
65
+
66
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
67
+ if pk.nil? # Who called us? What does the sql look like? No idea!
68
+ execute sql, name
69
+ elsif id_value # Pre-assigned id
70
+ log(sql, name) { @connection.execute_insert sql,pk }
71
+ else # Assume the sql contains a bind-variable for the id
72
+ id_value = select_one("select #{sequence_name}.nextval id from dual")['id']
73
+ log(sql, name) {
74
+ execute_prepared_insert(sql,id_value)
75
+ }
76
+ end
77
+ id_value
78
+ end
79
+
80
+ def execute_prepared_insert(sql, id)
81
+ @stmts ||= {}
82
+ @stmts[sql] ||= @connection.ps(sql)
83
+ stmt = @stmts[sql]
84
+ stmt.setLong(1,id)
85
+ stmt.executeUpdate
86
+ id
87
+ end
88
+
89
+ def modify_types(tp)
90
+ tp[:primary_key] = "NUMBER(38) NOT NULL PRIMARY KEY"
91
+ tp[:datetime] = { :name => "DATE" }
92
+ tp[:timestamp] = { :name => "DATE" }
93
+ tp[:time] = { :name => "DATE" }
94
+ tp[:date] = { :name => "DATE" }
95
+ tp
96
+ end
97
+
98
+ def add_limit_offset!(sql, options) #:nodoc:
99
+ offset = options[:offset] || 0
100
+
101
+ if limit = options[:limit]
102
+ sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_ where rownum <= #{offset+limit}) where raw_rnum_ > #{offset}"
103
+ elsif offset > 0
104
+ sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_) where raw_rnum_ > #{offset}"
105
+ end
106
+ end
107
+
108
+ def current_database #:nodoc:
109
+ select_one("select sys_context('userenv','db_name') db from dual")["db"]
110
+ end
111
+
112
+ def indexes(table_name, name = nil) #:nodoc:
113
+ result = select_all(<<-SQL, name)
114
+ SELECT lower(i.index_name) as index_name, i.uniqueness, lower(c.column_name) as column_name
115
+ FROM user_indexes i, user_ind_columns c
116
+ WHERE i.table_name = '#{table_name.to_s.upcase}'
117
+ AND c.index_name = i.index_name
118
+ AND i.index_name NOT IN (SELECT index_name FROM user_constraints WHERE constraint_type = 'P')
119
+ ORDER BY i.index_name, c.column_position
120
+ SQL
121
+
122
+ current_index = nil
123
+ indexes = []
124
+
125
+ result.each do |row|
126
+ if current_index != row['index_name']
127
+ indexes << IndexDefinition.new(table_name, row['index_name'], row['uniqueness'] == "UNIQUE", [])
128
+ current_index = row['index_name']
129
+ end
130
+
131
+ indexes.last.columns << row['column_name']
132
+ end
133
+
134
+ indexes
135
+ end
136
+
137
+ def remove_index(table_name, options = {}) #:nodoc:
138
+ execute "DROP INDEX #{index_name(table_name, options)}"
139
+ end
140
+
141
+ def change_column_default(table_name, column_name, default) #:nodoc:
142
+ execute "ALTER TABLE #{table_name} MODIFY #{column_name} DEFAULT #{quote(default)}"
143
+ end
144
+
145
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
146
+ change_column_sql = "ALTER TABLE #{table_name} MODIFY #{column_name} #{type_to_sql(type, options[:limit])}"
147
+ add_column_options!(change_column_sql, options)
148
+ execute(change_column_sql)
149
+ end
150
+
151
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
152
+ execute "ALTER TABLE #{table_name} RENAME COLUMN #{column_name} to #{new_column_name}"
153
+ end
154
+
155
+ def remove_column(table_name, column_name) #:nodoc:
156
+ execute "ALTER TABLE #{table_name} DROP COLUMN #{column_name}"
157
+ end
158
+
159
+ def structure_dump #:nodoc:
160
+ s = select_all("select sequence_name from user_sequences").inject("") do |structure, seq|
161
+ structure << "create sequence #{seq.to_a.first.last};\n\n"
162
+ end
163
+
164
+ select_all("select table_name from user_tables").inject(s) do |structure, table|
165
+ ddl = "create table #{table.to_a.first.last} (\n "
166
+ cols = select_all(%Q{
167
+ select column_name, data_type, data_length, data_precision, data_scale, data_default, nullable
168
+ from user_tab_columns
169
+ where table_name = '#{table.to_a.first.last}'
170
+ order by column_id
171
+ }).map do |row|
172
+ col = "#{row['column_name'].downcase} #{row['data_type'].downcase}"
173
+ if row['data_type'] =='NUMBER' and !row['data_precision'].nil?
174
+ col << "(#{row['data_precision'].to_i}"
175
+ col << ",#{row['data_scale'].to_i}" if !row['data_scale'].nil?
176
+ col << ')'
177
+ elsif row['data_type'].include?('CHAR')
178
+ col << "(#{row['data_length'].to_i})"
179
+ end
180
+ col << " default #{row['data_default']}" if !row['data_default'].nil?
181
+ col << ' not null' if row['nullable'] == 'N'
182
+ col
183
+ end
184
+ ddl << cols.join(",\n ")
185
+ ddl << ");\n\n"
186
+ structure << ddl
187
+ end
188
+ end
189
+
190
+ def structure_drop #:nodoc:
191
+ s = select_all("select sequence_name from user_sequences").inject("") do |drop, seq|
192
+ drop << "drop sequence #{seq.to_a.first.last};\n\n"
193
+ end
194
+
195
+ select_all("select table_name from user_tables").inject(s) do |drop, table|
196
+ drop << "drop table #{table.to_a.first.last} cascade constraints;\n\n"
197
+ end
198
+ end
199
+
200
+ # QUOTING ==================================================
201
+ #
202
+ # see: abstract/quoting.rb
203
+
204
+ # camelCase column names need to be quoted; not that anyone using Oracle
205
+ # would really do this, but handling this case means we pass the test...
206
+ def quote_column_name(name) #:nodoc:
207
+ name =~ /[A-Z]/ ? "\"#{name}\"" : name
208
+ end
209
+
210
+ def quote_string(string) #:nodoc:
211
+ string.gsub(/'/, "''")
212
+ end
213
+
214
+ def quote(value, column = nil) #:nodoc:
215
+ if column && column.type == :binary
216
+ if /(.*?)\([0-9]+\)/ =~ column.sql_type
217
+ %Q{empty_#{ $1 }()}
218
+ else
219
+ %Q{empty_#{ column.sql_type rescue 'blob' }()}
220
+ end
221
+ else
222
+ if column && column.type == :primary_key
223
+ return value.to_s
224
+ end
225
+ case value
226
+ when String : %Q{'#{quote_string(value)}'}
227
+ when NilClass : 'null'
228
+ when TrueClass : '1'
229
+ when FalseClass : '0'
230
+ when Numeric : value.to_s
231
+ when Date, Time : %Q{TIMESTAMP'#{value.strftime("%Y-%m-%d %H:%M:%S")}'}
232
+ else %Q{'#{quote_string(value.to_yaml)}'}
233
+ end
234
+ end
235
+ end
236
+ end
237
+ end