intentmedia-activerecord-jdbc-adapter 1.1.1.1
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 +404 -0
- data/LICENSE.txt +21 -0
- data/README.txt +181 -0
- data/Rakefile +10 -0
- data/lib/active_record/connection_adapters/derby_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/h2_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/hsqldb_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/informix_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/jdbc_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mssql_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/oracle_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -0
- data/lib/activerecord-jdbc-adapter.rb +8 -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 +15 -0
- data/lib/arel/engines/sql/compilers/jdbc_compiler.rb +6 -0
- data/lib/arel/engines/sql/compilers/mssql_compiler.rb +46 -0
- data/lib/arel/visitors/compat.rb +13 -0
- data/lib/arel/visitors/db2.rb +17 -0
- data/lib/arel/visitors/derby.rb +21 -0
- data/lib/arel/visitors/hsqldb.rb +26 -0
- data/lib/arel/visitors/sql_server.rb +46 -0
- data/lib/arjdbc.rb +29 -0
- data/lib/arjdbc/db2.rb +2 -0
- data/lib/arjdbc/db2/adapter.rb +413 -0
- data/lib/arjdbc/derby.rb +7 -0
- data/lib/arjdbc/derby/adapter.rb +348 -0
- data/lib/arjdbc/derby/connection_methods.rb +18 -0
- data/lib/arjdbc/discover.rb +92 -0
- data/lib/arjdbc/firebird.rb +2 -0
- data/lib/arjdbc/firebird/adapter.rb +131 -0
- data/lib/arjdbc/h2.rb +4 -0
- data/lib/arjdbc/h2/adapter.rb +36 -0
- data/lib/arjdbc/h2/connection_methods.rb +12 -0
- data/lib/arjdbc/hsqldb.rb +4 -0
- data/lib/arjdbc/hsqldb/adapter.rb +184 -0
- data/lib/arjdbc/hsqldb/connection_methods.rb +14 -0
- data/lib/arjdbc/informix.rb +3 -0
- data/lib/arjdbc/informix/adapter.rb +138 -0
- data/lib/arjdbc/informix/connection_methods.rb +10 -0
- data/lib/arjdbc/jdbc.rb +2 -0
- data/lib/arjdbc/jdbc/adapter.rb +285 -0
- data/lib/arjdbc/jdbc/callbacks.rb +44 -0
- data/lib/arjdbc/jdbc/column.rb +38 -0
- data/lib/arjdbc/jdbc/compatibility.rb +51 -0
- data/lib/arjdbc/jdbc/connection.rb +128 -0
- data/lib/arjdbc/jdbc/connection_methods.rb +16 -0
- data/lib/arjdbc/jdbc/core_ext.rb +24 -0
- data/lib/arjdbc/jdbc/discover.rb +18 -0
- data/lib/arjdbc/jdbc/driver.rb +44 -0
- data/lib/arjdbc/jdbc/extension.rb +47 -0
- data/lib/arjdbc/jdbc/java.rb +14 -0
- data/lib/arjdbc/jdbc/jdbc.rake +127 -0
- data/lib/arjdbc/jdbc/missing_functionality_helper.rb +87 -0
- data/lib/arjdbc/jdbc/quoted_primary_key.rb +28 -0
- data/lib/arjdbc/jdbc/railtie.rb +9 -0
- data/lib/arjdbc/jdbc/rake_tasks.rb +10 -0
- data/lib/arjdbc/jdbc/require_driver.rb +16 -0
- data/lib/arjdbc/jdbc/type_converter.rb +127 -0
- data/lib/arjdbc/mimer.rb +2 -0
- data/lib/arjdbc/mimer/adapter.rb +142 -0
- data/lib/arjdbc/mssql.rb +4 -0
- data/lib/arjdbc/mssql/adapter.rb +472 -0
- data/lib/arjdbc/mssql/connection_methods.rb +30 -0
- data/lib/arjdbc/mssql/limit_helpers.rb +92 -0
- data/lib/arjdbc/mssql/tsql_helper.rb +61 -0
- data/lib/arjdbc/mysql.rb +4 -0
- data/lib/arjdbc/mysql/adapter.rb +416 -0
- data/lib/arjdbc/mysql/connection_methods.rb +27 -0
- data/lib/arjdbc/oracle.rb +3 -0
- data/lib/arjdbc/oracle/adapter.rb +412 -0
- data/lib/arjdbc/oracle/connection_methods.rb +11 -0
- data/lib/arjdbc/postgresql.rb +4 -0
- data/lib/arjdbc/postgresql/adapter.rb +579 -0
- data/lib/arjdbc/postgresql/connection_methods.rb +21 -0
- data/lib/arjdbc/sqlite3.rb +4 -0
- data/lib/arjdbc/sqlite3/adapter.rb +381 -0
- data/lib/arjdbc/sqlite3/connection_methods.rb +34 -0
- data/lib/arjdbc/sybase.rb +2 -0
- data/lib/arjdbc/sybase/adapter.rb +46 -0
- data/lib/arjdbc/version.rb +8 -0
- data/lib/generators/jdbc/jdbc_generator.rb +9 -0
- data/lib/jdbc_adapter.rb +2 -0
- data/lib/jdbc_adapter/rake_tasks.rb +3 -0
- data/lib/jdbc_adapter/version.rb +3 -0
- data/lib/pg.rb +26 -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 +25 -0
- data/rakelib/db.rake +19 -0
- data/rakelib/package.rake +91 -0
- data/rakelib/rails.rake +41 -0
- data/rakelib/test.rake +81 -0
- data/src/java/arjdbc/derby/DerbyModule.java +322 -0
- data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +70 -0
- data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +74 -0
- data/src/java/arjdbc/jdbc/AdapterJavaService.java +66 -0
- data/src/java/arjdbc/jdbc/JdbcConnectionFactory.java +36 -0
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +1305 -0
- data/src/java/arjdbc/jdbc/SQLBlock.java +48 -0
- data/src/java/arjdbc/mssql/MssqlRubyJdbcConnection.java +127 -0
- data/src/java/arjdbc/mysql/MySQLModule.java +134 -0
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +87 -0
- data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +85 -0
- data/src/java/arjdbc/postgresql/PostgresqlRubyJdbcConnection.java +57 -0
- data/src/java/arjdbc/sqlite3/Sqlite3RubyJdbcConnection.java +64 -0
- data/test/abstract_db_create.rb +124 -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/db/db2.rb +11 -0
- data/test/db/derby.rb +12 -0
- data/test/db/h2.rb +11 -0
- data/test/db/hsqldb.rb +13 -0
- data/test/db/informix.rb +11 -0
- data/test/db/jdbc.rb +11 -0
- data/test/db/jndi_config.rb +40 -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 +11 -0
- data/test/db2_simple_test.rb +66 -0
- data/test/derby_migration_test.rb +68 -0
- data/test/derby_multibyte_test.rb +12 -0
- data/test/derby_simple_test.rb +99 -0
- data/test/generic_jdbc_connection_test.rb +29 -0
- data/test/h2_simple_test.rb +41 -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_common.rb +25 -0
- data/test/jndi_callbacks_test.rb +40 -0
- data/test/jndi_test.rb +25 -0
- data/test/manualTestDatabase.rb +191 -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 +43 -0
- data/test/models/mixed_case.rb +25 -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 +136 -0
- data/test/mssql_multibyte_test.rb +18 -0
- data/test/mssql_simple_test.rb +55 -0
- data/test/mysql_db_create_test.rb +27 -0
- data/test/mysql_info_test.rb +123 -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 +49 -0
- data/test/oracle_simple_test.rb +18 -0
- data/test/oracle_specific_test.rb +83 -0
- data/test/pick_rails_version.rb +3 -0
- data/test/postgres_db_create_test.rb +32 -0
- data/test/postgres_drop_db_test.rb +16 -0
- data/test/postgres_mixed_case_test.rb +29 -0
- data/test/postgres_nonseq_pkey_test.rb +38 -0
- data/test/postgres_reserved_test.rb +22 -0
- data/test/postgres_schema_search_path_test.rb +44 -0
- data/test/postgres_simple_test.rb +65 -0
- data/test/postgres_table_alias_length_test.rb +15 -0
- data/test/simple.rb +546 -0
- data/test/sqlite3_simple_test.rb +233 -0
- data/test/sybase_jtds_simple_test.rb +28 -0
- metadata +259 -0
data/Rakefile
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
require 'rake/testtask'
|
|
2
|
+
require 'rake/clean'
|
|
3
|
+
CLEAN.include 'derby*', 'test.db.*','test/reports', 'test.sqlite3','lib/**/*.jar','manifest.mf', '*.log'
|
|
4
|
+
|
|
5
|
+
task :default => [:java_compile, :test]
|
|
6
|
+
|
|
7
|
+
task :filelist do
|
|
8
|
+
puts FileList['pkg/**/*'].inspect
|
|
9
|
+
end
|
|
10
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'arjdbc/derby'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'arjdbc/h2'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'arjdbc/hsqldb'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'arjdbc/informix'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'arjdbc/jdbc'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'arjdbc/jdbc'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'arjdbc/mssql'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'arjdbc/mysql'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'arjdbc/mysql'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'arjdbc/oracle'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'arjdbc/postgresql'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'arjdbc/sqlite3'
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Arel
|
|
2
|
+
module SqlCompiler
|
|
3
|
+
class HsqldbCompiler < GenericCompiler
|
|
4
|
+
def select_sql
|
|
5
|
+
# HSQLDB needs to add LIMIT in right after SELECT
|
|
6
|
+
query = super
|
|
7
|
+
offset = relation.skipped
|
|
8
|
+
limit = relation.taken
|
|
9
|
+
@engine.connection.add_limit_offset!(query, :limit => limit,
|
|
10
|
+
:offset => offset) if offset || limit
|
|
11
|
+
query
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module Arel
|
|
2
|
+
module SqlCompiler
|
|
3
|
+
class MsSQLCompiler < GenericCompiler
|
|
4
|
+
def select_sql
|
|
5
|
+
projections = @relation.projections
|
|
6
|
+
offset = relation.skipped
|
|
7
|
+
limit = relation.taken
|
|
8
|
+
if Count === projections.first && projections.size == 1 &&
|
|
9
|
+
(relation.taken.present? || relation.wheres.present?) && relation.joins(self).blank?
|
|
10
|
+
subquery = [
|
|
11
|
+
"SELECT * FROM #{relation.from_clauses}", build_clauses
|
|
12
|
+
].join ' '
|
|
13
|
+
@engine.connection.add_limit_offset!(subquery, :limit => limit, :offset => offset) if offset || limit
|
|
14
|
+
query = "SELECT COUNT(*) AS count_id FROM (#{subquery}) AS subquery"
|
|
15
|
+
else
|
|
16
|
+
query = [
|
|
17
|
+
"SELECT #{relation.select_clauses.join(', ')}",
|
|
18
|
+
"FROM #{relation.from_clauses}",
|
|
19
|
+
build_clauses
|
|
20
|
+
].compact.join ' '
|
|
21
|
+
@engine.connection.add_limit_offset!(query, :limit => limit, :offset => offset) if offset || limit
|
|
22
|
+
end
|
|
23
|
+
query
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def build_clauses
|
|
27
|
+
joins = relation.joins(self)
|
|
28
|
+
wheres = relation.where_clauses
|
|
29
|
+
groups = relation.group_clauses
|
|
30
|
+
havings = relation.having_clauses
|
|
31
|
+
orders = relation.order_clauses
|
|
32
|
+
|
|
33
|
+
clauses = [ "",
|
|
34
|
+
joins,
|
|
35
|
+
("WHERE #{wheres.join(' AND ')}" unless wheres.empty?),
|
|
36
|
+
("GROUP BY #{groups.join(', ')}" unless groups.empty?),
|
|
37
|
+
("HAVING #{havings.join(' AND ')}" unless havings.empty?),
|
|
38
|
+
("ORDER BY #{orders.join(', ')}" unless orders.empty?)
|
|
39
|
+
].compact.join ' '
|
|
40
|
+
|
|
41
|
+
clauses << " #{locked}" unless locked.blank?
|
|
42
|
+
clauses unless clauses.blank?
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'arel/visitors/compat'
|
|
2
|
+
|
|
3
|
+
module Arel
|
|
4
|
+
module Visitors
|
|
5
|
+
class DB2 < Arel::Visitors::ToSql
|
|
6
|
+
def visit_Arel_Nodes_SelectStatement o
|
|
7
|
+
add_limit_offset([o.cores.map { |x| visit_Arel_Nodes_SelectCore x }.join,
|
|
8
|
+
("ORDER BY #{o.orders.map { |x| visit x }.join(', ')}" unless o.orders.empty?),
|
|
9
|
+
].compact.join(' '), o)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def add_limit_offset(sql, o)
|
|
13
|
+
@connection.replace_limit_offset! sql, limit_for(o.limit), o.offset && o.offset.value
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require 'arel/visitors/compat'
|
|
2
|
+
|
|
3
|
+
module Arel
|
|
4
|
+
module Visitors
|
|
5
|
+
class Derby < Arel::Visitors::ToSql
|
|
6
|
+
def visit_Arel_Nodes_SelectStatement o
|
|
7
|
+
[
|
|
8
|
+
o.cores.map { |x| visit_Arel_Nodes_SelectCore x }.join,
|
|
9
|
+
("ORDER BY #{o.orders.map { |x| visit x }.join(', ')}" unless o.orders.empty?),
|
|
10
|
+
("FETCH FIRST #{limit_for(o.limit)} ROWS ONLY" if o.limit),
|
|
11
|
+
(visit(o.offset) if o.offset),
|
|
12
|
+
(visit(o.lock) if o.lock),
|
|
13
|
+
].compact.join ' '
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def visit_Arel_Nodes_Offset o
|
|
17
|
+
"OFFSET #{visit o.value} ROWS"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'arel/visitors/compat'
|
|
2
|
+
|
|
3
|
+
module Arel
|
|
4
|
+
module Visitors
|
|
5
|
+
class HSQLDB < Arel::Visitors::ToSql
|
|
6
|
+
def visit_Arel_Nodes_SelectStatement o
|
|
7
|
+
[
|
|
8
|
+
limit_offset(o.cores.map { |x| visit_Arel_Nodes_SelectCore x }.join, o),
|
|
9
|
+
("ORDER BY #{o.orders.map { |x| visit x }.join(', ')}" unless o.orders.empty?),
|
|
10
|
+
].compact.join ' '
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def limit_offset sql, o
|
|
14
|
+
offset = o.offset || 0
|
|
15
|
+
bef = sql[7..-1]
|
|
16
|
+
if limit = o.limit
|
|
17
|
+
"SELECT LIMIT #{offset} #{limit_for(limit)} #{bef}"
|
|
18
|
+
elsif offset > 0
|
|
19
|
+
"SELECT LIMIT #{offset} 0 #{bef}"
|
|
20
|
+
else
|
|
21
|
+
sql
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require 'arel/visitors/compat'
|
|
2
|
+
|
|
3
|
+
module Arel
|
|
4
|
+
module Visitors
|
|
5
|
+
class SQLServer < Arel::Visitors::ToSql
|
|
6
|
+
include ArJdbc::MsSQL::LimitHelpers::SqlServerReplaceLimitOffset
|
|
7
|
+
|
|
8
|
+
def select_count? o
|
|
9
|
+
sel = o.cores.length == 1 && o.cores.first
|
|
10
|
+
projections = sel.projections.length == 1 && sel.projections
|
|
11
|
+
Arel::Nodes::Count === projections.first
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Need to mimic the subquery logic in ARel 1.x for select count with limit
|
|
15
|
+
# See arel/engines/sql/compilers/mssql_compiler.rb for details
|
|
16
|
+
def visit_Arel_Nodes_SelectStatement o
|
|
17
|
+
order = "ORDER BY #{o.orders.map { |x| visit x }.join(', ')}" unless o.orders.empty?
|
|
18
|
+
if o.limit
|
|
19
|
+
if select_count?(o)
|
|
20
|
+
subquery = true
|
|
21
|
+
sql = o.cores.map do |x|
|
|
22
|
+
x = x.dup
|
|
23
|
+
x.projections = [Arel::Nodes::SqlLiteral.new("*")]
|
|
24
|
+
visit_Arel_Nodes_SelectCore x
|
|
25
|
+
end.join
|
|
26
|
+
else
|
|
27
|
+
sql = o.cores.map { |x| visit_Arel_Nodes_SelectCore x }.join
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
order ||= "ORDER BY #{@connection.determine_order_clause(sql)}"
|
|
31
|
+
replace_limit_offset!(sql, limit_for(o.limit).to_i, o.offset && o.offset.value.to_i, order)
|
|
32
|
+
sql = "SELECT COUNT(*) AS count_id FROM (#{sql}) AS subquery" if subquery
|
|
33
|
+
elsif order
|
|
34
|
+
sql << " #{order}"
|
|
35
|
+
else
|
|
36
|
+
sql = super
|
|
37
|
+
end
|
|
38
|
+
sql
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class SQLServer2000 < SQLServer
|
|
43
|
+
include ArJdbc::MsSQL::LimitHelpers::SqlServer2000ReplaceLimitOffset
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
data/lib/arjdbc.rb
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
if defined?(JRUBY_VERSION)
|
|
2
|
+
begin
|
|
3
|
+
tried_gem ||= false
|
|
4
|
+
require 'active_record/version'
|
|
5
|
+
rescue LoadError
|
|
6
|
+
raise if tried_gem
|
|
7
|
+
require 'rubygems'
|
|
8
|
+
gem 'activerecord'
|
|
9
|
+
tried_gem = true
|
|
10
|
+
retry
|
|
11
|
+
end
|
|
12
|
+
if ActiveRecord::VERSION::MAJOR < 2
|
|
13
|
+
if defined?(RAILS_CONNECTION_ADAPTERS)
|
|
14
|
+
RAILS_CONNECTION_ADAPTERS << %q(jdbc)
|
|
15
|
+
else
|
|
16
|
+
RAILS_CONNECTION_ADAPTERS = %w(jdbc)
|
|
17
|
+
end
|
|
18
|
+
if ActiveRecord::VERSION::MAJOR == 1 && ActiveRecord::VERSION::MINOR == 14
|
|
19
|
+
require 'arjdbc/jdbc'
|
|
20
|
+
end
|
|
21
|
+
else
|
|
22
|
+
require 'active_record'
|
|
23
|
+
require 'arjdbc/jdbc'
|
|
24
|
+
end
|
|
25
|
+
else
|
|
26
|
+
warn "activerecord-jdbc-adapter is for use with JRuby only"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
require 'arjdbc/version'
|
data/lib/arjdbc/db2.rb
ADDED
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
module ArJdbc
|
|
2
|
+
module DB2
|
|
3
|
+
def self.column_selector
|
|
4
|
+
[ /(db2|as400)/i,
|
|
5
|
+
lambda { |cfg, column| column.extend(::ArJdbc::DB2::Column) } ]
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
module Column
|
|
9
|
+
def type_cast(value)
|
|
10
|
+
return nil if value.nil? || value =~ /^\s*null\s*$/i
|
|
11
|
+
case type
|
|
12
|
+
when :string then value
|
|
13
|
+
when :integer then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
|
|
14
|
+
when :primary_key then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
|
|
15
|
+
when :float then value.to_f
|
|
16
|
+
when :datetime then ArJdbc::DB2::Column.cast_to_date_or_time(value)
|
|
17
|
+
when :date then ArJdbc::DB2::Column.cast_to_date_or_time(value)
|
|
18
|
+
when :timestamp then ArJdbc::DB2::Column.cast_to_time(value)
|
|
19
|
+
when :time then ArJdbc::DB2::Column.cast_to_time(value)
|
|
20
|
+
# TODO AS400 stores binary strings in EBCDIC (CCSID 65535), need to convert back to ASCII
|
|
21
|
+
else
|
|
22
|
+
super
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def type_cast_code(var_name)
|
|
27
|
+
case type
|
|
28
|
+
when :datetime then "ArJdbc::DB2::Column.cast_to_date_or_time(#{var_name})"
|
|
29
|
+
when :date then "ArJdbc::DB2::Column.cast_to_date_or_time(#{var_name})"
|
|
30
|
+
when :timestamp then "ArJdbc::DB2::Column.cast_to_time(#{var_name})"
|
|
31
|
+
when :time then "ArJdbc::DB2::Column.cast_to_time(#{var_name})"
|
|
32
|
+
else
|
|
33
|
+
super
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.cast_to_date_or_time(value)
|
|
38
|
+
return value if value.is_a? Date
|
|
39
|
+
return nil if value.blank?
|
|
40
|
+
guess_date_or_time((value.is_a? Time) ? value : cast_to_time(value))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.cast_to_time(value)
|
|
44
|
+
return value if value.is_a? Time
|
|
45
|
+
# AS400 returns a 2 digit year, LUW returns a 4 digit year, so comp = true to help out AS400
|
|
46
|
+
time_array = ParseDate.parsedate(value, true)
|
|
47
|
+
time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1;
|
|
48
|
+
Time.send(ActiveRecord::Base.default_timezone, *time_array) rescue nil
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def self.guess_date_or_time(value)
|
|
52
|
+
(value.hour == 0 and value.min == 0 and value.sec == 0) ?
|
|
53
|
+
Date.new(value.year, value.month, value.day) : value
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
# <b>DEPRECATED:</b> SMALLINT is now used for boolean field types. Please
|
|
58
|
+
# convert your tables using DECIMAL(5) for boolean values to SMALLINT instead.
|
|
59
|
+
def use_decimal5_for_boolean
|
|
60
|
+
warn "[DEPRECATION] using DECIMAL(5) for boolean is deprecated. Convert your columns to SMALLINT instead."
|
|
61
|
+
:boolean
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/topic/com.ibm.db2.luw.apdv.java.doc/doc/rjvjdata.html
|
|
65
|
+
def simplified_type(field_type)
|
|
66
|
+
case field_type
|
|
67
|
+
# old jdbc_db2.rb used decimal(5,0) as boolean
|
|
68
|
+
when /^smallint/i then :boolean
|
|
69
|
+
when /^decimal\(5\)$/i then use_decimal5_for_boolean
|
|
70
|
+
when /^real/i then :float
|
|
71
|
+
when /^timestamp/i then :datetime
|
|
72
|
+
else
|
|
73
|
+
super
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Post process default value from JDBC into a Rails-friendly format (columns{-internal})
|
|
78
|
+
def default_value(value)
|
|
79
|
+
# IBM i (AS400) will return an empty string instead of null for no default
|
|
80
|
+
return nil if value.blank?
|
|
81
|
+
|
|
82
|
+
# string defaults are surrounded by single quotes
|
|
83
|
+
return $1 if value =~ /^'(.*)'$/
|
|
84
|
+
|
|
85
|
+
value
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def _execute(sql, name = nil)
|
|
90
|
+
if ActiveRecord::ConnectionAdapters::JdbcConnection::select?(sql)
|
|
91
|
+
@connection.execute_query(sql)
|
|
92
|
+
elsif ActiveRecord::ConnectionAdapters::JdbcConnection::insert?(sql)
|
|
93
|
+
(@connection.execute_insert(sql) or last_insert_id(sql)).to_i
|
|
94
|
+
else
|
|
95
|
+
@connection.execute_update(sql)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# holy moly batman! all this to tell AS400 "yes i am sure"
|
|
100
|
+
def execute_and_auto_confirm(sql)
|
|
101
|
+
begin
|
|
102
|
+
@connection.execute_update "call qsys.qcmdexc('QSYS/CHGJOB INQMSGRPY(*SYSRPYL)',0000000031.00000)"
|
|
103
|
+
@connection.execute_update "call qsys.qcmdexc('ADDRPYLE SEQNBR(9876) MSGID(CPA32B2) RPY(''I'')',0000000045.00000)"
|
|
104
|
+
rescue Exception => e
|
|
105
|
+
raise "Could not call CHGJOB INQMSGRPY(*SYSRPYL) and ADDRPYLE SEQNBR(9876) MSGID(CPA32B2) RPY('I').\n" +
|
|
106
|
+
"Do you have authority to do this?\n\n" + e.to_s
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
r = execute sql
|
|
110
|
+
|
|
111
|
+
begin
|
|
112
|
+
@connection.execute_update "call qsys.qcmdexc('QSYS/CHGJOB INQMSGRPY(*DFT)',0000000027.00000)"
|
|
113
|
+
@connection.execute_update "call qsys.qcmdexc('RMVRPYLE SEQNBR(9876)',0000000021.00000)"
|
|
114
|
+
rescue Exception => e
|
|
115
|
+
raise "Could not call CHGJOB INQMSGRPY(*DFT) and RMVRPYLE SEQNBR(9876).\n" +
|
|
116
|
+
"Do you have authority to do this?\n\n" + e.to_s
|
|
117
|
+
end
|
|
118
|
+
r
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def last_insert_id(sql)
|
|
122
|
+
table_name = sql.split(/\s/)[2]
|
|
123
|
+
result = select(ActiveRecord::Base.send(:sanitize_sql,
|
|
124
|
+
%[select IDENTITY_VAL_LOCAL() as last_insert_id from #{table_name}],
|
|
125
|
+
nil))
|
|
126
|
+
result.last['last_insert_id']
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def modify_types(tp)
|
|
130
|
+
tp[:primary_key] = 'int not null generated by default as identity (start with 1) primary key'
|
|
131
|
+
tp[:string][:limit] = 255
|
|
132
|
+
tp[:integer][:limit] = nil
|
|
133
|
+
tp[:boolean] = {:name => "smallint"}
|
|
134
|
+
tp
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
|
|
138
|
+
limit = nil if type.to_sym == :integer
|
|
139
|
+
super(type, limit, precision, scale)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def adapter_name
|
|
143
|
+
'DB2'
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def arel2_visitors
|
|
147
|
+
require 'arel/visitors/db2'
|
|
148
|
+
{'db2' => ::Arel::Visitors::DB2, 'as400' => ::Arel::Visitors::DB2}
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def add_limit_offset!(sql, options)
|
|
152
|
+
replace_limit_offset!(sql, options[:limit], options[:offset])
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def replace_limit_offset!(sql, limit, offset)
|
|
156
|
+
if limit
|
|
157
|
+
limit = limit.to_i
|
|
158
|
+
if !offset
|
|
159
|
+
if limit == 1
|
|
160
|
+
sql << " FETCH FIRST ROW ONLY"
|
|
161
|
+
else
|
|
162
|
+
sql << " FETCH FIRST #{limit} ROWS ONLY"
|
|
163
|
+
end
|
|
164
|
+
else
|
|
165
|
+
offset = offset.to_i
|
|
166
|
+
sql.gsub!(/SELECT/i, 'SELECT B.* FROM (SELECT A.*, row_number() over () AS internal$rownum FROM (SELECT')
|
|
167
|
+
sql << ") A ) B WHERE B.internal$rownum > #{offset} AND B.internal$rownum <= #{limit + offset}"
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
sql
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def pk_and_sequence_for(table)
|
|
174
|
+
# In JDBC/DB2 side, only upcase names of table and column are handled.
|
|
175
|
+
keys = super(table.upcase)
|
|
176
|
+
if keys && keys[0]
|
|
177
|
+
# In ActiveRecord side, only downcase names of table and column are handled.
|
|
178
|
+
keys[0] = keys[0].downcase
|
|
179
|
+
end
|
|
180
|
+
keys
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def quote_column_name(column_name)
|
|
184
|
+
column_name
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def quote(value, column = nil) # :nodoc:
|
|
188
|
+
if column && column.respond_to?(:primary) && column.primary && column.klass != String
|
|
189
|
+
return value.to_i.to_s
|
|
190
|
+
end
|
|
191
|
+
if column && (column.type == :decimal || column.type == :integer) && value
|
|
192
|
+
return value.to_s
|
|
193
|
+
end
|
|
194
|
+
case value
|
|
195
|
+
when String
|
|
196
|
+
if column && column.type == :binary
|
|
197
|
+
"BLOB('#{quote_string(value)}')"
|
|
198
|
+
else
|
|
199
|
+
"'#{quote_string(value)}'"
|
|
200
|
+
end
|
|
201
|
+
else super
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def quote_string(string)
|
|
206
|
+
string.gsub(/'/, "''") # ' (for ruby-mode)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def quoted_true
|
|
210
|
+
'1'
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def quoted_false
|
|
214
|
+
'0'
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def reorg_table(table_name)
|
|
218
|
+
unless as400?
|
|
219
|
+
@connection.execute_update "call sysproc.admin_cmd ('REORG TABLE #{table_name}')"
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def recreate_database(name)
|
|
224
|
+
tables.each {|table| drop_table("#{db2_schema}.#{table}")}
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def remove_index(table_name, options = { })
|
|
228
|
+
execute "DROP INDEX #{quote_column_name(index_name(table_name, options))}"
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/topic/com.ibm.db2.luw.admin.dbobj.doc/doc/t0020130.html
|
|
232
|
+
# ...not supported on IBM i, so we raise in this case
|
|
233
|
+
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
|
234
|
+
if as400?
|
|
235
|
+
raise NotImplementedError, "rename_column is not supported on IBM i"
|
|
236
|
+
else
|
|
237
|
+
execute "ALTER TABLE #{table_name} RENAME COLUMN #{column_name} TO #{new_column_name}"
|
|
238
|
+
reorg_table(table_name)
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def change_column_null(table_name, column_name, null)
|
|
243
|
+
if null
|
|
244
|
+
execute_and_auto_confirm "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} DROP NOT NULL"
|
|
245
|
+
else
|
|
246
|
+
execute_and_auto_confirm "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET NOT NULL"
|
|
247
|
+
end
|
|
248
|
+
reorg_table(table_name)
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def change_column_default(table_name, column_name, default)
|
|
252
|
+
if default.nil?
|
|
253
|
+
execute_and_auto_confirm "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} DROP DEFAULT"
|
|
254
|
+
else
|
|
255
|
+
execute_and_auto_confirm "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET WITH DEFAULT #{quote(default)}"
|
|
256
|
+
end
|
|
257
|
+
reorg_table(table_name)
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def change_column(table_name, column_name, type, options = {})
|
|
261
|
+
data_type = type_to_sql(type, options[:limit], options[:precision], options[:scale])
|
|
262
|
+
sql = "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DATA TYPE #{data_type}"
|
|
263
|
+
as400? ? execute_and_auto_confirm(sql) : execute(sql)
|
|
264
|
+
reorg_table(table_name)
|
|
265
|
+
|
|
266
|
+
if options.include?(:default) and options.include?(:null)
|
|
267
|
+
# which to run first?
|
|
268
|
+
if options[:null] or options[:default].nil?
|
|
269
|
+
change_column_null(table_name, column_name, options[:null])
|
|
270
|
+
change_column_default(table_name, column_name, options[:default])
|
|
271
|
+
else
|
|
272
|
+
change_column_default(table_name, column_name, options[:default])
|
|
273
|
+
change_column_null(table_name, column_name, options[:null])
|
|
274
|
+
end
|
|
275
|
+
elsif options.include?(:default)
|
|
276
|
+
change_column_default(table_name, column_name, options[:default])
|
|
277
|
+
elsif options.include?(:null)
|
|
278
|
+
change_column_null(table_name, column_name, options[:null])
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/topic/com.ibm.db2.luw.admin.dbobj.doc/doc/t0020132.html
|
|
283
|
+
def remove_column(table_name, column_name) #:nodoc:
|
|
284
|
+
sql = "ALTER TABLE #{table_name} DROP COLUMN #{column_name}"
|
|
285
|
+
|
|
286
|
+
as400? ? execute_and_auto_confirm(sql) : execute(sql)
|
|
287
|
+
reorg_table(table_name)
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/topic/com.ibm.db2.luw.sql.ref.doc/doc/r0000980.html
|
|
291
|
+
def rename_table(name, new_name) #:nodoc:
|
|
292
|
+
execute "RENAME TABLE #{name} TO #{new_name}"
|
|
293
|
+
reorg_table(new_name)
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def tables
|
|
297
|
+
@connection.tables(nil, db2_schema, nil, ["TABLE"])
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
# only record precision and scale for types that can set
|
|
301
|
+
# them via CREATE TABLE:
|
|
302
|
+
# http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/topic/com.ibm.db2.luw.sql.ref.doc/doc/r0000927.html
|
|
303
|
+
HAVE_LIMIT = %w(FLOAT DECFLOAT CHAR VARCHAR CLOB BLOB NCHAR NCLOB DBCLOB GRAPHIC VARGRAPHIC) #TIMESTAMP
|
|
304
|
+
HAVE_PRECISION = %w(DECIMAL NUMERIC)
|
|
305
|
+
HAVE_SCALE = %w(DECIMAL NUMERIC)
|
|
306
|
+
|
|
307
|
+
def columns(table_name, name = nil)
|
|
308
|
+
cols = @connection.columns(table_name, name, db2_schema)
|
|
309
|
+
|
|
310
|
+
# scrub out sizing info when CREATE TABLE doesn't support it
|
|
311
|
+
# but JDBC reports it (doh!)
|
|
312
|
+
for col in cols
|
|
313
|
+
base_sql_type = col.sql_type.sub(/\(.*/, "").upcase
|
|
314
|
+
col.limit = nil unless HAVE_LIMIT.include?(base_sql_type)
|
|
315
|
+
col.precision = nil unless HAVE_PRECISION.include?(base_sql_type)
|
|
316
|
+
#col.scale = nil unless HAVE_SCALE.include?(base_sql_type)
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
cols
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def indexes(table_name, name = nil)
|
|
323
|
+
@connection.indexes(table_name, name, db2_schema)
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
def add_quotes(name)
|
|
327
|
+
return name unless name
|
|
328
|
+
%Q{"#{name}"}
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def strip_quotes(str)
|
|
332
|
+
return str unless str
|
|
333
|
+
return str unless /^(["']).*\1$/ =~ str
|
|
334
|
+
str[1..-2]
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def expand_double_quotes(name)
|
|
338
|
+
return name unless name && name['"']
|
|
339
|
+
name.gsub(/"/,'""')
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def structure_dump #:nodoc:
|
|
343
|
+
definition=""
|
|
344
|
+
rs = @connection.connection.meta_data.getTables(nil,db2_schema.upcase,nil,["TABLE"].to_java(:string))
|
|
345
|
+
while rs.next
|
|
346
|
+
tname = rs.getString(3)
|
|
347
|
+
definition << "CREATE TABLE #{tname} (\n"
|
|
348
|
+
rs2 = @connection.connection.meta_data.getColumns(nil,db2_schema.upcase,tname,nil)
|
|
349
|
+
first_col = true
|
|
350
|
+
while rs2.next
|
|
351
|
+
col_name = add_quotes(rs2.getString(4));
|
|
352
|
+
default = ""
|
|
353
|
+
d1 = rs2.getString(13)
|
|
354
|
+
# IBM i (as400 toolbox driver) will return an empty string if there is no default
|
|
355
|
+
if @config[:url] =~ /^jdbc:as400:/
|
|
356
|
+
default = !d1.blank? ? " DEFAULT #{d1}" : ""
|
|
357
|
+
else
|
|
358
|
+
default = d1 ? " DEFAULT #{d1}" : ""
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
type = rs2.getString(6)
|
|
362
|
+
col_precision = rs2.getString(7)
|
|
363
|
+
col_scale = rs2.getString(9)
|
|
364
|
+
col_size = ""
|
|
365
|
+
if HAVE_SCALE.include?(type) and col_scale
|
|
366
|
+
col_size = "(#{col_precision},#{col_scale})"
|
|
367
|
+
elsif (HAVE_LIMIT + HAVE_PRECISION).include?(type) and col_precision
|
|
368
|
+
col_size = "(#{col_precision})"
|
|
369
|
+
end
|
|
370
|
+
nulling = (rs2.getString(18) == 'NO' ? " NOT NULL" : "")
|
|
371
|
+
create_col_string = add_quotes(expand_double_quotes(strip_quotes(col_name))) +
|
|
372
|
+
" " +
|
|
373
|
+
type +
|
|
374
|
+
col_size +
|
|
375
|
+
"" +
|
|
376
|
+
nulling +
|
|
377
|
+
default
|
|
378
|
+
if !first_col
|
|
379
|
+
create_col_string = ",\n #{create_col_string}"
|
|
380
|
+
else
|
|
381
|
+
create_col_string = " #{create_col_string}"
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
definition << create_col_string
|
|
385
|
+
|
|
386
|
+
first_col = false
|
|
387
|
+
end
|
|
388
|
+
definition << ");\n\n"
|
|
389
|
+
end
|
|
390
|
+
definition
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
private
|
|
394
|
+
def as400?
|
|
395
|
+
@config[:url] =~ /^jdbc:as400:/
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
def db2_schema
|
|
399
|
+
if @config[:schema].blank?
|
|
400
|
+
if as400?
|
|
401
|
+
# AS400 implementation takes schema from library name (last part of url)
|
|
402
|
+
schema = @config[:url].split('/').last.strip
|
|
403
|
+
(schema[-1..-1] == ";") ? schema.chop : schema
|
|
404
|
+
else
|
|
405
|
+
# LUW implementation uses schema name of username by default
|
|
406
|
+
@config[:username] or ENV['USER']
|
|
407
|
+
end
|
|
408
|
+
else
|
|
409
|
+
@config[:schema]
|
|
410
|
+
end
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
end
|