saturnflyer-activerecord-jdbc-adapter 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (183) hide show
  1. data/.gitignore +16 -0
  2. data/History.txt +225 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.txt +161 -0
  5. data/Rakefile +28 -0
  6. data/VERSION +1 -0
  7. data/activerecord-jdbc-adapter.gemspec +277 -0
  8. data/adapters/derby/LICENSE.txt +21 -0
  9. data/adapters/derby/README.txt +5 -0
  10. data/adapters/derby/Rakefile +31 -0
  11. data/adapters/derby/lib/active_record/connection_adapters/jdbcderby_adapter.rb +30 -0
  12. data/adapters/h2/History.txt +0 -0
  13. data/adapters/h2/LICENSE.txt +21 -0
  14. data/adapters/h2/README.txt +5 -0
  15. data/adapters/h2/Rakefile +31 -0
  16. data/adapters/h2/lib/active_record/connection_adapters/jdbch2_adapter.rb +30 -0
  17. data/adapters/hsqldb/History.txt +0 -0
  18. data/adapters/hsqldb/LICENSE.txt +21 -0
  19. data/adapters/hsqldb/README.txt +5 -0
  20. data/adapters/hsqldb/Rakefile +31 -0
  21. data/adapters/hsqldb/lib/active_record/connection_adapters/jdbchsqldb_adapter.rb +30 -0
  22. data/adapters/mysql/History.txt +0 -0
  23. data/adapters/mysql/LICENSE.txt +21 -0
  24. data/adapters/mysql/README.txt +5 -0
  25. data/adapters/mysql/Rakefile +31 -0
  26. data/adapters/mysql/lib/active_record/connection_adapters/jdbcmysql_adapter.rb +30 -0
  27. data/adapters/postgresql/History.txt +0 -0
  28. data/adapters/postgresql/LICENSE.txt +21 -0
  29. data/adapters/postgresql/README.txt +5 -0
  30. data/adapters/postgresql/Rakefile +31 -0
  31. data/adapters/postgresql/lib/active_record/connection_adapters/jdbcpostgresql_adapter.rb +30 -0
  32. data/adapters/sqlite3/LICENSE.txt +21 -0
  33. data/adapters/sqlite3/README.txt +5 -0
  34. data/adapters/sqlite3/Rakefile +31 -0
  35. data/adapters/sqlite3/lib/active_record/connection_adapters/jdbcsqlite3_adapter.rb +30 -0
  36. data/bench/bench_attributes.rb +13 -0
  37. data/bench/bench_attributes_new.rb +14 -0
  38. data/bench/bench_create.rb +12 -0
  39. data/bench/bench_find_all.rb +12 -0
  40. data/bench/bench_find_all_mt.rb +25 -0
  41. data/bench/bench_model.rb +85 -0
  42. data/bench/bench_new.rb +12 -0
  43. data/bench/bench_new_valid.rb +12 -0
  44. data/bench/bench_valid.rb +13 -0
  45. data/drivers/derby/LICENSE.txt +1 -0
  46. data/drivers/derby/README.txt +6 -0
  47. data/drivers/derby/Rakefile +26 -0
  48. data/drivers/derby/lib/derby-10.5.3.0.jar +0 -0
  49. data/drivers/derby/lib/jdbc/derby.rb +11 -0
  50. data/drivers/h2/History.txt +0 -0
  51. data/drivers/h2/LICENSE.txt +2 -0
  52. data/drivers/h2/README.txt +6 -0
  53. data/drivers/h2/Rakefile +26 -0
  54. data/drivers/h2/lib/h2-1.1.107.jar +0 -0
  55. data/drivers/h2/lib/jdbc/h2.rb +10 -0
  56. data/drivers/hsqldb/History.txt +0 -0
  57. data/drivers/hsqldb/LICENSE.txt +2 -0
  58. data/drivers/hsqldb/README.txt +6 -0
  59. data/drivers/hsqldb/Rakefile +26 -0
  60. data/drivers/hsqldb/lib/hsqldb-1.8.0.7.jar +0 -0
  61. data/drivers/hsqldb/lib/jdbc/hsqldb.rb +10 -0
  62. data/drivers/mysql/History.txt +0 -0
  63. data/drivers/mysql/LICENSE.txt +1 -0
  64. data/drivers/mysql/README.txt +6 -0
  65. data/drivers/mysql/Rakefile +26 -0
  66. data/drivers/mysql/lib/jdbc/mysql.rb +10 -0
  67. data/drivers/mysql/lib/mysql-connector-java-5.0.4-bin.jar +0 -0
  68. data/drivers/postgres/History.txt +7 -0
  69. data/drivers/postgres/LICENSE.txt +12 -0
  70. data/drivers/postgres/README.txt +5 -0
  71. data/drivers/postgres/Rakefile +27 -0
  72. data/drivers/postgres/lib/jdbc/postgres.rb +25 -0
  73. data/drivers/postgres/lib/postgresql-8.3-604.jdbc3.jar +0 -0
  74. data/drivers/postgres/lib/postgresql-8.3-604.jdbc4.jar +0 -0
  75. data/drivers/sqlite3/LICENSE.txt +13 -0
  76. data/drivers/sqlite3/README.txt +6 -0
  77. data/drivers/sqlite3/Rakefile +26 -0
  78. data/drivers/sqlite3/lib/jdbc/sqlite3.rb +10 -0
  79. data/drivers/sqlite3/lib/sqlitejdbc-3.6.3.054.jar +0 -0
  80. data/lib/active_record/connection_adapters/cachedb_adapter.rb +1 -0
  81. data/lib/active_record/connection_adapters/derby_adapter.rb +13 -0
  82. data/lib/active_record/connection_adapters/h2_adapter.rb +13 -0
  83. data/lib/active_record/connection_adapters/hsqldb_adapter.rb +13 -0
  84. data/lib/active_record/connection_adapters/informix_adapter.rb +1 -0
  85. data/lib/active_record/connection_adapters/jdbc_adapter.rb +637 -0
  86. data/lib/active_record/connection_adapters/jdbc_adapter_spec.rb +14 -0
  87. data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -0
  88. data/lib/active_record/connection_adapters/mysql_adapter.rb +13 -0
  89. data/lib/active_record/connection_adapters/oracle_adapter.rb +1 -0
  90. data/lib/active_record/connection_adapters/postgresql_adapter.rb +13 -0
  91. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +13 -0
  92. data/lib/jdbc_adapter.rb +27 -0
  93. data/lib/jdbc_adapter/jdbc.rake +104 -0
  94. data/lib/jdbc_adapter/jdbc_cachedb.rb +33 -0
  95. data/lib/jdbc_adapter/jdbc_db2.rb +191 -0
  96. data/lib/jdbc_adapter/jdbc_derby.rb +421 -0
  97. data/lib/jdbc_adapter/jdbc_firebird.rb +109 -0
  98. data/lib/jdbc_adapter/jdbc_hsqldb.rb +210 -0
  99. data/lib/jdbc_adapter/jdbc_informix.rb +147 -0
  100. data/lib/jdbc_adapter/jdbc_mimer.rb +141 -0
  101. data/lib/jdbc_adapter/jdbc_mssql.rb +333 -0
  102. data/lib/jdbc_adapter/jdbc_mysql.rb +233 -0
  103. data/lib/jdbc_adapter/jdbc_oracle.rb +374 -0
  104. data/lib/jdbc_adapter/jdbc_postgre.rb +483 -0
  105. data/lib/jdbc_adapter/jdbc_sqlite3.rb +360 -0
  106. data/lib/jdbc_adapter/jdbc_sybase.rb +50 -0
  107. data/lib/jdbc_adapter/missing_functionality_helper.rb +85 -0
  108. data/lib/jdbc_adapter/rake_tasks.rb +10 -0
  109. data/lib/jdbc_adapter/tsql_helper.rb +60 -0
  110. data/lib/jdbc_adapter/version.rb +5 -0
  111. data/pom.xml +57 -0
  112. data/rakelib/compile.rake +23 -0
  113. data/rakelib/package.rake +73 -0
  114. data/rakelib/rails.rake +41 -0
  115. data/src/java/jdbc_adapter/JdbcAdapterInternalService.java +53 -0
  116. data/src/java/jdbc_adapter/JdbcConnectionFactory.java +36 -0
  117. data/src/java/jdbc_adapter/JdbcDerbySpec.java +293 -0
  118. data/src/java/jdbc_adapter/JdbcMySQLSpec.java +133 -0
  119. data/src/java/jdbc_adapter/MssqlRubyJdbcConnection.java +71 -0
  120. data/src/java/jdbc_adapter/PostgresRubyJdbcConnection.java +55 -0
  121. data/src/java/jdbc_adapter/RubyJdbcConnection.java +1163 -0
  122. data/src/java/jdbc_adapter/SQLBlock.java +27 -0
  123. data/src/java/jdbc_adapter/Sqlite3RubyJdbcConnection.java +41 -0
  124. data/test/activerecord/connection_adapters/type_conversion_test.rb +31 -0
  125. data/test/activerecord/connections/native_jdbc_mysql/connection.rb +25 -0
  126. data/test/activerecord/jall.sh +7 -0
  127. data/test/activerecord/jtest.sh +3 -0
  128. data/test/cachedb_simple_test.rb +6 -0
  129. data/test/db/cachedb.rb +9 -0
  130. data/test/db/db2.rb +9 -0
  131. data/test/db/derby.rb +14 -0
  132. data/test/db/h2.rb +11 -0
  133. data/test/db/hsqldb.rb +12 -0
  134. data/test/db/informix.rb +11 -0
  135. data/test/db/jdbc.rb +11 -0
  136. data/test/db/jndi_config.rb +30 -0
  137. data/test/db/logger.rb +3 -0
  138. data/test/db/mssql.rb +9 -0
  139. data/test/db/mysql.rb +10 -0
  140. data/test/db/oracle.rb +34 -0
  141. data/test/db/postgres.rb +9 -0
  142. data/test/db/sqlite3.rb +15 -0
  143. data/test/db2_simple_test.rb +10 -0
  144. data/test/derby_migration_test.rb +21 -0
  145. data/test/derby_multibyte_test.rb +12 -0
  146. data/test/derby_simple_test.rb +12 -0
  147. data/test/generic_jdbc_connection_test.rb +9 -0
  148. data/test/h2_simple_test.rb +6 -0
  149. data/test/has_many_through.rb +79 -0
  150. data/test/hsqldb_simple_test.rb +6 -0
  151. data/test/informix_simple_test.rb +48 -0
  152. data/test/jdbc_adapter/jdbc_db2_test.rb +26 -0
  153. data/test/jdbc_adapter/jdbc_sybase_test.rb +33 -0
  154. data/test/jdbc_common.rb +24 -0
  155. data/test/jndi_callbacks_test.rb +38 -0
  156. data/test/jndi_test.rb +35 -0
  157. data/test/manualTestDatabase.rb +191 -0
  158. data/test/minirunit.rb +109 -0
  159. data/test/minirunit/testConnect.rb +14 -0
  160. data/test/minirunit/testH2.rb +73 -0
  161. data/test/minirunit/testHsqldb.rb +73 -0
  162. data/test/minirunit/testLoadActiveRecord.rb +3 -0
  163. data/test/minirunit/testMysql.rb +83 -0
  164. data/test/minirunit/testRawSelect.rb +24 -0
  165. data/test/models/add_not_null_column_to_table.rb +12 -0
  166. data/test/models/auto_id.rb +18 -0
  167. data/test/models/data_types.rb +28 -0
  168. data/test/models/entry.rb +23 -0
  169. data/test/models/mixed_case.rb +20 -0
  170. data/test/models/reserved_word.rb +18 -0
  171. data/test/mssql_simple_test.rb +6 -0
  172. data/test/mysql_multibyte_test.rb +10 -0
  173. data/test/mysql_nonstandard_primary_key_test.rb +43 -0
  174. data/test/mysql_simple_test.rb +26 -0
  175. data/test/oracle_simple_test.rb +29 -0
  176. data/test/postgres_mixed_case_test.rb +19 -0
  177. data/test/postgres_nonseq_pkey_test.rb +40 -0
  178. data/test/postgres_reserved_test.rb +22 -0
  179. data/test/postgres_simple_test.rb +12 -0
  180. data/test/simple.rb +397 -0
  181. data/test/sqlite3_simple_test.rb +179 -0
  182. data/test/sybase_jtds_simple_test.rb +6 -0
  183. metadata +294 -0
@@ -0,0 +1,233 @@
1
+ require 'active_record/connection_adapters/abstract/schema_definitions'
2
+
3
+ module ::JdbcSpec
4
+ # Don't need to load native mysql adapter
5
+ $LOADED_FEATURES << "active_record/connection_adapters/mysql_adapter.rb"
6
+
7
+ module ActiveRecordExtensions
8
+ def mysql_connection(config)
9
+ config[:port] ||= 3306
10
+ url_options = "zeroDateTimeBehavior=convertToNull&jdbcCompliantTruncation=false&useUnicode=true&characterEncoding="
11
+ url_options << (config[:encoding] || 'utf8')
12
+ if config[:url]
13
+ config[:url] = config[:url]['?'] ? "#{config[:url]}&#{url_options}" : "#{config[:url]}?#{url_options}"
14
+ else
15
+ config[:url] = "jdbc:mysql://#{config[:host]}:#{config[:port]}/#{config[:database]}?#{url_options}"
16
+ end
17
+ config[:driver] = "com.mysql.jdbc.Driver"
18
+ connection = jdbc_connection(config)
19
+ ::JdbcSpec::MySQL.kill_cancel_timer(connection.raw_connection)
20
+ connection
21
+ end
22
+ end
23
+
24
+ module MySQL
25
+ def self.adapter_matcher(name, *)
26
+ name =~ /mysql/i ? self : false
27
+ end
28
+
29
+ def self.column_selector
30
+ [/mysql/i, lambda {|cfg,col| col.extend(::JdbcSpec::MySQL::Column)}]
31
+ end
32
+
33
+ def self.extended(adapter)
34
+ adapter.execute("SET SQL_AUTO_IS_NULL=0")
35
+ end
36
+
37
+ module Column
38
+ TYPES_ALLOWING_EMPTY_STRING_DEFAULT = Set.new([:binary, :string, :text])
39
+
40
+ def simplified_type(field_type)
41
+ return :boolean if field_type =~ /tinyint\(1\)|bit/i
42
+ return :string if field_type =~ /enum/i
43
+ super
44
+ end
45
+
46
+ def init_column(name, default, *args)
47
+ @original_default = default
48
+ @default = nil if missing_default_forged_as_empty_string?
49
+ end
50
+
51
+ # MySQL misreports NOT NULL column default when none is given.
52
+ # We can't detect this for columns which may have a legitimate ''
53
+ # default (string, text, binary) but we can for others (integer,
54
+ # datetime, boolean, and the rest).
55
+ #
56
+ # Test whether the column has default '', is not null, and is not
57
+ # a type allowing default ''.
58
+ def missing_default_forged_as_empty_string?
59
+ !null && @original_default == '' && !TYPES_ALLOWING_EMPTY_STRING_DEFAULT.include?(type)
60
+ end
61
+ end
62
+
63
+ def modify_types(tp)
64
+ tp[:primary_key] = "int(11) DEFAULT NULL auto_increment PRIMARY KEY"
65
+ tp[:decimal] = { :name => "decimal" }
66
+ tp[:timestamp] = { :name => "datetime" }
67
+ tp[:datetime][:limit] = nil
68
+ tp
69
+ end
70
+
71
+ # QUOTING ==================================================
72
+
73
+ def quote(value, column = nil)
74
+ return value.quoted_id if value.respond_to?(:quoted_id)
75
+
76
+ if column && column.type == :primary_key
77
+ value.to_s
78
+ elsif column && String === value && column.type == :binary && column.class.respond_to?(:string_to_binary)
79
+ s = column.class.string_to_binary(value).unpack("H*")[0]
80
+ "x'#{s}'"
81
+ elsif BigDecimal === value
82
+ "'#{value.to_s("F")}'"
83
+ else
84
+ super
85
+ end
86
+ end
87
+
88
+ def quoted_true
89
+ "1"
90
+ end
91
+
92
+ def quoted_false
93
+ "0"
94
+ end
95
+
96
+ def begin_db_transaction #:nodoc:
97
+ @connection.begin
98
+ rescue Exception
99
+ # Transactions aren't supported
100
+ end
101
+
102
+ def commit_db_transaction #:nodoc:
103
+ @connection.commit
104
+ rescue Exception
105
+ # Transactions aren't supported
106
+ end
107
+
108
+ def rollback_db_transaction #:nodoc:
109
+ @connection.rollback
110
+ rescue Exception
111
+ # Transactions aren't supported
112
+ end
113
+
114
+ def disable_referential_integrity(&block) #:nodoc:
115
+ old = select_value("SELECT @@FOREIGN_KEY_CHECKS")
116
+ begin
117
+ update("SET FOREIGN_KEY_CHECKS = 0")
118
+ yield
119
+ ensure
120
+ update("SET FOREIGN_KEY_CHECKS = #{old}")
121
+ end
122
+ end
123
+
124
+ # SCHEMA STATEMENTS ========================================
125
+
126
+ def structure_dump #:nodoc:
127
+ if supports_views?
128
+ sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
129
+ else
130
+ sql = "SHOW TABLES"
131
+ end
132
+
133
+ select_all(sql).inject("") do |structure, table|
134
+ table.delete('Table_type')
135
+
136
+ hash = select_one("SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}")
137
+
138
+ if(table = hash["Create Table"])
139
+ structure += table + ";\n\n"
140
+ elsif(view = hash["Create View"])
141
+ structure += view + ";\n\n"
142
+ end
143
+ end
144
+ end
145
+
146
+ def recreate_database(name) #:nodoc:
147
+ drop_database(name)
148
+ create_database(name)
149
+ end
150
+
151
+ def character_set(options) #:nodoc:
152
+ str = "CHARACTER SET `#{options[:charset] || 'utf8'}`"
153
+ str += " COLLATE `#{options[:collation]}`" if options[:collation]
154
+ str
155
+ end
156
+ private :character_set
157
+
158
+ def create_database(name, options = {}) #:nodoc:
159
+ execute "CREATE DATABASE `#{name}` DEFAULT #{character_set(options)}"
160
+ end
161
+
162
+ def drop_database(name) #:nodoc:
163
+ execute "DROP DATABASE IF EXISTS `#{name}`"
164
+ end
165
+
166
+ def current_database
167
+ select_one("SELECT DATABASE() as db")["db"]
168
+ end
169
+
170
+ def create_table(name, options = {}) #:nodoc:
171
+ super(name, {:options => "ENGINE=InnoDB #{character_set(options)}"}.merge(options))
172
+ end
173
+
174
+ def rename_table(name, new_name)
175
+ execute "RENAME TABLE #{quote_table_name(name)} TO #{quote_table_name(new_name)}"
176
+ end
177
+
178
+ def change_column_default(table_name, column_name, default) #:nodoc:
179
+ current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
180
+
181
+ execute("ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{current_type} DEFAULT #{quote(default)}")
182
+ end
183
+
184
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
185
+ unless options_include_default?(options)
186
+ if column = columns(table_name).find { |c| c.name == column_name.to_s }
187
+ options[:default] = column.default
188
+ else
189
+ raise "No such column: #{table_name}.#{column_name}"
190
+ end
191
+ end
192
+
193
+ 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])}"
194
+ add_column_options!(change_column_sql, options)
195
+ execute(change_column_sql)
196
+ end
197
+
198
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
199
+ cols = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")
200
+ current_type = cols["Type"] || cols["COLUMN_TYPE"]
201
+ execute "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_table_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
202
+ end
203
+
204
+ def add_limit_offset!(sql, options) #:nodoc:
205
+ if limit = options[:limit]
206
+ unless offset = options[:offset]
207
+ sql << " LIMIT #{limit}"
208
+ else
209
+ sql << " LIMIT #{offset}, #{limit}"
210
+ end
211
+ end
212
+ end
213
+
214
+ def show_variable(var)
215
+ res = execute("show variables like '#{var}'")
216
+ row = res.detect {|row| row["Variable_name"] == var }
217
+ row && row["Value"]
218
+ end
219
+
220
+ def charset
221
+ show_variable("character_set_database")
222
+ end
223
+
224
+ def collation
225
+ show_variable("collation_database")
226
+ end
227
+
228
+ private
229
+ def supports_views?
230
+ false
231
+ end
232
+ end
233
+ end
@@ -0,0 +1,374 @@
1
+ module ::JdbcSpec
2
+ module ActiveRecordExtensions
3
+ def oracle_connection(config)
4
+ config[:port] ||= 1521
5
+ config[:url] ||= "jdbc:oracle:thin:@#{config[:host]}:#{config[:port]}:#{config[:database]}"
6
+ config[:driver] ||= "oracle.jdbc.driver.OracleDriver"
7
+ jdbc_connection(config)
8
+ end
9
+ end
10
+
11
+ module Oracle
12
+ def self.extended(mod)
13
+ unless @lob_callback_added
14
+ ActiveRecord::Base.class_eval do
15
+ def after_save_with_oracle_lob
16
+ self.class.columns.select { |c| c.sql_type =~ /LOB\(|LOB$/i }.each do |c|
17
+ value = self[c.name]
18
+ value = value.to_yaml if unserializable_attribute?(c.name, c)
19
+ next if value.nil? || (value == '')
20
+
21
+ connection.write_large_object(c.type == :binary, c.name, self.class.table_name, self.class.primary_key, quote_value(id), value)
22
+ end
23
+ end
24
+ end
25
+
26
+ ActiveRecord::Base.after_save :after_save_with_oracle_lob
27
+ @lob_callback_added = true
28
+ end
29
+ end
30
+
31
+ def self.adapter_matcher(name, *)
32
+ name =~ /oracle/i ? self : false
33
+ end
34
+
35
+ def self.column_selector
36
+ [/oracle/i, lambda {|cfg,col| col.extend(::JdbcSpec::Oracle::Column)}]
37
+ end
38
+
39
+ module Column
40
+ def type_cast(value)
41
+ return nil if value.nil?
42
+ case type
43
+ when :datetime then JdbcSpec::Oracle::Column.string_to_time(value, self.class)
44
+ else
45
+ super
46
+ end
47
+ end
48
+
49
+ def type_cast_code(var_name)
50
+ case type
51
+ when :datetime then "JdbcSpec::Oracle::Column.string_to_time(#{var_name}, self.class)"
52
+ else
53
+ super
54
+ end
55
+ end
56
+
57
+ def self.string_to_time(string, klass)
58
+ time = klass.string_to_time(string)
59
+ guess_date_or_time(time)
60
+ end
61
+
62
+ def self.guess_date_or_time(value)
63
+ (value.hour == 0 && value.min == 0 && value.sec == 0) ?
64
+ new_date(value.year, value.month, value.day) : value
65
+ end
66
+
67
+ private
68
+ def simplified_type(field_type)
69
+ case field_type
70
+ when /^number\(1\)$/i then :boolean
71
+ when /char/i then :string
72
+ when /float|double/i then :float
73
+ when /int/i then :integer
74
+ when /num|dec|real/i then (@scale.nil? || @scale == 0) ? :integer : :decimal
75
+ when /date|time/i then :datetime
76
+ when /clob/i then :text
77
+ when /blob/i then :binary
78
+ end
79
+ end
80
+
81
+ # Post process default value from JDBC into a Rails-friendly format (columns{-internal})
82
+ def default_value(value)
83
+ return nil unless value
84
+
85
+ # Not sure why we need this for Oracle?
86
+ value = value.strip
87
+
88
+ return nil if value == "null"
89
+
90
+ # sysdate default should be treated like a null value
91
+ return nil if value.downcase == "sysdate"
92
+
93
+ # jdbc returns column default strings with actual single quotes around the value.
94
+ return $1 if value =~ /^'(.*)'$/
95
+
96
+ value
97
+ end
98
+ end
99
+
100
+ def adapter_name
101
+ 'oracle'
102
+ end
103
+
104
+ def table_alias_length
105
+ 30
106
+ end
107
+
108
+ def default_sequence_name(table, column = nil) #:nodoc:
109
+ "#{table}_seq"
110
+ end
111
+
112
+ def create_table(name, options = {}) #:nodoc:
113
+ super(name, options)
114
+ seq_name = options[:sequence_name] || "#{name}_seq"
115
+ raise ActiveRecord::StatementInvalid.new("name #{seq_name} too long") if seq_name.length > table_alias_length
116
+ execute "CREATE SEQUENCE #{seq_name} START WITH 10000" unless options[:id] == false
117
+ end
118
+
119
+ def rename_table(name, new_name) #:nodoc:
120
+ execute "RENAME #{name} TO #{new_name}"
121
+ execute "RENAME #{name}_seq TO #{new_name}_seq" rescue nil
122
+ end
123
+
124
+ def drop_table(name, options = {}) #:nodoc:
125
+ super(name)
126
+ seq_name = options[:sequence_name] || "#{name}_seq"
127
+ execute "DROP SEQUENCE #{seq_name}" rescue nil
128
+ end
129
+
130
+ def recreate_database(name)
131
+ tables.each{ |table| drop_table(table) }
132
+ end
133
+
134
+ def drop_database(name)
135
+ recreate_database(name)
136
+ end
137
+
138
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
139
+ if id_value # Pre-assigned id
140
+ execute sql, name
141
+ else # Assume the sql contains a bind-variable for the id
142
+ # Extract the table from the insert sql. Yuck.
143
+ table = sql.split(" ", 4)[2].gsub('"', '')
144
+ sequence_name ||= default_sequence_name(table)
145
+ id_value = select_one("select #{sequence_name}.nextval id from dual")['id'].to_i
146
+ log(sql, name) do
147
+ @connection.execute_id_insert(sql,id_value)
148
+ end
149
+ end
150
+ id_value
151
+ end
152
+
153
+ def indexes(table, name = nil)
154
+ @connection.indexes(table, name, @connection.connection.meta_data.user_name)
155
+ end
156
+
157
+ def _execute(sql, name = nil)
158
+ case sql.strip
159
+ when /\A\(?\s*(select|show)/i then
160
+ @connection.execute_query(sql)
161
+ else
162
+ @connection.execute_update(sql)
163
+ end
164
+ end
165
+
166
+ def modify_types(tp)
167
+ tp[:primary_key] = "NUMBER(38) NOT NULL PRIMARY KEY"
168
+ tp[:integer] = { :name => "NUMBER", :limit => 38 }
169
+ tp[:datetime] = { :name => "DATE" }
170
+ tp[:timestamp] = { :name => "DATE" }
171
+ tp[:time] = { :name => "DATE" }
172
+ tp[:date] = { :name => "DATE" }
173
+ tp
174
+ end
175
+
176
+ def add_limit_offset!(sql, options) #:nodoc:
177
+ offset = options[:offset] || 0
178
+
179
+ if limit = options[:limit]
180
+ sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_ where rownum <= #{offset+limit}) where raw_rnum_ > #{offset}"
181
+ elsif offset > 0
182
+ sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_) where raw_rnum_ > #{offset}"
183
+ end
184
+ end
185
+
186
+ def current_database #:nodoc:
187
+ select_one("select sys_context('userenv','db_name') db from dual")["db"]
188
+ end
189
+
190
+ def remove_index(table_name, options = {}) #:nodoc:
191
+ execute "DROP INDEX #{index_name(table_name, options)}"
192
+ end
193
+
194
+ def change_column_default(table_name, column_name, default) #:nodoc:
195
+ execute "ALTER TABLE #{table_name} MODIFY #{column_name} DEFAULT #{quote(default)}"
196
+ end
197
+
198
+ def add_column_options!(sql, options) #:nodoc:
199
+ # handle case of defaults for CLOB columns, which would otherwise get "quoted" incorrectly
200
+ if options_include_default?(options) && (column = options[:column]) && column.type == :text
201
+ sql << " DEFAULT #{quote(options.delete(:default))}"
202
+ end
203
+ super
204
+ end
205
+
206
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
207
+ change_column_sql = "ALTER TABLE #{table_name} MODIFY #{column_name} #{type_to_sql(type, options[:limit])}"
208
+ add_column_options!(change_column_sql, options)
209
+ execute(change_column_sql)
210
+ end
211
+
212
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
213
+ execute "ALTER TABLE #{table_name} RENAME COLUMN #{column_name} to #{new_column_name}"
214
+ end
215
+
216
+ def remove_column(table_name, column_name) #:nodoc:
217
+ execute "ALTER TABLE #{table_name} DROP COLUMN #{column_name}"
218
+ end
219
+
220
+ def structure_dump #:nodoc:
221
+ s = select_all("select sequence_name from user_sequences").inject("") do |structure, seq|
222
+ structure << "create sequence #{seq.to_a.first.last};\n\n"
223
+ end
224
+
225
+ select_all("select table_name from user_tables").inject(s) do |structure, table|
226
+ ddl = "create table #{table.to_a.first.last} (\n "
227
+ cols = select_all(%Q{
228
+ select column_name, data_type, data_length, data_precision, data_scale, data_default, nullable
229
+ from user_tab_columns
230
+ where table_name = '#{table.to_a.first.last}'
231
+ order by column_id
232
+ }).map do |row|
233
+ row = row.inject({}) do |h,args|
234
+ h[args[0].downcase] = args[1]
235
+ h
236
+ end
237
+ col = "#{row['column_name'].downcase} #{row['data_type'].downcase}"
238
+ if row['data_type'] =='NUMBER' and !row['data_precision'].nil?
239
+ col << "(#{row['data_precision'].to_i}"
240
+ col << ",#{row['data_scale'].to_i}" if !row['data_scale'].nil?
241
+ col << ')'
242
+ elsif row['data_type'].include?('CHAR')
243
+ col << "(#{row['data_length'].to_i})"
244
+ end
245
+ col << " default #{row['data_default']}" if !row['data_default'].nil?
246
+ col << ' not null' if row['nullable'] == 'N'
247
+ col
248
+ end
249
+ ddl << cols.join(",\n ")
250
+ ddl << ");\n\n"
251
+ structure << ddl
252
+ end
253
+ end
254
+
255
+ def structure_drop #:nodoc:
256
+ s = select_all("select sequence_name from user_sequences").inject("") do |drop, seq|
257
+ drop << "drop sequence #{seq.to_a.first.last};\n\n"
258
+ end
259
+
260
+ select_all("select table_name from user_tables").inject(s) do |drop, table|
261
+ drop << "drop table #{table.to_a.first.last} cascade constraints;\n\n"
262
+ end
263
+ end
264
+
265
+ # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
266
+ #
267
+ # Oracle requires the ORDER BY columns to be in the SELECT list for DISTINCT
268
+ # queries. However, with those columns included in the SELECT DISTINCT list, you
269
+ # won't actually get a distinct list of the column you want (presuming the column
270
+ # has duplicates with multiple values for the ordered-by columns. So we use the
271
+ # FIRST_VALUE function to get a single (first) value for each column, effectively
272
+ # making every row the same.
273
+ #
274
+ # distinct("posts.id", "posts.created_at desc")
275
+ def distinct(columns, order_by)
276
+ return "DISTINCT #{columns}" if order_by.blank?
277
+
278
+ # construct a valid DISTINCT clause, ie. one that includes the ORDER BY columns, using
279
+ # FIRST_VALUE such that the inclusion of these columns doesn't invalidate the DISTINCT
280
+ order_columns = order_by.split(',').map { |s| s.strip }.reject(&:blank?)
281
+ order_columns = order_columns.zip((0...order_columns.size).to_a).map do |c, i|
282
+ "FIRST_VALUE(#{c.split.first}) OVER (PARTITION BY #{columns} ORDER BY #{c}) AS alias_#{i}__"
283
+ end
284
+ sql = "DISTINCT #{columns}, "
285
+ sql << order_columns * ", "
286
+ end
287
+
288
+ # ORDER BY clause for the passed order option.
289
+ #
290
+ # Uses column aliases as defined by #distinct.
291
+ def add_order_by_for_association_limiting!(sql, options)
292
+ return sql if options[:order].blank?
293
+
294
+ order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?)
295
+ order.map! {|s| $1 if s =~ / (.*)/}
296
+ order = order.zip((0...order.size).to_a).map { |s,i| "alias_#{i}__ #{s}" }.join(', ')
297
+
298
+ sql << "ORDER BY #{order}"
299
+ end
300
+
301
+ def tables
302
+ @connection.tables(nil, oracle_schema)
303
+ end
304
+
305
+ def columns(table_name, name=nil)
306
+ @connection.columns_internal(table_name, name, oracle_schema)
307
+ end
308
+
309
+ # QUOTING ==================================================
310
+ #
311
+ # see: abstract/quoting.rb
312
+
313
+ # Camelcase column names need to be quoted.
314
+ # Nonquoted identifiers can contain only alphanumeric characters from your
315
+ # database character set and the underscore (_), dollar sign ($), and pound sign (#).
316
+ # Database links can also contain periods (.) and "at" signs (@).
317
+ # Oracle strongly discourages you from using $ and # in nonquoted identifiers.
318
+ # Source: http://download.oracle.com/docs/cd/B28359_01/server.111/b28286/sql_elements008.htm
319
+ def quote_column_name(name) #:nodoc:
320
+ name.to_s =~ /^[a-z0-9_$#]+$/ ? name.to_s : "\"#{name}\""
321
+ end
322
+
323
+ def quote_string(string) #:nodoc:
324
+ string.gsub(/'/, "''")
325
+ end
326
+
327
+ def quote(value, column = nil) #:nodoc:
328
+ if column && [:text, :binary].include?(column.type)
329
+ if /(.*?)\([0-9]+\)/ =~ column.sql_type
330
+ %Q{empty_#{ $1.downcase }()}
331
+ else
332
+ %Q{empty_#{ column.sql_type.downcase rescue 'blob' }()}
333
+ end
334
+ else
335
+ if column.respond_to?(:primary) && column.primary
336
+ return value.to_i.to_s
337
+ end
338
+ quoted = super
339
+ if value.acts_like?(:date) || value.acts_like?(:time)
340
+ quoted = "#{quoted_date(value)}"
341
+ end
342
+ quoted
343
+ end
344
+ end
345
+
346
+ def quoted_date(value)
347
+ %Q{TIMESTAMP'#{value.strftime("%Y-%m-%d %H:%M:%S")}'}
348
+ end
349
+
350
+ def quoted_true #:nodoc:
351
+ '1'
352
+ end
353
+
354
+ def quoted_false #:nodoc:
355
+ '0'
356
+ end
357
+
358
+ private
359
+ # In Oracle, schemas are created under your username:
360
+ # http://www.oracle.com/technology/obe/2day_dba/schema/schema.htm
361
+ def oracle_schema
362
+ @config[:username].to_s if @config[:username]
363
+ end
364
+
365
+ def select(sql, name=nil)
366
+ records = execute(sql,name)
367
+ records.each do |col|
368
+ col.delete('raw_rnum_')
369
+ end
370
+ records
371
+ end
372
+ end
373
+ end
374
+