activerecord-jdbc-adapter 0.8 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/History.txt +21 -0
  2. data/Manifest.txt +12 -0
  3. data/README.txt +1 -0
  4. data/Rakefile +19 -13
  5. data/lib/active_record/connection_adapters/cachedb_adapter.rb +1 -0
  6. data/lib/active_record/connection_adapters/jdbc_adapter.rb +22 -5
  7. data/lib/active_record/connection_adapters/jdbc_adapter_spec.rb +3 -0
  8. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +13 -0
  9. data/lib/jdbc_adapter/jdbc.rake +32 -29
  10. data/lib/jdbc_adapter/jdbc_adapter_internal.jar +0 -0
  11. data/lib/jdbc_adapter/jdbc_cachedb.rb +33 -0
  12. data/lib/jdbc_adapter/jdbc_db2.rb +96 -7
  13. data/lib/jdbc_adapter/jdbc_derby.rb +35 -28
  14. data/lib/jdbc_adapter/jdbc_mssql.rb +62 -103
  15. data/lib/jdbc_adapter/jdbc_mysql.rb +34 -25
  16. data/lib/jdbc_adapter/jdbc_oracle.rb +7 -7
  17. data/lib/jdbc_adapter/jdbc_postgre.rb +1 -1
  18. data/lib/jdbc_adapter/jdbc_sqlite3.rb +188 -0
  19. data/lib/jdbc_adapter/jdbc_sybase.rb +39 -0
  20. data/lib/jdbc_adapter/tsql_helper.rb +59 -0
  21. data/lib/jdbc_adapter/version.rb +1 -1
  22. data/src/java/jdbc_adapter/JdbcAdapterInternalService.java +66 -40
  23. data/src/java/jdbc_adapter/JdbcMySQLSpec.java +2 -2
  24. data/test/cachedb_simple_test.rb +6 -0
  25. data/test/db/cachedb.rb +9 -0
  26. data/test/db/mssql.rb +9 -0
  27. data/test/db/oracle.rb +22 -2
  28. data/test/db/sqlite3.rb +11 -0
  29. data/test/db2_simple_test.rb +4 -0
  30. data/test/jdbc_common.rb +3 -1
  31. data/test/manualTestDatabase.rb +1 -5
  32. data/test/models/entry.rb +1 -1
  33. data/test/mssql_simple_test.rb +6 -0
  34. data/test/mysql_simple_test.rb +9 -0
  35. data/test/oracle_simple_test.rb +23 -0
  36. data/test/simple.rb +3 -2
  37. data/test/sqlite3_simple_test.rb +6 -0
  38. metadata +14 -2
@@ -26,16 +26,16 @@ module ::JdbcSpec
26
26
  def self.adapter_selector
27
27
  [/mysql/i, lambda {|cfg,adapt| adapt.extend(::JdbcSpec::MySQL)}]
28
28
  end
29
-
29
+
30
30
  def self.extended(adapter)
31
31
  adapter.execute("SET SQL_AUTO_IS_NULL=0")
32
32
  end
33
-
33
+
34
34
  module Column
35
35
  TYPES_ALLOWING_EMPTY_STRING_DEFAULT = Set.new([:binary, :string, :text])
36
36
 
37
37
  def simplified_type(field_type)
38
- return :boolean if field_type =~ /tinyint\(1\)|bit/i
38
+ return :boolean if field_type =~ /tinyint\(1\)|bit/i
39
39
  return :string if field_type =~ /enum/i
40
40
  super
41
41
  end
@@ -44,7 +44,7 @@ module ::JdbcSpec
44
44
  @original_default = default
45
45
  @default = nil if missing_default_forged_as_empty_string?
46
46
  end
47
-
47
+
48
48
  # MySQL misreports NOT NULL column default when none is given.
49
49
  # We can't detect this for columns which may have a legitimate ''
50
50
  # default (string, text, binary) but we can for others (integer,
@@ -56,7 +56,7 @@ module ::JdbcSpec
56
56
  !null && @original_default == '' && !TYPES_ALLOWING_EMPTY_STRING_DEFAULT.include?(type)
57
57
  end
58
58
  end
59
-
59
+
60
60
  def modify_types(tp)
61
61
  tp[:primary_key] = "int(11) DEFAULT NULL auto_increment PRIMARY KEY"
62
62
  tp[:decimal] = { :name => "decimal" }
@@ -64,12 +64,12 @@ module ::JdbcSpec
64
64
  tp[:datetime][:limit] = nil
65
65
  tp
66
66
  end
67
-
67
+
68
68
  # QUOTING ==================================================
69
-
69
+
70
70
  def quote(value, column = nil)
71
71
  return value.quoted_id if value.respond_to?(:quoted_id)
72
-
72
+
73
73
  if column && column.type == :primary_key
74
74
  value.to_s
75
75
  elsif column && String === value && column.type == :binary && column.class.respond_to?(:string_to_binary)
@@ -81,7 +81,7 @@ module ::JdbcSpec
81
81
  super
82
82
  end
83
83
  end
84
-
84
+
85
85
  def quote_column_name(name) #:nodoc:
86
86
  "`#{name}`"
87
87
  end
@@ -89,15 +89,15 @@ module ::JdbcSpec
89
89
  def quote_table_name(name) #:nodoc:
90
90
  quote_column_name(name).gsub('.', '`.`')
91
91
  end
92
-
92
+
93
93
  def quoted_true
94
94
  "1"
95
95
  end
96
-
96
+
97
97
  def quoted_false
98
98
  "0"
99
99
  end
100
-
100
+
101
101
  def begin_db_transaction #:nodoc:
102
102
  @connection.begin
103
103
  rescue Exception
@@ -116,16 +116,25 @@ module ::JdbcSpec
116
116
  # Transactions aren't supported
117
117
  end
118
118
 
119
+ def disable_referential_integrity(&block) #:nodoc:
120
+ old = select_value("SELECT @@FOREIGN_KEY_CHECKS")
121
+ begin
122
+ update("SET FOREIGN_KEY_CHECKS = 0")
123
+ yield
124
+ ensure
125
+ update("SET FOREIGN_KEY_CHECKS = #{old}")
126
+ end
127
+ end
119
128
 
120
129
  # SCHEMA STATEMENTS ========================================
121
-
130
+
122
131
  def structure_dump #:nodoc:
123
132
  if supports_views?
124
133
  sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
125
134
  else
126
135
  sql = "SHOW TABLES"
127
136
  end
128
-
137
+
129
138
  select_all(sql).inject("") do |structure, table|
130
139
  table.delete('Table_type')
131
140
 
@@ -135,15 +144,15 @@ module ::JdbcSpec
135
144
  structure += table + ";\n\n"
136
145
  elsif(view = hash["Create View"])
137
146
  structure += view + ";\n\n"
138
- end
147
+ end
139
148
  end
140
149
  end
141
-
150
+
142
151
  def recreate_database(name) #:nodoc:
143
152
  drop_database(name)
144
153
  create_database(name)
145
154
  end
146
-
155
+
147
156
  def create_database(name, options = {}) #:nodoc:
148
157
  if options[:collation]
149
158
  execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`"
@@ -151,29 +160,29 @@ module ::JdbcSpec
151
160
  execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}`"
152
161
  end
153
162
  end
154
-
163
+
155
164
  def drop_database(name) #:nodoc:
156
165
  execute "DROP DATABASE IF EXISTS `#{name}`"
157
166
  end
158
-
167
+
159
168
  def current_database
160
169
  select_one("SELECT DATABASE() as db")["db"]
161
170
  end
162
-
171
+
163
172
  def create_table(name, options = {}) #:nodoc:
164
173
  super(name, {:options => "ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin"}.merge(options))
165
174
  end
166
-
175
+
167
176
  def rename_table(name, new_name)
168
177
  execute "RENAME TABLE #{quote_table_name(name)} TO #{quote_table_name(new_name)}"
169
- end
170
-
178
+ end
179
+
171
180
  def change_column_default(table_name, column_name, default) #:nodoc:
172
181
  current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
173
182
 
174
183
  execute("ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{current_type} DEFAULT #{quote(default)}")
175
184
  end
176
-
185
+
177
186
  def change_column(table_name, column_name, type, options = {}) #:nodoc:
178
187
  unless options_include_default?(options)
179
188
  if column = columns(table_name).find { |c| c.name == column_name.to_s }
@@ -193,7 +202,7 @@ module ::JdbcSpec
193
202
  current_type = cols["Type"] || cols["COLUMN_TYPE"]
194
203
  execute "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_table_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
195
204
  end
196
-
205
+
197
206
  def add_limit_offset!(sql, options) #:nodoc:
198
207
  if limit = options[:limit]
199
208
  unless offset = options[:offset]
@@ -65,12 +65,7 @@ module ::JdbcSpec
65
65
  else value
66
66
  end
67
67
  end
68
-
69
- def type_cast_code(var_name)
70
- return "JdbcSpec::Oracle::Column.cast_to_date_or_time(#{var_name})" if type == :datetime
71
- super
72
- end
73
-
68
+
74
69
  private
75
70
  def simplified_type(field_type)
76
71
  case field_type
@@ -317,7 +312,12 @@ module ::JdbcSpec
317
312
  return value.to_s
318
313
  end
319
314
  case value
320
- when String, ActiveSupport::Multibyte::Chars : %Q{'#{quote_string(value)}'}
315
+ when String, ActiveSupport::Multibyte::Chars
316
+ if column.type == :datetime
317
+ %Q{TIMESTAMP'#{value}'}
318
+ else
319
+ %Q{'#{quote_string(value)}'}
320
+ end
321
321
  when NilClass : 'null'
322
322
  when TrueClass : '1'
323
323
  when FalseClass : '0'
@@ -165,7 +165,7 @@ module ::JdbcSpec
165
165
  def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
166
166
  execute(sql, name)
167
167
  table = sql.split(" ", 4)[2]
168
- id_value || last_insert_id(table, sequence_name || default_sequence_name(table, pk))
168
+ id_value || pk && last_insert_id(table, sequence_name || default_sequence_name(table, pk))
169
169
  end
170
170
 
171
171
  def columns(table_name, name=nil)
@@ -0,0 +1,188 @@
1
+ module ::JdbcSpec
2
+ module ActiveRecordExtensions
3
+ def sqlite3_connection(config)
4
+ config[:url] ||= "jdbc:sqlite:#{config[:database]}"
5
+ config[:driver] ||= "org.sqlite.JDBC"
6
+ jdbc_connection(config)
7
+ end
8
+ end
9
+
10
+ module SQLite3
11
+ def self.column_selector
12
+ [/sqlite/i, lambda {|cfg,col| col.extend(::JdbcSpec::SQLite3::Column)}]
13
+ end
14
+
15
+ def self.adapter_selector
16
+ [/sqlite/i, lambda {|cfg,adapt| adapt.extend(::JdbcSpec::SQLite3)}]
17
+ end
18
+
19
+ module Column
20
+
21
+ private
22
+ def simplified_type(field_type)
23
+ case field_type
24
+ when /^integer\(1\)$/i then :boolean
25
+ when /text/i then :string
26
+ when /int/i then :integer
27
+ when /real/i then @scale == 0 ? :integer : :decimal
28
+ when /date|time/i then :datetime
29
+ when /blob/i then :binary
30
+ end
31
+ end
32
+
33
+ def self.cast_to_date_or_time(value)
34
+ return value if value.is_a? Date
35
+ return nil if value.blank?
36
+ guess_date_or_time((value.is_a? Time) ? value : cast_to_time(value))
37
+ end
38
+
39
+ def self.cast_to_time(value)
40
+ return value if value.is_a? Time
41
+ Time.at(value) rescue nil
42
+ end
43
+
44
+ def self.guess_date_or_time(value)
45
+ (value.hour == 0 and value.min == 0 and value.sec == 0) ?
46
+ Date.new(value.year, value.month, value.day) : value
47
+ end
48
+ end
49
+
50
+ def type_cast(value)
51
+ return nil if value.nil?
52
+ case type
53
+ when :string then value
54
+ when :integer then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
55
+ when :primary_key then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
56
+ when :float then value.to_f
57
+ when :datetime then JdbcSpec::SQLite3::Column.cast_to_date_or_time(value)
58
+ when :time then JdbcSpec::SQLite3::Column.cast_to_time(value)
59
+ when :decimal then self.class.value_to_decimal(value)
60
+ when :boolean then self.class.value_to_boolean(value)
61
+ else value
62
+ end
63
+ end
64
+
65
+ def modify_types(tp)
66
+ tp[:primary_key] = "INTEGER PRIMARY KEY AUTOINCREMENT"
67
+ tp[:float] = { :name => "REAL" }
68
+ tp[:decimal] = { :name => "REAL" }
69
+ tp[:datetime] = { :name => "INTEGER" }
70
+ tp[:timestamp] = { :name => "INTEGER" }
71
+ tp[:time] = { :name => "INTEGER" }
72
+ tp[:date] = { :name => "INTEGER" }
73
+ tp[:boolean] = { :name => "INTEGER", :limit => 1}
74
+ tp
75
+ end
76
+
77
+ def quote(value, column = nil) # :nodoc:
78
+ return value.quoted_id if value.respond_to?(:quoted_id)
79
+
80
+ case value
81
+ when String
82
+ if column && column.type == :binary
83
+ "'#{quote_string(value).unpack("C*").collect {|v| v.to_s(16)}.join}'"
84
+ else
85
+ "'#{quote_string(value)}'"
86
+ end
87
+ else super
88
+ end
89
+ end
90
+
91
+ def quote_string(str)
92
+ str.gsub(/'/, "''")
93
+ end
94
+
95
+ def quoted_true
96
+ '1'
97
+ end
98
+
99
+ def quoted_false
100
+ '0'
101
+ end
102
+
103
+ def add_column(table_name, column_name, type, options = {})
104
+ if option_not_null = options[:null] == false
105
+ option_not_null = options.delete(:null)
106
+ end
107
+ add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
108
+ add_column_options!(add_column_sql, options)
109
+ execute(add_column_sql)
110
+ if option_not_null
111
+ alter_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} NOT NULL"
112
+ end
113
+ end
114
+
115
+ def remove_column(table_name, column_name) #:nodoc:
116
+ cols = columns(table_name).collect {|col| col.name}
117
+ cols.delete(column_name)
118
+ cols = cols.join(', ')
119
+ table_backup = table_name + "_backup"
120
+
121
+ @connection.begin
122
+
123
+ execute "CREATE TEMPORARY TABLE #{table_backup}(#{cols})"
124
+ insert "INSERT INTO #{table_backup} SELECT #{cols} FROM #{table_name}"
125
+ execute "DROP TABLE #{table_name}"
126
+ execute "CREATE TABLE #{table_name}(#{cols})"
127
+ insert "INSERT INTO #{table_name} SELECT #{cols} FROM #{table_backup}"
128
+ execute "DROP TABLE #{table_backup}"
129
+
130
+ @connection.commit
131
+ end
132
+
133
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
134
+ execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} #{type_to_sql(type, options[:limit])}"
135
+ end
136
+
137
+ def change_column_default(table_name, column_name, default) #:nodoc:
138
+ execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DEFAULT #{quote(default)}"
139
+ end
140
+
141
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
142
+ execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} RENAME TO #{new_column_name}"
143
+ end
144
+
145
+ def rename_table(name, new_name)
146
+ execute "ALTER TABLE #{name} RENAME TO #{new_name}"
147
+ end
148
+
149
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
150
+ log(sql,name) do
151
+ @connection.execute_update(sql)
152
+ end
153
+ table = sql.split(" ", 4)[2]
154
+ id_value || last_insert_id(table, nil)
155
+ end
156
+
157
+ def last_insert_id(table, sequence_name)
158
+ Integer(select_value("SELECT SEQ FROM SQLITE_SEQUENCE WHERE NAME = '#{table}'"))
159
+ end
160
+
161
+ def add_limit_offset!(sql, options) #:nodoc:
162
+ if options[:limit]
163
+ sql << " LIMIT #{options[:limit]}"
164
+ sql << " OFFSET #{options[:offset]}" if options[:offset]
165
+ end
166
+ end
167
+
168
+ def tables
169
+ @connection.tables.select {|row| row.to_s !~ /^sqlite_/i }
170
+ end
171
+
172
+ def remove_index(table_name, options = {})
173
+ execute "DROP INDEX #{quote_column_name(index_name(table_name, options))}"
174
+ end
175
+
176
+ def indexes(table_name, name = nil)
177
+ result = select_rows("SELECT name, sql FROM sqlite_master WHERE tbl_name = '#{table_name}' AND type = 'index'", name)
178
+
179
+ result.collect do |row|
180
+ name = row[0]
181
+ index_sql = row[1]
182
+ unique = (index_sql =~ /unique/i)
183
+ cols = index_sql.match(/\((.*)\)/)[1].gsub(/,/,' ').split
184
+ ::ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, name, unique, cols)
185
+ end
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,39 @@
1
+ module JdbcSpec
2
+ module Sybase
3
+ def self.adapter_selector
4
+ [/sybase/i, lambda{|cfg,adapt| adapt.extend(JdbcSpec::Sybase)}]
5
+ end
6
+
7
+ def add_limit_offset!(sql, options) # :nodoc:
8
+ @limit = options[:limit]
9
+ @offset = options[:offset]
10
+ if use_temp_table?
11
+ # Use temp table to hack offset with Sybase
12
+ sql.sub!(/ FROM /i, ' INTO #artemp FROM ')
13
+ elsif zero_limit?
14
+ # "SET ROWCOUNT 0" turns off limits, so we havesy
15
+ # to use a cheap trick.
16
+ if sql =~ /WHERE/i
17
+ sql.sub!(/WHERE/i, 'WHERE 1 = 2 AND ')
18
+ elsif sql =~ /ORDER\s+BY/i
19
+ sql.sub!(/ORDER\s+BY/i, 'WHERE 1 = 2 ORDER BY')
20
+ else
21
+ sql << 'WHERE 1 = 2'
22
+ end
23
+ end
24
+ end
25
+
26
+ # If limit is not set at all, we can ignore offset;
27
+ # if limit *is* set but offset is zero, use normal select
28
+ # with simple SET ROWCOUNT. Thus, only use the temp table
29
+ # if limit is set and offset > 0.
30
+ def use_temp_table?
31
+ !@limit.nil? && !@offset.nil? && @offset > 0
32
+ end
33
+
34
+ def zero_limit?
35
+ !@limit.nil? && @limit == 0
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,59 @@
1
+ # Common methods for handling TSQL databases.
2
+ module TSqlMethods
3
+
4
+ def modify_types(tp) #:nodoc:
5
+ tp[:primary_key] = "int NOT NULL IDENTITY(1, 1) PRIMARY KEY"
6
+ tp[:integer][:limit] = nil
7
+ tp[:boolean] = {:name => "bit"}
8
+ tp[:binary] = { :name => "image"}
9
+ tp
10
+ end
11
+
12
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
13
+ return super unless type.to_s == 'integer'
14
+
15
+ if limit.nil? || limit == 4
16
+ 'int'
17
+ elsif limit == 2
18
+ 'smallint'
19
+ elsif limit == 1
20
+ 'tinyint'
21
+ else
22
+ 'bigint'
23
+ end
24
+ end
25
+
26
+ def add_limit_offset!(sql, options)
27
+ if options[:limit] and options[:offset]
28
+ total_rows = select_all("SELECT count(*) as TotalRows from (#{sql.gsub(/\bSELECT(\s+DISTINCT)?\b/i, "SELECT\\1 TOP 1000000000")}) tally")[0]["TotalRows"].to_i
29
+ if (options[:limit] + options[:offset]) >= total_rows
30
+ options[:limit] = (total_rows - options[:offset] >= 0) ? (total_rows - options[:offset]) : 0
31
+ end
32
+ sql.sub!(/^\s*SELECT(\s+DISTINCT)?/i, "SELECT * FROM (SELECT TOP #{options[:limit]} * FROM (SELECT\\1 TOP #{options[:limit] + options[:offset]} ")
33
+ sql << ") AS tmp1"
34
+ if options[:order]
35
+ options[:order] = options[:order].split(',').map do |field|
36
+ parts = field.split(" ")
37
+ tc = parts[0]
38
+ if sql =~ /\.\[/ and tc =~ /\./ # if column quoting used in query
39
+ tc.gsub!(/\./, '\\.\\[')
40
+ tc << '\\]'
41
+ end
42
+ if sql =~ /#{tc} AS (t\d_r\d\d?)/
43
+ parts[0] = $1
44
+ elsif parts[0] =~ /\w+\.(\w+)/
45
+ parts[0] = $1
46
+ end
47
+ parts.join(' ')
48
+ end.join(', ')
49
+ sql << " ORDER BY #{change_order_direction(options[:order])}) AS tmp2 ORDER BY #{options[:order]}"
50
+ else
51
+ sql << " ) AS tmp2"
52
+ end
53
+ elsif sql !~ /^\s*SELECT (@@|COUNT\()/i
54
+ sql.sub!(/^\s*SELECT(\s+DISTINCT)?/i) do
55
+ "SELECT#{$1} TOP #{options[:limit]}"
56
+ end unless options[:limit].nil?
57
+ end
58
+ end
59
+ end