activerecord-jdbc-adapter 0.8 → 0.8.1

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.
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