activerecord-jdbc-adapter 0.9.7-java → 1.0.0.beta1-java

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 (123) hide show
  1. data/History.txt +11 -0
  2. data/Manifest.txt +71 -38
  3. data/lib/active_record/connection_adapters/cachedb_adapter.rb +1 -1
  4. data/lib/active_record/connection_adapters/derby_adapter.rb +1 -13
  5. data/lib/active_record/connection_adapters/h2_adapter.rb +1 -13
  6. data/lib/active_record/connection_adapters/hsqldb_adapter.rb +1 -13
  7. data/lib/active_record/connection_adapters/informix_adapter.rb +1 -1
  8. data/lib/active_record/connection_adapters/jdbc_adapter.rb +1 -661
  9. data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -1
  10. data/lib/active_record/connection_adapters/mssql_adapter.rb +1 -13
  11. data/lib/active_record/connection_adapters/mysql_adapter.rb +1 -13
  12. data/lib/active_record/connection_adapters/oracle_adapter.rb +1 -1
  13. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1 -13
  14. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -13
  15. data/lib/activerecord-jdbc-adapter.rb +2 -2
  16. data/lib/arjdbc.rb +29 -0
  17. data/lib/arjdbc/cachedb.rb +3 -0
  18. data/lib/arjdbc/cachedb/adapter.rb +20 -0
  19. data/lib/arjdbc/cachedb/connection_methods.rb +10 -0
  20. data/lib/arjdbc/db2.rb +2 -0
  21. data/lib/{jdbc_adapter/jdbc_db2.rb → arjdbc/db2/adapter.rb} +1 -17
  22. data/lib/arjdbc/derby.rb +7 -0
  23. data/lib/{jdbc_adapter/jdbc_derby.rb → arjdbc/derby/adapter.rb} +8 -26
  24. data/lib/arjdbc/derby/connection_methods.rb +18 -0
  25. data/lib/arjdbc/discover.rb +99 -0
  26. data/lib/arjdbc/firebird.rb +2 -0
  27. data/lib/{jdbc_adapter/jdbc_firebird.rb → arjdbc/firebird/adapter.rb} +12 -16
  28. data/lib/arjdbc/h2.rb +4 -0
  29. data/lib/arjdbc/h2/adapter.rb +15 -0
  30. data/lib/arjdbc/h2/connection_methods.rb +12 -0
  31. data/lib/arjdbc/hsqldb.rb +4 -0
  32. data/lib/{jdbc_adapter/jdbc_hsqldb.rb → arjdbc/hsqldb/adapter.rb} +6 -58
  33. data/lib/arjdbc/hsqldb/connection_methods.rb +14 -0
  34. data/lib/arjdbc/informix.rb +3 -0
  35. data/lib/{jdbc_adapter/jdbc_informix.rb → arjdbc/informix/adapter.rb} +6 -19
  36. data/lib/arjdbc/informix/connection_methods.rb +10 -0
  37. data/lib/arjdbc/jdbc.rb +2 -0
  38. data/lib/arjdbc/jdbc/adapter.rb +235 -0
  39. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  40. data/lib/arjdbc/jdbc/callbacks.rb +44 -0
  41. data/lib/arjdbc/jdbc/column.rb +38 -0
  42. data/lib/arjdbc/jdbc/compatibility.rb +51 -0
  43. data/lib/arjdbc/jdbc/connection.rb +97 -0
  44. data/lib/arjdbc/jdbc/connection_methods.rb +16 -0
  45. data/lib/arjdbc/jdbc/core_ext.rb +24 -0
  46. data/lib/arjdbc/jdbc/discover.rb +18 -0
  47. data/lib/arjdbc/jdbc/driver.rb +44 -0
  48. data/lib/arjdbc/jdbc/extension.rb +47 -0
  49. data/lib/arjdbc/jdbc/java.rb +14 -0
  50. data/lib/{jdbc_adapter → arjdbc/jdbc}/jdbc.rake +0 -0
  51. data/lib/{jdbc_adapter → arjdbc/jdbc}/missing_functionality_helper.rb +5 -5
  52. data/lib/arjdbc/jdbc/quoted_primary_key.rb +28 -0
  53. data/lib/{jdbc_adapter → arjdbc/jdbc}/railtie.rb +1 -1
  54. data/lib/{jdbc_adapter → arjdbc/jdbc}/rake_tasks.rb +0 -0
  55. data/lib/arjdbc/jdbc/require_driver.rb +16 -0
  56. data/lib/arjdbc/jdbc/type_converter.rb +119 -0
  57. data/lib/arjdbc/mimer.rb +2 -0
  58. data/lib/{jdbc_adapter/jdbc_mimer.rb → arjdbc/mimer/adapter.rb} +16 -19
  59. data/lib/arjdbc/mssql.rb +4 -0
  60. data/lib/{jdbc_adapter/jdbc_mssql.rb → arjdbc/mssql/adapter.rb} +19 -31
  61. data/lib/arjdbc/mssql/connection_methods.rb +13 -0
  62. data/lib/{jdbc_adapter → arjdbc/mssql}/tsql_helper.rb +1 -1
  63. data/lib/arjdbc/mysql.rb +4 -0
  64. data/lib/arjdbc/mysql/adapter.rb +388 -0
  65. data/lib/arjdbc/mysql/connection_methods.rb +26 -0
  66. data/lib/arjdbc/oracle.rb +3 -0
  67. data/lib/{jdbc_adapter/jdbc_oracle.rb → arjdbc/oracle/adapter.rb} +9 -17
  68. data/lib/arjdbc/oracle/connection_methods.rb +11 -0
  69. data/lib/arjdbc/postgresql.rb +4 -0
  70. data/lib/{jdbc_adapter/jdbc_postgre.rb → arjdbc/postgresql/adapter.rb} +7 -36
  71. data/lib/arjdbc/postgresql/connection_methods.rb +21 -0
  72. data/lib/arjdbc/sqlite3.rb +4 -0
  73. data/lib/{jdbc_adapter/jdbc_sqlite3.rb → arjdbc/sqlite3/adapter.rb} +106 -104
  74. data/lib/arjdbc/sqlite3/connection_methods.rb +33 -0
  75. data/lib/arjdbc/sybase.rb +2 -0
  76. data/lib/arjdbc/sybase/adapter.rb +46 -0
  77. data/lib/arjdbc/version.rb +8 -0
  78. data/lib/jdbc_adapter.rb +2 -27
  79. data/lib/jdbc_adapter/version.rb +3 -5
  80. data/rails_generators/templates/config/initializers/jdbc.rb +1 -1
  81. data/rakelib/compile.rake +3 -2
  82. data/rakelib/package.rake +3 -3
  83. data/src/java/{jdbc_adapter/JdbcDerbySpec.java → arjdbc/derby/DerbyModule.java} +32 -32
  84. data/src/java/{jdbc_adapter/JdbcAdapterInternalService.java → arjdbc/jdbc/AdapterJavaService.java} +13 -7
  85. data/src/java/{jdbc_adapter → arjdbc/jdbc}/JdbcConnectionFactory.java +6 -6
  86. data/src/java/{jdbc_adapter → arjdbc/jdbc}/RubyJdbcConnection.java +91 -16
  87. data/src/java/arjdbc/jdbc/SQLBlock.java +48 -0
  88. data/src/java/{jdbc_adapter → arjdbc/mssql}/MssqlRubyJdbcConnection.java +5 -2
  89. data/src/java/{jdbc_adapter/JdbcMySQLSpec.java → arjdbc/mysql/MySQLModule.java} +12 -12
  90. data/src/java/{jdbc_adapter/PostgresRubyJdbcConnection.java → arjdbc/postgresql/PostgresqlRubyJdbcConnection.java} +11 -9
  91. data/src/java/arjdbc/sqlite3/Sqlite3RubyJdbcConnection.java +64 -0
  92. data/test/abstract_db_create.rb +4 -1
  93. data/test/activerecord/connection_adapters/type_conversion_test.rb +1 -1
  94. data/test/db/cachedb.rb +0 -0
  95. data/test/db/derby.rb +12 -14
  96. data/test/db/hsqldb.rb +3 -2
  97. data/test/db/jndi_config.rb +4 -4
  98. data/test/db/sqlite3.rb +2 -6
  99. data/test/db2_simple_test.rb +23 -0
  100. data/test/derby_migration_test.rb +50 -3
  101. data/test/jdbc_common.rb +1 -1
  102. data/test/jndi_callbacks_test.rb +1 -0
  103. data/test/postgres_nonseq_pkey_test.rb +0 -2
  104. data/test/postgres_schema_search_path_test.rb +0 -2
  105. data/test/simple.rb +3 -3
  106. data/test/sybase_jtds_simple_test.rb +22 -0
  107. metadata +82 -46
  108. data/lib/active_record/connection_adapters/jdbc_adapter_spec.rb +0 -26
  109. data/lib/jdbc_adapter/jdbc_adapter_internal.jar +0 -0
  110. data/lib/jdbc_adapter/jdbc_cachedb.rb +0 -33
  111. data/lib/jdbc_adapter/jdbc_mysql.rb +0 -260
  112. data/lib/jdbc_adapter/jdbc_sybase.rb +0 -50
  113. data/src/java/jdbc_adapter/SQLBlock.java +0 -27
  114. data/src/java/jdbc_adapter/Sqlite3RubyJdbcConnection.java +0 -41
  115. data/test/jdbc_adapter/jdbc_db2_test.rb +0 -26
  116. data/test/jdbc_adapter/jdbc_sybase_test.rb +0 -33
  117. data/test/minirunit.rb +0 -109
  118. data/test/minirunit/testConnect.rb +0 -14
  119. data/test/minirunit/testH2.rb +0 -73
  120. data/test/minirunit/testHsqldb.rb +0 -73
  121. data/test/minirunit/testLoadActiveRecord.rb +0 -3
  122. data/test/minirunit/testMysql.rb +0 -83
  123. data/test/minirunit/testRawSelect.rb +0 -24
@@ -0,0 +1,28 @@
1
+ module ArJdbc
2
+ module QuotedPrimaryKeyExtension
3
+ def self.extended(base)
4
+ # Rails 3 method Rails 2 method
5
+ meth = [:arel_attributes_values, :attributes_with_quotes].detect do |m|
6
+ base.private_instance_methods.include?(m.to_s)
7
+ end
8
+ pk_hash_key = "self.class.primary_key"
9
+ pk_hash_value = '"?"'
10
+ if meth == :arel_attributes_values
11
+ pk_hash_key = "self.class.arel_table[#{pk_hash_key}]"
12
+ pk_hash_value = "Arel::SqlLiteral.new(#{pk_hash_value})"
13
+ end
14
+ if meth
15
+ base.module_eval <<-PK, __FILE__, __LINE__
16
+ alias :#{meth}_pre_pk :#{meth}
17
+ def #{meth}(include_primary_key = true, *args) #:nodoc:
18
+ aq = #{meth}_pre_pk(include_primary_key, *args)
19
+ if connection.is_a?(ArJdbc::Oracle) || connection.is_a?(ArJdbc::Mimer)
20
+ aq[#{pk_hash_key}] = #{pk_hash_value} if include_primary_key && aq[#{pk_hash_key}].nil?
21
+ end
22
+ aq
23
+ end
24
+ PK
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,6 +1,6 @@
1
1
  require 'rails/railtie'
2
2
 
3
- module ::JdbcSpec
3
+ module ::ArJdbc
4
4
  class Railtie < ::Rails::Railtie
5
5
  rake_tasks do
6
6
  load File.expand_path('../rake_tasks.rb', __FILE__)
@@ -0,0 +1,16 @@
1
+ module Kernel
2
+ # load a JDBC driver library/gem, failing silently. If failed, trust
3
+ # that the driver jar is already present through some other means
4
+ def jdbc_require_driver(path, gem_name = nil)
5
+ gem_name ||= path.sub('/', '-')
6
+ 2.times do
7
+ begin
8
+ require path
9
+ break
10
+ rescue LoadError
11
+ require 'rubygems'
12
+ begin; gem gem_name; rescue LoadError; end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,119 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ # I want to use JDBC's DatabaseMetaData#getTypeInfo to choose the best native types to
4
+ # use for ActiveRecord's Adapter#native_database_types in a database-independent way,
5
+ # but apparently a database driver can return multiple types for a given
6
+ # java.sql.Types constant. So this type converter uses some heuristics to try to pick
7
+ # the best (most common) type to use. It's not great, it would be better to just
8
+ # delegate to each database's existin AR adapter's native_database_types method, but I
9
+ # wanted to try to do this in a way that didn't pull in all the other adapters as
10
+ # dependencies. Suggestions appreciated.
11
+ class JdbcTypeConverter
12
+ # The basic ActiveRecord types, mapped to an array of procs that are used to #select
13
+ # the best type. The procs are used as selectors in order until there is only one
14
+ # type left. If all the selectors are applied and there is still more than one
15
+ # type, an exception will be raised.
16
+ AR_TO_JDBC_TYPES = {
17
+ :string => [ lambda {|r| Jdbc::Types::VARCHAR == r['data_type'].to_i},
18
+ lambda {|r| r['type_name'] =~ /^varchar/i},
19
+ lambda {|r| r['type_name'] =~ /^varchar$/i},
20
+ lambda {|r| r['type_name'] =~ /varying/i}],
21
+ :text => [ lambda {|r| [Jdbc::Types::LONGVARCHAR, Jdbc::Types::CLOB].include?(r['data_type'].to_i)},
22
+ lambda {|r| r['type_name'] =~ /^text$/i}, # For Informix
23
+ lambda {|r| r['type_name'] =~ /^(text|clob)$/i},
24
+ lambda {|r| r['type_name'] =~ /^character large object$/i},
25
+ lambda {|r| r['sql_data_type'] == 2005}],
26
+ :integer => [ lambda {|r| Jdbc::Types::INTEGER == r['data_type'].to_i},
27
+ lambda {|r| r['type_name'] =~ /^integer$/i},
28
+ lambda {|r| r['type_name'] =~ /^int4$/i},
29
+ lambda {|r| r['type_name'] =~ /^int$/i}],
30
+ :decimal => [ lambda {|r| Jdbc::Types::DECIMAL == r['data_type'].to_i},
31
+ lambda {|r| r['type_name'] =~ /^decimal$/i},
32
+ lambda {|r| r['type_name'] =~ /^numeric$/i},
33
+ lambda {|r| r['type_name'] =~ /^number$/i},
34
+ lambda {|r| r['type_name'] =~ /^real$/i},
35
+ lambda {|r| r['precision'] == '38'},
36
+ lambda {|r| r['data_type'] == '2'}],
37
+ :float => [ lambda {|r| [Jdbc::Types::FLOAT,Jdbc::Types::DOUBLE, Jdbc::Types::REAL].include?(r['data_type'].to_i)},
38
+ lambda {|r| r['data_type'].to_i == Jdbc::Types::REAL}, #Prefer REAL to DOUBLE for Postgresql
39
+ lambda {|r| r['type_name'] =~ /^float/i},
40
+ lambda {|r| r['type_name'] =~ /^double$/i},
41
+ lambda {|r| r['type_name'] =~ /^real$/i},
42
+ lambda {|r| r['precision'] == '15'}],
43
+ :datetime => [ lambda {|r| Jdbc::Types::TIMESTAMP == r['data_type'].to_i},
44
+ lambda {|r| r['type_name'] =~ /^datetime$/i},
45
+ lambda {|r| r['type_name'] =~ /^timestamp$/i},
46
+ lambda {|r| r['type_name'] =~ /^date/i},
47
+ lambda {|r| r['type_name'] =~ /^integer/i}], #Num of milliseconds for SQLite3 JDBC Driver
48
+ :timestamp => [ lambda {|r| Jdbc::Types::TIMESTAMP == r['data_type'].to_i},
49
+ lambda {|r| r['type_name'] =~ /^timestamp$/i},
50
+ lambda {|r| r['type_name'] =~ /^datetime/i},
51
+ lambda {|r| r['type_name'] =~ /^date/i},
52
+ lambda {|r| r['type_name'] =~ /^integer/i}], #Num of milliseconds for SQLite3 JDBC Driver
53
+ :time => [ lambda {|r| Jdbc::Types::TIME == r['data_type'].to_i},
54
+ lambda {|r| r['type_name'] =~ /^time$/i},
55
+ lambda {|r| r['type_name'] =~ /^datetime/i}, # For Informix
56
+ lambda {|r| r['type_name'] =~ /^date/i},
57
+ lambda {|r| r['type_name'] =~ /^integer/i}], #Num of milliseconds for SQLite3 JDBC Driver
58
+ :date => [ lambda {|r| Jdbc::Types::DATE == r['data_type'].to_i},
59
+ lambda {|r| r['type_name'] =~ /^date$/i},
60
+ lambda {|r| r['type_name'] =~ /^date/i},
61
+ lambda {|r| r['type_name'] =~ /^integer/i}], #Num of milliseconds for SQLite3 JDBC Driver3
62
+ :binary => [ lambda {|r| [Jdbc::Types::LONGVARBINARY,Jdbc::Types::BINARY,Jdbc::Types::BLOB].include?(r['data_type'].to_i)},
63
+ lambda {|r| r['type_name'] =~ /^blob/i},
64
+ lambda {|r| r['type_name'] =~ /sub_type 0$/i}, # For FireBird
65
+ lambda {|r| r['type_name'] =~ /^varbinary$/i}, # We want this sucker for Mimer
66
+ lambda {|r| r['type_name'] =~ /^binary$/i}, ],
67
+ :boolean => [ lambda {|r| [Jdbc::Types::TINYINT].include?(r['data_type'].to_i)},
68
+ lambda {|r| r['type_name'] =~ /^bool/i},
69
+ lambda {|r| r['data_type'] == '-7'},
70
+ lambda {|r| r['type_name'] =~ /^tinyint$/i},
71
+ lambda {|r| r['type_name'] =~ /^decimal$/i},
72
+ lambda {|r| r['type_name'] =~ /^integer$/i}]
73
+ }
74
+
75
+ def initialize(types)
76
+ @types = types
77
+ @types.each {|t| t['type_name'] ||= t['local_type_name']} # Sybase driver seems to want 'local_type_name'
78
+ end
79
+
80
+ def choose_best_types
81
+ type_map = {}
82
+ @types.each do |row|
83
+ name = row['type_name'].downcase
84
+ k = name.to_sym
85
+ type_map[k] = { :name => name }
86
+ type_map[k][:limit] = row['precision'].to_i if row['precision']
87
+ end
88
+
89
+ AR_TO_JDBC_TYPES.keys.each do |k|
90
+ typerow = choose_type(k)
91
+ type_map[k] = { :name => typerow['type_name'].downcase }
92
+ case k
93
+ when :integer, :string, :decimal
94
+ type_map[k][:limit] = typerow['precision'] && typerow['precision'].to_i
95
+ when :boolean
96
+ type_map[k][:limit] = 1
97
+ end
98
+ end
99
+ type_map
100
+ end
101
+
102
+ def choose_type(ar_type)
103
+ procs = AR_TO_JDBC_TYPES[ar_type]
104
+ types = @types
105
+ procs.each do |p|
106
+ new_types = types.reject {|r| r["data_type"].to_i == Jdbc::Types::OTHER}
107
+ new_types = new_types.select(&p)
108
+ new_types = new_types.inject([]) do |typs,t|
109
+ typs << t unless typs.detect {|el| el['type_name'] == t['type_name']}
110
+ typs
111
+ end
112
+ return new_types.first if new_types.length == 1
113
+ types = new_types if new_types.length > 0
114
+ end
115
+ raise "unable to choose type for #{ar_type} from:\n#{types.collect{|t| t['type_name']}.inspect}"
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,2 @@
1
+ require 'arjdbc/jdbc'
2
+ require 'arjdbc/mimer/adapter'
@@ -1,11 +1,8 @@
1
- module JdbcSpec
1
+ module ArJdbc
2
2
  module Mimer
3
3
  def self.extended(mod)
4
- ActiveRecord::Base.extend JdbcSpec::QuotedPrimaryKeyExtension
5
- end
6
-
7
- def self.adapter_matcher(name, *)
8
- name =~ /mimer/i ? self : false
4
+ require 'arjdbc/jdbc/quoted_primary_key'
5
+ ActiveRecord::Base.extend ArJdbc::QuotedPrimaryKeyExtension
9
6
  end
10
7
 
11
8
  def modify_types(tp)
@@ -20,7 +17,7 @@ module JdbcSpec
20
17
  tp[:date] = { :name => "TIMESTAMP" }
21
18
  tp
22
19
  end
23
-
20
+
24
21
  def default_sequence_name(table, column) #:nodoc:
25
22
  "#{table}_seq"
26
23
  end
@@ -54,13 +51,13 @@ module JdbcSpec
54
51
  log(sql, name) { @connection.execute_insert sql,pk }
55
52
  else # Assume the sql contains a bind-variable for the id
56
53
  id_value = select_one("SELECT NEXT_VALUE OF #{sequence_name} AS val FROM MIMER.ONEROW")['val']
57
- log(sql, name) {
54
+ log(sql, name) {
58
55
  execute_prepared_insert(sql,id_value)
59
56
  }
60
57
  end
61
58
  id_value
62
59
  end
63
-
60
+
64
61
  def execute_prepared_insert(sql, id)
65
62
  @stmts ||= {}
66
63
  @stmts[sql] ||= @connection.ps(sql)
@@ -72,24 +69,24 @@ module JdbcSpec
72
69
 
73
70
  def quote(value, column = nil) #:nodoc:
74
71
  return value.quoted_id if value.respond_to?(:quoted_id)
75
-
72
+
76
73
  if String === value && column && column.type == :binary
77
74
  return "X'#{quote_string(value.unpack("C*").collect {|v| v.to_s(16)}.join)}'"
78
75
  end
79
76
  case value
80
- when String
77
+ when String
81
78
  %Q{'#{quote_string(value)}'}
82
- when NilClass
79
+ when NilClass
83
80
  'NULL'
84
- when TrueClass
81
+ when TrueClass
85
82
  '1'
86
- when FalseClass
83
+ when FalseClass
87
84
  '0'
88
- when Numeric
85
+ when Numeric
89
86
  value.to_s
90
- when Date, Time
87
+ when Date, Time
91
88
  %Q{TIMESTAMP '#{value.strftime("%Y-%m-%d %H:%M:%S")}'}
92
- else
89
+ else
93
90
  %Q{'#{quote_string(value.to_yaml)}'}
94
91
  end
95
92
  end
@@ -106,7 +103,7 @@ module JdbcSpec
106
103
  @limit = options[:limit]
107
104
  @offset = options[:offset]
108
105
  end
109
-
106
+
110
107
  def select_all(sql, name = nil)
111
108
  @offset ||= 0
112
109
  if !@limit || @limit == -1
@@ -118,7 +115,7 @@ module JdbcSpec
118
115
  ensure
119
116
  @limit = @offset = nil
120
117
  end
121
-
118
+
122
119
  def select_one(sql, name = nil)
123
120
  @offset ||= 0
124
121
  select(sql, name)[@offset]
@@ -0,0 +1,4 @@
1
+ require 'arjdbc/jdbc'
2
+ jdbc_require_driver 'jdbc/jtds', 'jdbc-mssql'
3
+ require 'arjdbc/mssql/connection_methods'
4
+ require 'arjdbc/mssql/adapter'
@@ -1,22 +1,7 @@
1
- require 'jdbc_adapter/tsql_helper'
2
-
3
- module ::JdbcSpec
4
-
5
- module ActiveRecordExtensions
6
-
7
- def mssql_connection(config)
8
- require "active_record/connection_adapters/mssql_adapter"
9
- config[:host] ||= "localhost"
10
- config[:port] ||= 1433
11
- config[:url] ||= "jdbc:jtds:sqlserver://#{config[:host]}:#{config[:port]}/#{config[:database]}"
12
- config[:driver] ||= "net.sourceforge.jtds.jdbc.Driver"
13
- embedded_driver(config)
14
- end
15
-
16
- end
1
+ require 'arjdbc/mssql/tsql_helper'
17
2
 
3
+ module ::ArJdbc
18
4
  module MsSQL
19
-
20
5
  include TSqlMethods
21
6
 
22
7
  def self.extended(mod)
@@ -39,12 +24,8 @@ module ::JdbcSpec
39
24
  mod.add_version_specific_add_limit_offset
40
25
  end
41
26
 
42
- def self.adapter_matcher(name, *)
43
- name =~ /sqlserver|tds/i ? self : false
44
- end
45
-
46
27
  def self.column_selector
47
- [/sqlserver|tds/i, lambda {|cfg,col| col.extend(::JdbcSpec::MsSQL::Column)}]
28
+ [/sqlserver|tds/i, lambda {|cfg,col| col.extend(::ArJdbc::MsSQL::Column)}]
48
29
  end
49
30
 
50
31
  def self.jdbc_connection_class
@@ -102,7 +83,7 @@ module ::JdbcSpec
102
83
  def type_cast(value)
103
84
  return nil if value.nil? || value == "(null)" || value == "(NULL)"
104
85
  case type
105
- when :integer then unquote(value).to_i rescue value ? 1 : 0
86
+ when :integer then value.to_i rescue unquote(value).to_i rescue value ? 1 : 0
106
87
  when :primary_key then value == true || value == false ? value == true ? 1 : 0 : value.to_i
107
88
  when :decimal then self.class.value_to_decimal(unquote(value))
108
89
  when :datetime then cast_to_datetime(value)
@@ -165,7 +146,7 @@ module ::JdbcSpec
165
146
  when String, ActiveSupport::Multibyte::Chars
166
147
  value = value.to_s
167
148
  if column && column.type == :binary
168
- "'#{quote_string(JdbcSpec::MsSQL::Column.string_to_binary(value))}'" # ' (for ruby-mode)
149
+ "'#{quote_string(ArJdbc::MsSQL::Column.string_to_binary(value))}'" # ' (for ruby-mode)
169
150
  elsif column && [:integer, :float].include?(column.type)
170
151
  value = column.type == :integer ? value.to_i : value.to_f
171
152
  value.to_s
@@ -209,7 +190,7 @@ module ::JdbcSpec
209
190
  end_row = offset + limit.to_i
210
191
  order = (options[:order] || determine_order_clause(sql))
211
192
  sql.sub!(/ ORDER BY.*$/i, '')
212
- find_select = /\b(SELECT(?:\s+DISTINCT)?)\b(.*)/i
193
+ find_select = /\b(SELECT(?:\s+DISTINCT)?)\b(.*)/im
213
194
  whole, select, rest_of_query = find_select.match(sql).to_a
214
195
  if (start_row == 1) && (end_row ==1)
215
196
  new_sql = "#{select} TOP 1 #{rest_of_query}"
@@ -240,7 +221,7 @@ module ::JdbcSpec
240
221
  end_row = offset + limit.to_i
241
222
  order = (options[:order] || determine_order_clause(sql))
242
223
  sql.sub!(/ ORDER BY.*$/i, '')
243
- find_select = /\b(SELECT(?:\s+DISTINCT)?)\b(.*)/i
224
+ find_select = /\b(SELECT(?:\s+DISTINCT)?)\b(.*)/im
244
225
  whole, select, rest_of_query = find_select.match(sql).to_a
245
226
  new_sql = "#{select} t.* FROM (SELECT ROW_NUMBER() OVER(ORDER BY #{order}) AS row_num, #{rest_of_query}"
246
227
  new_sql << ") AS t WHERE t.row_num BETWEEN #{start_row.to_s} AND #{end_row.to_s}"
@@ -371,6 +352,12 @@ module ::JdbcSpec
371
352
  end
372
353
  end
373
354
 
355
+ def select(sql, name = nil)
356
+ log(sql, name) do
357
+ @connection.execute_query(sql)
358
+ end
359
+ end
360
+
374
361
  #SELECT .. FOR UPDATE is not supported on Microsoft SQL Server
375
362
  def add_lock!(sql, options)
376
363
  sql
@@ -452,17 +439,18 @@ module ::JdbcSpec
452
439
 
453
440
  def determine_order_clause(sql)
454
441
  return $1 if sql =~ /ORDER BY (.*)$/
455
- sql =~ /FROM +(\w+?)\b/ || raise("can't determine table name")
456
- table_name = $1
442
+ table_name = get_table_name(sql)
457
443
  "#{table_name}.#{determine_primary_key(table_name)}"
458
444
  end
459
445
 
460
446
  def determine_primary_key(table_name)
461
447
  primary_key = columns(table_name).detect { |column| column.primary || column.identity }
462
- primary_key ? primary_key.name : "id"
448
+ return primary_key.name if primary_key
449
+ # Look for an id column. Return it, without changing case, to cover dbs with a case-sensitive collation.
450
+ columns(table_name).each { |column| return column.name if column.name =~ /^id$/i }
451
+ # Give up and provide something which is going to crash almost certainly
452
+ "id"
463
453
  end
464
-
465
454
  end
466
-
467
455
  end
468
456
 
@@ -0,0 +1,13 @@
1
+ class ActiveRecord::Base
2
+ class << self
3
+ def mssql_connection(config)
4
+ require "arjdbc/mssql"
5
+ config[:host] ||= "localhost"
6
+ config[:port] ||= 1433
7
+ config[:url] ||= "jdbc:jtds:sqlserver://#{config[:host]}:#{config[:port]}/#{config[:database]}"
8
+ config[:driver] ||= "net.sourceforge.jtds.jdbc.Driver"
9
+ embedded_driver(config)
10
+ end
11
+ alias_method :jdbcmssql_connection, :mssql_connection
12
+ end
13
+ end
@@ -12,7 +12,7 @@ module TSqlMethods
12
12
  def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
13
13
  return 'uniqueidentifier' if (type.to_s == 'uniqueidentifier')
14
14
  return super unless type.to_s == 'integer'
15
-
15
+
16
16
  if limit.nil? || limit == 4
17
17
  'int'
18
18
  elsif limit == 2
@@ -0,0 +1,4 @@
1
+ require 'arjdbc/jdbc'
2
+ jdbc_require_driver 'jdbc/mysql'
3
+ require 'arjdbc/mysql/connection_methods'
4
+ require 'arjdbc/mysql/adapter'
@@ -0,0 +1,388 @@
1
+ require 'active_record/connection_adapters/abstract/schema_definitions'
2
+
3
+ module ::ArJdbc
4
+ module MySQL
5
+ def self.column_selector
6
+ [/mysql/i, lambda {|cfg,col| col.extend(::ArJdbc::MySQL::Column)}]
7
+ end
8
+
9
+ def self.extended(adapter)
10
+ adapter.configure_connection
11
+ end
12
+
13
+ def configure_connection
14
+ execute("SET SQL_AUTO_IS_NULL=0")
15
+ end
16
+
17
+ module Column
18
+ def extract_default(default)
19
+ if sql_type =~ /blob/i || type == :text
20
+ if default.blank?
21
+ return null ? nil : ''
22
+ else
23
+ raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
24
+ end
25
+ elsif missing_default_forged_as_empty_string?(default)
26
+ nil
27
+ else
28
+ super
29
+ end
30
+ end
31
+
32
+ def has_default?
33
+ return false if sql_type =~ /blob/i || type == :text #mysql forbids defaults on blob and text columns
34
+ super
35
+ end
36
+
37
+ def simplified_type(field_type)
38
+ return :boolean if field_type =~ /tinyint\(1\)|bit/i
39
+ return :string if field_type =~ /enum/i
40
+ super
41
+ end
42
+
43
+ def extract_limit(sql_type)
44
+ case sql_type
45
+ when /blob|text/i
46
+ case sql_type
47
+ when /tiny/i
48
+ 255
49
+ when /medium/i
50
+ 16777215
51
+ when /long/i
52
+ 2147483647 # mysql only allows 2^31-1, not 2^32-1, somewhat inconsistently with the tiny/medium/normal cases
53
+ else
54
+ nil # we could return 65535 here, but we leave it undecorated by default
55
+ end
56
+ when /^bigint/i; 8
57
+ when /^int/i; 4
58
+ when /^mediumint/i; 3
59
+ when /^smallint/i; 2
60
+ when /^tinyint/i; 1
61
+ else
62
+ super
63
+ end
64
+ end
65
+
66
+ # MySQL misreports NOT NULL column default when none is given.
67
+ # We can't detect this for columns which may have a legitimate ''
68
+ # default (string) but we can for others (integer, datetime, boolean,
69
+ # and the rest).
70
+ #
71
+ # Test whether the column has default '', is not null, and is not
72
+ # a type allowing default ''.
73
+ def missing_default_forged_as_empty_string?(default)
74
+ type != :string && !null && default == ''
75
+ end
76
+ end
77
+
78
+ def modify_types(tp)
79
+ tp[:primary_key] = "int(11) DEFAULT NULL auto_increment PRIMARY KEY"
80
+ tp[:integer] = { :name => 'int', :limit => 4 }
81
+ tp[:decimal] = { :name => "decimal" }
82
+ tp[:timestamp] = { :name => "datetime" }
83
+ tp[:datetime][:limit] = nil
84
+ tp
85
+ end
86
+
87
+ def adapter_name #:nodoc:
88
+ 'MySQL'
89
+ end
90
+
91
+ def case_sensitive_equality_operator
92
+ "= BINARY"
93
+ end
94
+
95
+ # QUOTING ==================================================
96
+
97
+ def quote(value, column = nil)
98
+ return value.quoted_id if value.respond_to?(:quoted_id)
99
+
100
+ if column && column.type == :primary_key
101
+ value.to_s
102
+ elsif column && String === value && column.type == :binary && column.class.respond_to?(:string_to_binary)
103
+ s = column.class.string_to_binary(value).unpack("H*")[0]
104
+ "x'#{s}'"
105
+ elsif BigDecimal === value
106
+ "'#{value.to_s("F")}'"
107
+ else
108
+ super
109
+ end
110
+ end
111
+
112
+ def quoted_true
113
+ "1"
114
+ end
115
+
116
+ def quoted_false
117
+ "0"
118
+ end
119
+
120
+ def begin_db_transaction #:nodoc:
121
+ @connection.begin
122
+ rescue Exception
123
+ # Transactions aren't supported
124
+ end
125
+
126
+ def commit_db_transaction #:nodoc:
127
+ @connection.commit
128
+ rescue Exception
129
+ # Transactions aren't supported
130
+ end
131
+
132
+ def rollback_db_transaction #:nodoc:
133
+ @connection.rollback
134
+ rescue Exception
135
+ # Transactions aren't supported
136
+ end
137
+
138
+ def supports_savepoints? #:nodoc:
139
+ true
140
+ end
141
+
142
+ def create_savepoint
143
+ execute("SAVEPOINT #{current_savepoint_name}")
144
+ end
145
+
146
+ def rollback_to_savepoint
147
+ execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
148
+ end
149
+
150
+ def release_savepoint
151
+ execute("RELEASE SAVEPOINT #{current_savepoint_name}")
152
+ end
153
+
154
+ def disable_referential_integrity(&block) #:nodoc:
155
+ old = select_value("SELECT @@FOREIGN_KEY_CHECKS")
156
+ begin
157
+ update("SET FOREIGN_KEY_CHECKS = 0")
158
+ yield
159
+ ensure
160
+ update("SET FOREIGN_KEY_CHECKS = #{old}")
161
+ end
162
+ end
163
+
164
+ # SCHEMA STATEMENTS ========================================
165
+
166
+ def structure_dump #:nodoc:
167
+ if supports_views?
168
+ sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
169
+ else
170
+ sql = "SHOW TABLES"
171
+ end
172
+
173
+ select_all(sql).inject("") do |structure, table|
174
+ table.delete('Table_type')
175
+
176
+ hash = show_create_table(table.to_a.first.last)
177
+
178
+ if(table = hash["Create Table"])
179
+ structure += table + ";\n\n"
180
+ elsif(view = hash["Create View"])
181
+ structure += view + ";\n\n"
182
+ end
183
+ end
184
+ end
185
+
186
+ def recreate_database(name, options = {}) #:nodoc:
187
+ drop_database(name)
188
+ create_database(name, options)
189
+ end
190
+
191
+ def character_set(options) #:nodoc:
192
+ str = "CHARACTER SET `#{options[:charset] || 'utf8'}`"
193
+ str += " COLLATE `#{options[:collation]}`" if options[:collation]
194
+ str
195
+ end
196
+ private :character_set
197
+
198
+ def create_database(name, options = {}) #:nodoc:
199
+ execute "CREATE DATABASE `#{name}` DEFAULT #{character_set(options)}"
200
+ end
201
+
202
+ def drop_database(name) #:nodoc:
203
+ execute "DROP DATABASE IF EXISTS `#{name}`"
204
+ end
205
+
206
+ def current_database
207
+ select_one("SELECT DATABASE() as db")["db"]
208
+ end
209
+
210
+ def create_table(name, options = {}) #:nodoc:
211
+ super(name, {:options => "ENGINE=InnoDB #{character_set(options)}"}.merge(options))
212
+ end
213
+
214
+ def rename_table(name, new_name)
215
+ execute "RENAME TABLE #{quote_table_name(name)} TO #{quote_table_name(new_name)}"
216
+ end
217
+
218
+ def add_column(table_name, column_name, type, options = {})
219
+ 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])}"
220
+ add_column_options!(add_column_sql, options)
221
+ add_column_position!(add_column_sql, options)
222
+ execute(add_column_sql)
223
+ end
224
+
225
+ def change_column_default(table_name, column_name, default) #:nodoc:
226
+ column = column_for(table_name, column_name)
227
+ change_column table_name, column_name, column.sql_type, :default => default
228
+ end
229
+
230
+ def change_column_null(table_name, column_name, null, default = nil)
231
+ column = column_for(table_name, column_name)
232
+
233
+ unless null || default.nil?
234
+ execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
235
+ end
236
+
237
+ change_column table_name, column_name, column.sql_type, :null => null
238
+ end
239
+
240
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
241
+ column = column_for(table_name, column_name)
242
+
243
+ unless options_include_default?(options)
244
+ options[:default] = column.default
245
+ end
246
+
247
+ unless options.has_key?(:null)
248
+ options[:null] = column.null
249
+ end
250
+
251
+ change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
252
+ add_column_options!(change_column_sql, options)
253
+ add_column_position!(change_column_sql, options)
254
+ execute(change_column_sql)
255
+ end
256
+
257
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
258
+ options = {}
259
+ if column = columns(table_name).find { |c| c.name == column_name.to_s }
260
+ options[:default] = column.default
261
+ options[:null] = column.null
262
+ else
263
+ raise ActiveRecord::ActiveRecordError, "No such column: #{table_name}.#{column_name}"
264
+ end
265
+ current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
266
+ rename_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
267
+ add_column_options!(rename_column_sql, options)
268
+ execute(rename_column_sql)
269
+ end
270
+
271
+ def add_limit_offset!(sql, options) #:nodoc:
272
+ limit, offset = options[:limit], options[:offset]
273
+ if limit && offset
274
+ sql << " LIMIT #{offset.to_i}, #{sanitize_limit(limit)}"
275
+ elsif limit
276
+ sql << " LIMIT #{sanitize_limit(limit)}"
277
+ elsif offset
278
+ sql << " OFFSET #{offset.to_i}"
279
+ end
280
+ sql
281
+ end
282
+
283
+ def show_variable(var)
284
+ res = execute("show variables like '#{var}'")
285
+ row = res.detect {|row| row["Variable_name"] == var }
286
+ row && row["Value"]
287
+ end
288
+
289
+ def charset
290
+ show_variable("character_set_database")
291
+ end
292
+
293
+ def collation
294
+ show_variable("collation_database")
295
+ end
296
+
297
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil)
298
+ return super unless type.to_s == 'integer'
299
+
300
+ case limit
301
+ when 1; 'tinyint'
302
+ when 2; 'smallint'
303
+ when 3; 'mediumint'
304
+ when nil, 4, 11; 'int(11)' # compatibility with MySQL default
305
+ when 5..8; 'bigint'
306
+ else raise(ActiveRecordError, "No integer type has byte size #{limit}")
307
+ end
308
+ end
309
+
310
+ def add_column_position!(sql, options)
311
+ if options[:first]
312
+ sql << " FIRST"
313
+ elsif options[:after]
314
+ sql << " AFTER #{quote_column_name(options[:after])}"
315
+ end
316
+ end
317
+
318
+ protected
319
+ def translate_exception(exception, message)
320
+ return super unless exception.respond_to?(:errno)
321
+
322
+ case exception.errno
323
+ when 1062
324
+ ::ActiveRecord::RecordNotUnique.new(message, exception)
325
+ when 1452
326
+ ::ActiveRecord::InvalidForeignKey.new(message, exception)
327
+ else
328
+ super
329
+ end
330
+ end
331
+
332
+ private
333
+ def column_for(table_name, column_name)
334
+ unless column = columns(table_name).find { |c| c.name == column_name.to_s }
335
+ raise "No such column: #{table_name}.#{column_name}"
336
+ end
337
+ column
338
+ end
339
+
340
+ def show_create_table(table)
341
+ select_one("SHOW CREATE TABLE #{quote_table_name(table)}")
342
+ end
343
+
344
+ def supports_views?
345
+ false
346
+ end
347
+ end
348
+ end
349
+
350
+ module ActiveRecord::ConnectionAdapters
351
+ class MysqlColumn < JdbcColumn
352
+ include ArJdbc::MySQL::Column
353
+
354
+ def initialize(name, *args)
355
+ if Hash === name
356
+ super
357
+ else
358
+ super(nil, name, *args)
359
+ end
360
+ end
361
+
362
+ def call_discovered_column_callbacks(*)
363
+ end
364
+ end
365
+
366
+ class MysqlAdapter < JdbcAdapter
367
+ include ArJdbc::MySQL
368
+
369
+ def initialize(*args)
370
+ super
371
+ configure_connection
372
+ end
373
+
374
+ def adapter_spec(config)
375
+ # return nil to avoid extending ArJdbc::MySQL, which we've already done
376
+ end
377
+
378
+ def jdbc_column_class
379
+ ActiveRecord::ConnectionAdapters::MysqlColumn
380
+ end
381
+ end
382
+ end
383
+
384
+ module Mysql # :nodoc:
385
+ def self.client_version
386
+ 50400 # faked out for AR tests
387
+ end
388
+ end