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