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.
- data/History.txt +296 -0
- data/LICENSE.txt +21 -0
- data/Manifest.txt +139 -0
- data/README.txt +219 -0
- data/Rakefile +10 -0
- data/lib/active_record/connection_adapters/cachedb_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/derby_adapter.rb +13 -0
- data/lib/active_record/connection_adapters/h2_adapter.rb +13 -0
- data/lib/active_record/connection_adapters/hsqldb_adapter.rb +13 -0
- data/lib/active_record/connection_adapters/informix_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/jdbc_adapter.rb +661 -0
- data/lib/active_record/connection_adapters/jdbc_adapter_spec.rb +26 -0
- data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mssql_adapter.rb +13 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +13 -0
- data/lib/active_record/connection_adapters/oracle_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +13 -0
- data/lib/activerecord-jdbc-adapter.rb +6 -0
- data/lib/arel/engines/sql/compilers/db2_compiler.rb +9 -0
- data/lib/arel/engines/sql/compilers/derby_compiler.rb +6 -0
- data/lib/arel/engines/sql/compilers/h2_compiler.rb +6 -0
- data/lib/arel/engines/sql/compilers/hsqldb_compiler.rb +6 -0
- data/lib/arel/engines/sql/compilers/jdbc_compiler.rb +6 -0
- data/lib/generators/jdbc/jdbc_generator.rb +9 -0
- data/lib/jdbc_adapter.rb +27 -0
- data/lib/jdbc_adapter/jdbc.rake +122 -0
- data/lib/jdbc_adapter/jdbc_adapter_internal.jar +0 -0
- data/lib/jdbc_adapter/jdbc_cachedb.rb +33 -0
- data/lib/jdbc_adapter/jdbc_db2.rb +222 -0
- data/lib/jdbc_adapter/jdbc_derby.rb +426 -0
- data/lib/jdbc_adapter/jdbc_firebird.rb +109 -0
- data/lib/jdbc_adapter/jdbc_hsqldb.rb +221 -0
- data/lib/jdbc_adapter/jdbc_informix.rb +147 -0
- data/lib/jdbc_adapter/jdbc_mimer.rb +145 -0
- data/lib/jdbc_adapter/jdbc_mssql.rb +468 -0
- data/lib/jdbc_adapter/jdbc_mysql.rb +260 -0
- data/lib/jdbc_adapter/jdbc_oracle.rb +397 -0
- data/lib/jdbc_adapter/jdbc_postgre.rb +531 -0
- data/lib/jdbc_adapter/jdbc_sqlite3.rb +386 -0
- data/lib/jdbc_adapter/jdbc_sybase.rb +50 -0
- data/lib/jdbc_adapter/missing_functionality_helper.rb +87 -0
- data/lib/jdbc_adapter/railtie.rb +9 -0
- data/lib/jdbc_adapter/rake_tasks.rb +10 -0
- data/lib/jdbc_adapter/tsql_helper.rb +69 -0
- data/lib/jdbc_adapter/version.rb +5 -0
- data/lib/pg.rb +4 -0
- data/rails_generators/jdbc_generator.rb +15 -0
- data/rails_generators/templates/config/initializers/jdbc.rb +7 -0
- data/rails_generators/templates/lib/tasks/jdbc.rake +8 -0
- data/rakelib/compile.rake +23 -0
- data/rakelib/package.rake +91 -0
- data/rakelib/rails.rake +41 -0
- data/rakelib/test.rake +78 -0
- data/src/java/jdbc_adapter/JdbcAdapterInternalService.java +53 -0
- data/src/java/jdbc_adapter/JdbcConnectionFactory.java +36 -0
- data/src/java/jdbc_adapter/JdbcDerbySpec.java +293 -0
- data/src/java/jdbc_adapter/JdbcMySQLSpec.java +134 -0
- data/src/java/jdbc_adapter/MssqlRubyJdbcConnection.java +71 -0
- data/src/java/jdbc_adapter/PostgresRubyJdbcConnection.java +55 -0
- data/src/java/jdbc_adapter/RubyJdbcConnection.java +1176 -0
- data/src/java/jdbc_adapter/SQLBlock.java +27 -0
- data/src/java/jdbc_adapter/Sqlite3RubyJdbcConnection.java +41 -0
- data/test/abstract_db_create.rb +107 -0
- data/test/activerecord/connection_adapters/type_conversion_test.rb +31 -0
- data/test/activerecord/connections/native_jdbc_mysql/connection.rb +25 -0
- data/test/cachedb_simple_test.rb +6 -0
- data/test/db/cachedb.rb +9 -0
- data/test/db/db2.rb +9 -0
- data/test/db/derby.rb +14 -0
- data/test/db/h2.rb +11 -0
- data/test/db/hsqldb.rb +12 -0
- data/test/db/informix.rb +11 -0
- data/test/db/jdbc.rb +11 -0
- data/test/db/jndi_config.rb +30 -0
- data/test/db/logger.rb +3 -0
- data/test/db/mssql.rb +9 -0
- data/test/db/mysql.rb +10 -0
- data/test/db/oracle.rb +34 -0
- data/test/db/postgres.rb +9 -0
- data/test/db/sqlite3.rb +15 -0
- data/test/db2_simple_test.rb +10 -0
- data/test/derby_migration_test.rb +21 -0
- data/test/derby_multibyte_test.rb +12 -0
- data/test/derby_simple_test.rb +21 -0
- data/test/generic_jdbc_connection_test.rb +9 -0
- data/test/h2_simple_test.rb +6 -0
- data/test/has_many_through.rb +79 -0
- data/test/helper.rb +5 -0
- data/test/hsqldb_simple_test.rb +6 -0
- data/test/informix_simple_test.rb +48 -0
- data/test/jdbc_adapter/jdbc_db2_test.rb +26 -0
- data/test/jdbc_adapter/jdbc_sybase_test.rb +33 -0
- data/test/jdbc_common.rb +25 -0
- data/test/jndi_callbacks_test.rb +38 -0
- data/test/jndi_test.rb +35 -0
- data/test/manualTestDatabase.rb +191 -0
- data/test/minirunit.rb +109 -0
- data/test/minirunit/testConnect.rb +14 -0
- data/test/minirunit/testH2.rb +73 -0
- data/test/minirunit/testHsqldb.rb +73 -0
- data/test/minirunit/testLoadActiveRecord.rb +3 -0
- data/test/minirunit/testMysql.rb +83 -0
- data/test/minirunit/testRawSelect.rb +24 -0
- data/test/models/add_not_null_column_to_table.rb +12 -0
- data/test/models/auto_id.rb +18 -0
- data/test/models/data_types.rb +28 -0
- data/test/models/entry.rb +23 -0
- data/test/models/mixed_case.rb +20 -0
- data/test/models/reserved_word.rb +18 -0
- data/test/models/string_id.rb +18 -0
- data/test/models/validates_uniqueness_of_string.rb +19 -0
- data/test/mssql_db_create_test.rb +26 -0
- data/test/mssql_identity_insert_test.rb +19 -0
- data/test/mssql_legacy_types_test.rb +58 -0
- data/test/mssql_limit_offset_test.rb +108 -0
- data/test/mssql_multibyte_test.rb +18 -0
- data/test/mssql_simple_test.rb +49 -0
- data/test/mysql_db_create_test.rb +25 -0
- data/test/mysql_info_test.rb +62 -0
- data/test/mysql_multibyte_test.rb +10 -0
- data/test/mysql_nonstandard_primary_key_test.rb +42 -0
- data/test/mysql_simple_test.rb +32 -0
- data/test/oracle_simple_test.rb +54 -0
- data/test/pick_rails_version.rb +3 -0
- data/test/postgres_db_create_test.rb +21 -0
- data/test/postgres_mixed_case_test.rb +19 -0
- data/test/postgres_nonseq_pkey_test.rb +40 -0
- data/test/postgres_reserved_test.rb +22 -0
- data/test/postgres_schema_search_path_test.rb +46 -0
- data/test/postgres_simple_test.rb +13 -0
- data/test/simple.rb +494 -0
- data/test/sqlite3_simple_test.rb +233 -0
- data/test/sybase_jtds_simple_test.rb +6 -0
- 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
|
+
|