activerecord-jdbc-adapter 0.9.3-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 +248 -0
- data/LICENSE.txt +21 -0
- data/Manifest.txt +125 -0
- data/README.txt +218 -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 +640 -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/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/generators/jdbc/jdbc_generator.rb +9 -0
- data/lib/jdbc_adapter.rb +27 -0
- data/lib/jdbc_adapter/jdbc.rake +121 -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 +203 -0
- data/lib/jdbc_adapter/jdbc_derby.rb +430 -0
- data/lib/jdbc_adapter/jdbc_firebird.rb +109 -0
- data/lib/jdbc_adapter/jdbc_hsqldb.rb +218 -0
- data/lib/jdbc_adapter/jdbc_informix.rb +147 -0
- data/lib/jdbc_adapter/jdbc_mimer.rb +141 -0
- data/lib/jdbc_adapter/jdbc_mssql.rb +337 -0
- data/lib/jdbc_adapter/jdbc_mysql.rb +236 -0
- data/lib/jdbc_adapter/jdbc_oracle.rb +377 -0
- data/lib/jdbc_adapter/jdbc_postgre.rb +498 -0
- data/lib/jdbc_adapter/jdbc_sqlite3.rb +384 -0
- data/lib/jdbc_adapter/jdbc_sybase.rb +50 -0
- data/lib/jdbc_adapter/missing_functionality_helper.rb +87 -0
- data/lib/jdbc_adapter/rake_tasks.rb +10 -0
- data/lib/jdbc_adapter/tsql_helper.rb +60 -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 +90 -0
- data/rakelib/rails.rake +41 -0
- data/rakelib/test.rake +76 -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 +1162 -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_simple_test.rb +6 -0
- data/test/mysql_db_create_test.rb +25 -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 +29 -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 +475 -0
- data/test/sqlite3_simple_test.rb +233 -0
- data/test/sybase_jtds_simple_test.rb +6 -0
- metadata +188 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
module ::JdbcSpec
|
|
2
|
+
# Don't need to load native postgres adapter
|
|
3
|
+
$LOADED_FEATURES << "active_record/connection_adapters/sqlite3_adapter.rb"
|
|
4
|
+
|
|
5
|
+
module ActiveRecordExtensions
|
|
6
|
+
add_method_to_remove_from_ar_base(:sqlite3_connection)
|
|
7
|
+
|
|
8
|
+
def sqlite3_connection(config)
|
|
9
|
+
require File.dirname(__FILE__) + "/../active_record/connection_adapters/sqlite3_adapter"
|
|
10
|
+
|
|
11
|
+
parse_sqlite3_config!(config)
|
|
12
|
+
|
|
13
|
+
config[:url] ||= "jdbc:sqlite:#{config[:database]}"
|
|
14
|
+
config[:driver] ||= "org.sqlite.JDBC"
|
|
15
|
+
jdbc_connection(config)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def parse_sqlite3_config!(config)
|
|
19
|
+
config[:database] ||= config[:dbfile]
|
|
20
|
+
|
|
21
|
+
# Allow database path relative to RAILS_ROOT, but only if
|
|
22
|
+
# the database path is not the special path that tells
|
|
23
|
+
# Sqlite to build a database only in memory.
|
|
24
|
+
if Object.const_defined?(:RAILS_ROOT) && ':memory:' != config[:database]
|
|
25
|
+
config[:database] = File.expand_path(config[:database], RAILS_ROOT)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
module SQLite3
|
|
31
|
+
def self.extended(base)
|
|
32
|
+
base.class.class_eval do
|
|
33
|
+
alias_chained_method :insert, :query_dirty, :insert
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.adapter_matcher(name, *)
|
|
38
|
+
name =~ /sqlite/i ? self : false
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.column_selector
|
|
42
|
+
[/sqlite/i, lambda {|cfg,col| col.extend(::JdbcSpec::SQLite3::Column)}]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.jdbc_connection_class
|
|
46
|
+
::ActiveRecord::ConnectionAdapters::Sqlite3JdbcConnection
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
module Column
|
|
50
|
+
def init_column(name, default, *args)
|
|
51
|
+
@default = '' if default =~ /NULL/
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def type_cast(value)
|
|
55
|
+
return nil if value.nil?
|
|
56
|
+
case type
|
|
57
|
+
when :string then value
|
|
58
|
+
when :integer then JdbcSpec::SQLite3::Column.cast_to_integer(value)
|
|
59
|
+
when :primary_key then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
|
|
60
|
+
when :float then value.to_f
|
|
61
|
+
when :datetime then JdbcSpec::SQLite3::Column.cast_to_date_or_time(value)
|
|
62
|
+
when :date then JdbcSpec::SQLite3::Column.cast_to_date_or_time(value)
|
|
63
|
+
when :time then JdbcSpec::SQLite3::Column.cast_to_time(value)
|
|
64
|
+
when :decimal then self.class.value_to_decimal(value)
|
|
65
|
+
when :boolean then self.class.value_to_boolean(value)
|
|
66
|
+
else value
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def type_cast_code(var_name)
|
|
71
|
+
case type
|
|
72
|
+
when :integer then "JdbcSpec::SQLite3::Column.cast_to_integer(#{var_name})"
|
|
73
|
+
when :datetime then "JdbcSpec::SQLite3::Column.cast_to_date_or_time(#{var_name})"
|
|
74
|
+
when :date then "JdbcSpec::SQLite3::Column.cast_to_date_or_time(#{var_name})"
|
|
75
|
+
when :time then "JdbcSpec::SQLite3::Column.cast_to_time(#{var_name})"
|
|
76
|
+
else
|
|
77
|
+
super
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
def simplified_type(field_type)
|
|
83
|
+
case field_type
|
|
84
|
+
when /boolean/i then :boolean
|
|
85
|
+
when /text/i then :text
|
|
86
|
+
when /varchar/i then :string
|
|
87
|
+
when /int/i then :integer
|
|
88
|
+
when /float/i then :float
|
|
89
|
+
when /real/i then @scale == 0 ? :integer : :decimal
|
|
90
|
+
when /datetime/i then :datetime
|
|
91
|
+
when /date/i then :date
|
|
92
|
+
when /time/i then :time
|
|
93
|
+
when /blob/i then :binary
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def extract_precision(sql_type)
|
|
98
|
+
case sql_type
|
|
99
|
+
when /^(real)\((\d+)(,\d+)?\)/i then $2.to_i
|
|
100
|
+
else super
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def extract_scale(sql_type)
|
|
105
|
+
case sql_type
|
|
106
|
+
when /^(real)\((\d+)\)/i then 0
|
|
107
|
+
when /^(real)\((\d+)(,(\d+))\)/i then $4.to_i
|
|
108
|
+
else super
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Post process default value from JDBC into a Rails-friendly format (columns{-internal})
|
|
113
|
+
def default_value(value)
|
|
114
|
+
# jdbc returns column default strings with actual single quotes around the value.
|
|
115
|
+
return $1 if value =~ /^'(.*)'$/
|
|
116
|
+
|
|
117
|
+
value
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def self.cast_to_integer(value)
|
|
121
|
+
return nil if value =~ /NULL/ or value.to_s.empty? or value.nil?
|
|
122
|
+
return (value.to_i) ? value.to_i : (value ? 1 : 0)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def self.cast_to_date_or_time(value)
|
|
126
|
+
return value if value.is_a? Date
|
|
127
|
+
return nil if value.blank?
|
|
128
|
+
guess_date_or_time((value.is_a? Time) ? value : cast_to_time(value))
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def self.cast_to_time(value)
|
|
132
|
+
return value if value.is_a? Time
|
|
133
|
+
time_array = ParseDate.parsedate value
|
|
134
|
+
time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1;
|
|
135
|
+
Time.send(ActiveRecord::Base.default_timezone, *time_array) rescue nil
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def self.guess_date_or_time(value)
|
|
139
|
+
(value.hour == 0 and value.min == 0 and value.sec == 0) ?
|
|
140
|
+
Date.new(value.year, value.month, value.day) : value
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def adapter_name #:nodoc:
|
|
145
|
+
'SQLite'
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def supports_count_distinct? #:nodoc:
|
|
149
|
+
sqlite_version >= '3.2.6'
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def supports_autoincrement? #:nodoc:
|
|
153
|
+
sqlite_version >= '3.1.0'
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def sqlite_version
|
|
157
|
+
@sqlite_version ||= select_value('select sqlite_version(*)')
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def modify_types(tp)
|
|
161
|
+
tp[:primary_key] = "INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL"
|
|
162
|
+
tp[:string] = { :name => "VARCHAR", :limit => 255 }
|
|
163
|
+
tp[:float] = { :name => "REAL" }
|
|
164
|
+
tp[:decimal] = { :name => "REAL" }
|
|
165
|
+
tp[:datetime] = { :name => "DATETIME" }
|
|
166
|
+
tp[:timestamp] = { :name => "DATETIME" }
|
|
167
|
+
tp[:time] = { :name => "TIME" }
|
|
168
|
+
tp[:date] = { :name => "DATE" }
|
|
169
|
+
tp[:boolean] = { :name => "BOOLEAN" }
|
|
170
|
+
tp
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def quote(value, column = nil) # :nodoc:
|
|
174
|
+
return value.quoted_id if value.respond_to?(:quoted_id)
|
|
175
|
+
case value
|
|
176
|
+
when String
|
|
177
|
+
if column && column.type == :binary
|
|
178
|
+
"'#{quote_string(column.class.string_to_binary(value))}'"
|
|
179
|
+
elsif column && [:integer, :float].include?(column.type)
|
|
180
|
+
(column.type == :integer ? value.to_i : value.to_f).to_s
|
|
181
|
+
elsif column && column.respond_to?(:primary) && column.primary && column.klass != String
|
|
182
|
+
value.to_i.to_s
|
|
183
|
+
else
|
|
184
|
+
"'#{quote_string(value)}'"
|
|
185
|
+
end
|
|
186
|
+
else super
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def quote_column_name(name) #:nodoc:
|
|
191
|
+
name = name.to_s
|
|
192
|
+
# Did not find reference on values needing quoting, but these
|
|
193
|
+
# happen in AR unit tests
|
|
194
|
+
if name == "references" || name =~ /-/
|
|
195
|
+
%Q("#{name}")
|
|
196
|
+
else
|
|
197
|
+
name
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def quote_string(str)
|
|
202
|
+
str.gsub(/'/, "''")
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def quoted_true
|
|
206
|
+
%Q{'t'}
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def quoted_false
|
|
210
|
+
%Q{'f'}
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def add_column(table_name, column_name, type, options = {})
|
|
214
|
+
if option_not_null = options[:null] == false
|
|
215
|
+
option_not_null = options.delete(:null)
|
|
216
|
+
end
|
|
217
|
+
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])}"
|
|
218
|
+
add_column_options!(add_column_sql, options)
|
|
219
|
+
execute(add_column_sql)
|
|
220
|
+
if option_not_null
|
|
221
|
+
alter_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} NOT NULL"
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def remove_column(table_name, *column_names) #:nodoc:
|
|
226
|
+
column_names.flatten.each do |column_name|
|
|
227
|
+
alter_table(table_name) do |definition|
|
|
228
|
+
definition.columns.delete(definition[column_name])
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
alias :remove_columns :remove_column
|
|
233
|
+
|
|
234
|
+
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
|
235
|
+
alter_table(table_name) do |definition|
|
|
236
|
+
include_default = options_include_default?(options)
|
|
237
|
+
definition[column_name].instance_eval do
|
|
238
|
+
self.type = type
|
|
239
|
+
self.limit = options[:limit] if options.include?(:limit)
|
|
240
|
+
self.default = options[:default] if include_default
|
|
241
|
+
self.null = options[:null] if options.include?(:null)
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def change_column_default(table_name, column_name, default) #:nodoc:
|
|
247
|
+
alter_table(table_name) do |definition|
|
|
248
|
+
definition[column_name].default = default
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
|
253
|
+
unless columns(table_name).detect{|c| c.name == column_name.to_s }
|
|
254
|
+
raise ActiveRecord::ActiveRecordError, "Missing column #{table_name}.#{column_name}"
|
|
255
|
+
end
|
|
256
|
+
alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s})
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def rename_table(name, new_name)
|
|
260
|
+
execute "ALTER TABLE #{name} RENAME TO #{new_name}"
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
|
|
264
|
+
log(sql,name) do
|
|
265
|
+
@connection.execute_update(sql)
|
|
266
|
+
end
|
|
267
|
+
table = sql.split(" ", 4)[2]
|
|
268
|
+
id_value || last_insert_id(table, nil)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def last_insert_id(table, sequence_name)
|
|
272
|
+
Integer(select_value("SELECT SEQ FROM SQLITE_SEQUENCE WHERE NAME = '#{table}'"))
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def add_limit_offset!(sql, options) #:nodoc:
|
|
276
|
+
if options[:limit]
|
|
277
|
+
sql << " LIMIT #{options[:limit]}"
|
|
278
|
+
sql << " OFFSET #{options[:offset]}" if options[:offset]
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def tables(name = nil) #:nodoc:
|
|
283
|
+
sql = <<-SQL
|
|
284
|
+
SELECT name
|
|
285
|
+
FROM sqlite_master
|
|
286
|
+
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
|
|
287
|
+
SQL
|
|
288
|
+
|
|
289
|
+
select_rows(sql, name).map do |row|
|
|
290
|
+
row[0]
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def remove_index(table_name, options = {})
|
|
295
|
+
execute "DROP INDEX #{quote_column_name(index_name(table_name, options))}"
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def indexes(table_name, name = nil)
|
|
299
|
+
result = select_rows("SELECT name, sql FROM sqlite_master WHERE tbl_name = '#{table_name}' AND type = 'index'", name)
|
|
300
|
+
|
|
301
|
+
result.collect do |row|
|
|
302
|
+
name = row[0]
|
|
303
|
+
index_sql = row[1]
|
|
304
|
+
unique = (index_sql =~ /unique/i)
|
|
305
|
+
cols = index_sql.match(/\((.*)\)/)[1].gsub(/,/,' ').split
|
|
306
|
+
::ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, name, unique, cols)
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def primary_key(table_name) #:nodoc:
|
|
311
|
+
column = table_structure(table_name).find {|field| field['pk'].to_i == 1}
|
|
312
|
+
column ? column['name'] : nil
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def recreate_database(name)
|
|
316
|
+
tables.each{ |table| drop_table(table) }
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def _execute(sql, name = nil)
|
|
320
|
+
if ActiveRecord::ConnectionAdapters::JdbcConnection::select?(sql)
|
|
321
|
+
@connection.execute_query(sql)
|
|
322
|
+
else
|
|
323
|
+
affected_rows = @connection.execute_update(sql)
|
|
324
|
+
ActiveRecord::ConnectionAdapters::JdbcConnection::insert?(sql) ? last_insert_id(sql.split(" ", 4)[2], nil) : affected_rows
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def select(sql, name=nil)
|
|
329
|
+
execute(sql, name).map do |row|
|
|
330
|
+
record = {}
|
|
331
|
+
row.each_key do |key|
|
|
332
|
+
if key.is_a?(String)
|
|
333
|
+
record[key.sub(/^"?\w+"?\./, '')] = row[key]
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
record
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def table_structure(table_name)
|
|
341
|
+
structure = @connection.execute_query("PRAGMA table_info(#{quote_table_name(table_name)})")
|
|
342
|
+
raise ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'" if structure.empty?
|
|
343
|
+
structure
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
def columns(table_name, name = nil) #:nodoc:
|
|
347
|
+
table_structure(table_name).map do |field|
|
|
348
|
+
::ActiveRecord::ConnectionAdapters::JdbcColumn.new(@config, field['name'], field['dflt_value'], field['type'], field['notnull'] == 0)
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
# SELECT ... FOR UPDATE is redundant since the table is locked.
|
|
353
|
+
def add_lock!(sql, options) #:nodoc:
|
|
354
|
+
sql
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
protected
|
|
358
|
+
include JdbcSpec::MissingFunctionalityHelper
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
module ActiveRecord
|
|
363
|
+
module ConnectionAdapters
|
|
364
|
+
class JdbcColumn < Column
|
|
365
|
+
def self.string_to_binary(value)
|
|
366
|
+
value.gsub(/\0|%/n) do |b|
|
|
367
|
+
case b
|
|
368
|
+
when "\0" then "%00"
|
|
369
|
+
when "\%" then "%25"
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def self.binary_to_string(value)
|
|
375
|
+
value.gsub(/%00|%25/n) do |b|
|
|
376
|
+
case b
|
|
377
|
+
when "%00" then "\0"
|
|
378
|
+
when "%25" then "%"
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module JdbcSpec
|
|
2
|
+
module Sybase
|
|
3
|
+
def self.adapter_matcher(name, *)
|
|
4
|
+
name =~ /sybase|tds/i ? self : false
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def add_limit_offset!(sql, options) # :nodoc:
|
|
8
|
+
@limit = options[:limit]
|
|
9
|
+
@offset = options[:offset]
|
|
10
|
+
if use_temp_table?
|
|
11
|
+
# Use temp table to hack offset with Sybase
|
|
12
|
+
sql.sub!(/ FROM /i, ' INTO #artemp FROM ')
|
|
13
|
+
elsif zero_limit?
|
|
14
|
+
# "SET ROWCOUNT 0" turns off limits, so we havesy
|
|
15
|
+
# to use a cheap trick.
|
|
16
|
+
if sql =~ /WHERE/i
|
|
17
|
+
sql.sub!(/WHERE/i, 'WHERE 1 = 2 AND ')
|
|
18
|
+
elsif sql =~ /ORDER\s+BY/i
|
|
19
|
+
sql.sub!(/ORDER\s+BY/i, 'WHERE 1 = 2 ORDER BY')
|
|
20
|
+
else
|
|
21
|
+
sql << 'WHERE 1 = 2'
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# If limit is not set at all, we can ignore offset;
|
|
27
|
+
# if limit *is* set but offset is zero, use normal select
|
|
28
|
+
# with simple SET ROWCOUNT. Thus, only use the temp table
|
|
29
|
+
# if limit is set and offset > 0.
|
|
30
|
+
def use_temp_table?
|
|
31
|
+
!@limit.nil? && !@offset.nil? && @offset > 0
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def zero_limit?
|
|
35
|
+
!@limit.nil? && @limit == 0
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def modify_types(tp) #:nodoc:
|
|
39
|
+
tp[:primary_key] = "NUMERIC(22,0) IDENTITY PRIMARY KEY"
|
|
40
|
+
tp[:integer][:limit] = nil
|
|
41
|
+
tp[:boolean] = {:name => "bit"}
|
|
42
|
+
tp[:binary] = {:name => "image"}
|
|
43
|
+
tp
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def remove_index(table_name, options = {})
|
|
47
|
+
execute "DROP INDEX #{table_name}.#{index_name(table_name, options)}"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
module JdbcSpec
|
|
2
|
+
module MissingFunctionalityHelper
|
|
3
|
+
#Taken from SQLite adapter
|
|
4
|
+
|
|
5
|
+
def alter_table(table_name, options = {}) #:nodoc:
|
|
6
|
+
table_name = table_name.to_s.downcase
|
|
7
|
+
altered_table_name = "altered_#{table_name}"
|
|
8
|
+
caller = lambda {|definition| yield definition if block_given?}
|
|
9
|
+
|
|
10
|
+
transaction do
|
|
11
|
+
# A temporary table might improve performance here, but
|
|
12
|
+
# it doesn't seem to maintain indices across the whole move.
|
|
13
|
+
move_table(table_name, altered_table_name,
|
|
14
|
+
options)
|
|
15
|
+
move_table(altered_table_name, table_name, &caller)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def move_table(from, to, options = {}, &block) #:nodoc:
|
|
20
|
+
copy_table(from, to, options, &block)
|
|
21
|
+
drop_table(from)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def copy_table(from, to, options = {}) #:nodoc:
|
|
25
|
+
options = options.merge(:id => (!columns(from).detect{|c| c.name == 'id'}.nil? && 'id' == primary_key(from).to_s))
|
|
26
|
+
create_table(to, options) do |definition|
|
|
27
|
+
@definition = definition
|
|
28
|
+
columns(from).each do |column|
|
|
29
|
+
column_name = options[:rename] ?
|
|
30
|
+
(options[:rename][column.name] ||
|
|
31
|
+
options[:rename][column.name.to_sym] ||
|
|
32
|
+
column.name) : column.name
|
|
33
|
+
|
|
34
|
+
@definition.column(column_name, column.type,
|
|
35
|
+
:limit => column.limit, :default => column.default,
|
|
36
|
+
:null => column.null)
|
|
37
|
+
end
|
|
38
|
+
@definition.primary_key(primary_key(from)) if primary_key(from)
|
|
39
|
+
yield @definition if block_given?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
copy_table_indexes(from, to, options[:rename] || {})
|
|
43
|
+
copy_table_contents(from, to,
|
|
44
|
+
@definition.columns.map {|column| column.name},
|
|
45
|
+
options[:rename] || {})
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def copy_table_indexes(from, to, rename = {}) #:nodoc:
|
|
49
|
+
indexes(from).each do |index|
|
|
50
|
+
name = index.name.downcase
|
|
51
|
+
if to == "altered_#{from}"
|
|
52
|
+
name = "temp_#{name}"
|
|
53
|
+
elsif from == "altered_#{to}"
|
|
54
|
+
name = name[5..-1]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
to_column_names = columns(to).map(&:name)
|
|
58
|
+
columns = index.columns.map {|c| rename[c] || c }.select do |column|
|
|
59
|
+
to_column_names.include?(column)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
unless columns.empty?
|
|
63
|
+
# index name can't be the same
|
|
64
|
+
opts = { :name => name.gsub(/(_?)(#{from})_/, "\\1#{to}_") }
|
|
65
|
+
opts[:unique] = true if index.unique
|
|
66
|
+
add_index(to, columns, opts)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
|
|
72
|
+
column_mappings = Hash[*columns.map {|name| [name, name]}.flatten]
|
|
73
|
+
rename.inject(column_mappings) {|map, a| map[a.last] = a.first; map}
|
|
74
|
+
from_columns = columns(from).collect {|col| col.name}
|
|
75
|
+
columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
|
|
76
|
+
quoted_columns = columns.map { |col| quote_column_name(col) } * ','
|
|
77
|
+
|
|
78
|
+
quoted_to = quote_table_name(to)
|
|
79
|
+
execute("SELECT * FROM #{quote_table_name(from)}").each do |row|
|
|
80
|
+
sql = "INSERT INTO #{quoted_to} (#{quoted_columns}) VALUES ("
|
|
81
|
+
sql << columns.map {|col| quote row[column_mappings[col]]} * ', '
|
|
82
|
+
sql << ')'
|
|
83
|
+
execute sql
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|