ActiveRecord-JDBC 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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