activerecord-jdbc-adapter 1.3.17 → 1.3.18
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +24 -5
- data/History.md +54 -0
- data/lib/arel/visitors/compat.rb +30 -2
- data/lib/arel/visitors/db2.rb +118 -29
- data/lib/arel/visitors/derby.rb +84 -29
- data/lib/arel/visitors/firebird.rb +66 -9
- data/lib/arel/visitors/h2.rb +16 -0
- data/lib/arel/visitors/hsqldb.rb +6 -3
- data/lib/arel/visitors/postgresql_jdbc.rb +6 -0
- data/lib/arel/visitors/sql_server.rb +121 -40
- data/lib/arel/visitors/sql_server/ng42.rb +293 -0
- data/lib/arjdbc.rb +1 -7
- data/lib/arjdbc/db2.rb +1 -0
- data/lib/arjdbc/db2/adapter.rb +118 -18
- data/lib/arjdbc/derby/adapter.rb +29 -8
- data/lib/arjdbc/firebird.rb +1 -0
- data/lib/arjdbc/firebird/adapter.rb +126 -11
- data/lib/arjdbc/hsqldb/adapter.rb +3 -0
- data/lib/arjdbc/informix.rb +1 -0
- data/lib/arjdbc/jdbc.rb +17 -0
- data/lib/arjdbc/jdbc/adapter.rb +28 -3
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/jdbc/column.rb +7 -3
- data/lib/arjdbc/jdbc/type_cast.rb +2 -0
- data/lib/arjdbc/jdbc/type_converter.rb +28 -15
- data/lib/arjdbc/mimer.rb +1 -0
- data/lib/arjdbc/mssql.rb +2 -1
- data/lib/arjdbc/mssql/adapter.rb +105 -30
- data/lib/arjdbc/mssql/column.rb +30 -7
- data/lib/arjdbc/mssql/limit_helpers.rb +22 -9
- data/lib/arjdbc/mssql/types.rb +343 -0
- data/lib/arjdbc/mssql/utils.rb +25 -2
- data/lib/arjdbc/mysql/adapter.rb +22 -21
- data/lib/arjdbc/oracle.rb +1 -0
- data/lib/arjdbc/oracle/adapter.rb +291 -19
- data/lib/arjdbc/oracle/column.rb +9 -5
- data/lib/arjdbc/oracle/connection_methods.rb +4 -1
- data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +21 -0
- data/lib/arjdbc/postgresql/adapter.rb +7 -1
- data/lib/arjdbc/postgresql/oid/bytea.rb +3 -0
- data/lib/arjdbc/postgresql/oid_types.rb +2 -1
- data/lib/arjdbc/tasks/database_tasks.rb +3 -0
- data/lib/arjdbc/util/quoted_cache.rb +2 -2
- data/lib/arjdbc/util/serialized_attributes.rb +11 -0
- data/lib/arjdbc/version.rb +1 -1
- data/rakelib/02-test.rake +1 -1
- data/rakelib/db.rake +3 -1
- data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +190 -0
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +259 -61
- data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +13 -2
- data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +192 -15
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +10 -2
- metadata +9 -4
data/lib/arjdbc/oracle/column.rb
CHANGED
@@ -11,13 +11,13 @@ module ArJdbc
|
|
11
11
|
|
12
12
|
def self.included(base)
|
13
13
|
# NOTE: assumes a standalone OracleColumn class
|
14
|
-
class << base; include Cast; end
|
14
|
+
class << base; include Cast; end # unless AR42
|
15
15
|
end
|
16
16
|
|
17
17
|
def primary=(value)
|
18
18
|
super
|
19
19
|
@type = :integer if value && @sql_type =~ /^NUMBER$/i
|
20
|
-
end
|
20
|
+
end unless AR42
|
21
21
|
|
22
22
|
def type_cast(value)
|
23
23
|
return nil if value.nil?
|
@@ -40,15 +40,19 @@ module ArJdbc
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
+
def sql_type
|
44
|
+
(@sql_type || '').start_with?('XMLTYPE') ? 'XMLTYPE' : @sql_type
|
45
|
+
end
|
46
|
+
|
43
47
|
private
|
44
48
|
|
45
49
|
def extract_limit(sql_type)
|
46
50
|
case sql_type
|
47
51
|
when /^(clob|date)/i then nil
|
48
|
-
when /^xml/i then
|
52
|
+
when /^xml/i then nil
|
49
53
|
else super
|
50
54
|
end
|
51
|
-
end
|
55
|
+
end unless AR42
|
52
56
|
|
53
57
|
def simplified_type(field_type)
|
54
58
|
case field_type
|
@@ -67,7 +71,7 @@ module ArJdbc
|
|
67
71
|
else
|
68
72
|
super
|
69
73
|
end
|
70
|
-
end
|
74
|
+
end unless AR42
|
71
75
|
|
72
76
|
# Post process default value from JDBC into a Rails-friendly format (columns{-internal})
|
73
77
|
def default_value(value)
|
@@ -9,9 +9,12 @@ ArJdbc::ConnectionMethods.module_eval do
|
|
9
9
|
return jndi_connection(config) if jndi_config?(config)
|
10
10
|
|
11
11
|
config[:port] ||= 1521
|
12
|
-
config[:url] ||= "jdbc:oracle:thin:@#{config[:host]}:#{config[:port]}:#{config[:database]}"
|
12
|
+
config[:url] ||= "jdbc:oracle:thin:@#{config[:host]}:#{config[:port]}:#{config[:database] || 'XE'}"
|
13
13
|
config[:driver] ||= "oracle.jdbc.driver.OracleDriver"
|
14
14
|
config[:connection_alive_sql] ||= 'SELECT 1 FROM DUAL'
|
15
|
+
unless config.key?(:statement_escape_processing)
|
16
|
+
config[:statement_escape_processing] = true
|
17
|
+
end
|
15
18
|
jdbc_connection(config)
|
16
19
|
end
|
17
20
|
alias_method :jdbcoracle_connection, :oracle_connection
|
@@ -0,0 +1,21 @@
|
|
1
|
+
ActiveRecord::ConnectionAdapters::PostgreSQL::OID::DateTime.class_eval do
|
2
|
+
def cast_value(value)
|
3
|
+
if value.is_a?(::String)
|
4
|
+
case value
|
5
|
+
when 'infinity' then ::Float::INFINITY
|
6
|
+
when '-infinity' then -::Float::INFINITY
|
7
|
+
#when / BC$/
|
8
|
+
# astronomical_year = format("%04d", value[/^\d+/].to_i)
|
9
|
+
# super(value.sub(/ BC$/, "").sub(/^\d+/, astronomical_year))
|
10
|
+
else
|
11
|
+
if value.end_with?(' BC')
|
12
|
+
DateTime.parse("-#{value}"[0...-3])
|
13
|
+
else
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
else
|
18
|
+
value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -44,7 +44,9 @@ module ArJdbc
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
+
# @see ActiveRecord::ConnectionAdapters::Jdbc::ArelSupport
|
47
48
|
def self.arel_visitor_type(config = nil)
|
49
|
+
require 'arel/visitors/postgresql_jdbc'
|
48
50
|
::Arel::Visitors::PostgreSQL
|
49
51
|
end
|
50
52
|
|
@@ -916,7 +918,7 @@ module ArJdbc
|
|
916
918
|
def _quote(value)
|
917
919
|
case value
|
918
920
|
when Type::Binary::Data
|
919
|
-
"'#{escape_bytea(value.to_s)}'"
|
921
|
+
"E'#{escape_bytea(value.to_s)}'"
|
920
922
|
when OID::Xml::Data
|
921
923
|
"xml '#{quote_string(value.to_s)}'"
|
922
924
|
when OID::Bit::Data
|
@@ -1182,6 +1184,7 @@ module ArJdbc
|
|
1182
1184
|
execute "ALTER INDEX #{quote_column_name(old_name)} RENAME TO #{quote_table_name(new_name)}"
|
1183
1185
|
end
|
1184
1186
|
|
1187
|
+
# @override
|
1185
1188
|
def supports_foreign_keys?; true end
|
1186
1189
|
|
1187
1190
|
def foreign_keys(table_name)
|
@@ -1477,6 +1480,9 @@ module ActiveRecord::ConnectionAdapters
|
|
1477
1480
|
|
1478
1481
|
require 'arjdbc/postgresql/oid_types' if ::ArJdbc::AR40
|
1479
1482
|
include ::ArJdbc::PostgreSQL::OIDTypes if ::ArJdbc::PostgreSQL.const_defined?(:OIDTypes)
|
1483
|
+
|
1484
|
+
load 'arjdbc/postgresql/_bc_time_cast_patch.rb' if ::ArJdbc::AR42
|
1485
|
+
|
1480
1486
|
include ::ArJdbc::PostgreSQL::ColumnHelpers if ::ArJdbc::AR42
|
1481
1487
|
|
1482
1488
|
include ::ArJdbc::Util::QuotedCache
|
@@ -5,6 +5,7 @@ module ArJdbc
|
|
5
5
|
|
6
6
|
if AR42
|
7
7
|
require 'active_record/connection_adapters/postgresql/oid'
|
8
|
+
require 'arjdbc/postgresql/oid/bytea.rb'
|
8
9
|
else
|
9
10
|
require 'arjdbc/postgresql/base/oid'
|
10
11
|
end
|
@@ -264,4 +265,4 @@ module ArJdbc
|
|
264
265
|
|
265
266
|
end
|
266
267
|
end
|
267
|
-
end
|
268
|
+
end
|
@@ -28,7 +28,7 @@ module ArJdbc
|
|
28
28
|
# Caches quoted table names, the cache is stored in the class'
|
29
29
|
# `QUOTED_TABLE_NAMES` constant.
|
30
30
|
# @return [String]
|
31
|
-
def quote_table_name(name)
|
31
|
+
def quote_table_name(name, *args)
|
32
32
|
if cache = self.class::QUOTED_TABLE_NAMES
|
33
33
|
unless quoted = cache[name]
|
34
34
|
quoted = super
|
@@ -43,7 +43,7 @@ module ArJdbc
|
|
43
43
|
# Caches quoted table names, the cache is stored in the class'
|
44
44
|
# `QUOTED_COLUMN_NAMES` constant.
|
45
45
|
# @return [String]
|
46
|
-
def quote_column_name(name)
|
46
|
+
def quote_column_name(name, *args)
|
47
47
|
if cache = self.class::QUOTED_COLUMN_NAMES
|
48
48
|
unless quoted = cache[name]
|
49
49
|
quoted = super
|
@@ -29,6 +29,15 @@ module ArJdbc
|
|
29
29
|
SerializedAttributes.dump_column_value(self, column)
|
30
30
|
end
|
31
31
|
|
32
|
+
if defined? ActiveRecord::Type::Serialized # ArJdbc::AR42
|
33
|
+
|
34
|
+
def self.dump_column_value(record, column)
|
35
|
+
value = record[ column.name.to_s ]
|
36
|
+
column.cast_type.type_cast_for_database(value)
|
37
|
+
end
|
38
|
+
|
39
|
+
else
|
40
|
+
|
32
41
|
def self.dump_column_value(record, column)
|
33
42
|
value = record[ name = column.name.to_s ]
|
34
43
|
if record.class.respond_to?(:serialized_attributes)
|
@@ -45,6 +54,8 @@ module ArJdbc
|
|
45
54
|
value
|
46
55
|
end
|
47
56
|
|
57
|
+
end
|
58
|
+
|
48
59
|
def self.setup(lob_type = nil, after_save_alias = nil)
|
49
60
|
ActiveRecord::Base.send :include, self # include SerializedAttributes
|
50
61
|
ActiveRecord::Base.lob_type = lob_type unless lob_type.nil?
|
data/lib/arjdbc/version.rb
CHANGED
data/rakelib/02-test.rake
CHANGED
@@ -94,7 +94,7 @@ test_task_for :AS400, :desc => "Run tests against AS400 (DB2) (ensure driver is
|
|
94
94
|
:files => FileList["test/db2*_test.rb"] + FileList["test/db/db2/*_test.rb"]
|
95
95
|
|
96
96
|
test_task_for 'JDBC', :desc => 'Run tests against plain JDBC adapter (uses MySQL and Derby)',
|
97
|
-
:files => FileList['test/*jdbc_*test.rb'] do |test_task|
|
97
|
+
:prereqs => 'db:mysql', :files => FileList['test/*jdbc_*test.rb'] do |test_task|
|
98
98
|
test_task.libs << 'jdbc-mysql/lib' << 'jdbc-derby/lib'
|
99
99
|
end
|
100
100
|
|
data/rakelib/db.rake
CHANGED
@@ -32,7 +32,9 @@ SQL
|
|
32
32
|
DROP DATABASE IF EXISTS #{POSTGRES_CONFIG[:database]};
|
33
33
|
DROP USER IF EXISTS #{POSTGRES_CONFIG[:username]};
|
34
34
|
CREATE USER #{POSTGRES_CONFIG[:username]} CREATEDB SUPERUSER LOGIN PASSWORD '#{POSTGRES_CONFIG[:password]}';
|
35
|
-
CREATE DATABASE #{POSTGRES_CONFIG[:database]} OWNER #{POSTGRES_CONFIG[:username]}
|
35
|
+
CREATE DATABASE #{POSTGRES_CONFIG[:database]} OWNER #{POSTGRES_CONFIG[:username]}
|
36
|
+
TEMPLATE template0
|
37
|
+
ENCODING '#{POSTGRES_CONFIG[:encoding]}' LC_COLLATE '#{POSTGRES_CONFIG[:collate]}' LC_CTYPE '#{POSTGRES_CONFIG[:collate]}';
|
36
38
|
SQL
|
37
39
|
params = { '-U' => ENV['PSQL_USER'] || 'postgres' }
|
38
40
|
params['-q'] = nil unless $VERBOSE
|
@@ -0,0 +1,190 @@
|
|
1
|
+
/*
|
2
|
+
* The MIT License
|
3
|
+
*
|
4
|
+
* Copyright 2015 Karol Bucek.
|
5
|
+
*
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
* of this software and associated documentation files (the "Software"), to deal
|
8
|
+
* in the Software without restriction, including without limitation the rights
|
9
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
* copies of the Software, and to permit persons to whom the Software is
|
11
|
+
* furnished to do so, subject to the following conditions:
|
12
|
+
*
|
13
|
+
* The above copyright notice and this permission notice shall be included in
|
14
|
+
* all copies or substantial portions of the Software.
|
15
|
+
*
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
* THE SOFTWARE.
|
23
|
+
*/
|
24
|
+
package arjdbc.firebird;
|
25
|
+
|
26
|
+
import arjdbc.jdbc.RubyJdbcConnection;
|
27
|
+
|
28
|
+
import java.sql.Connection;
|
29
|
+
import java.sql.ResultSet;
|
30
|
+
import java.sql.SQLException;
|
31
|
+
import java.sql.PreparedStatement;
|
32
|
+
import java.sql.ResultSetMetaData;
|
33
|
+
import java.sql.Types;
|
34
|
+
|
35
|
+
import org.jruby.Ruby;
|
36
|
+
import org.jruby.RubyClass;
|
37
|
+
import org.jruby.RubyString;
|
38
|
+
import org.jruby.runtime.ObjectAllocator;
|
39
|
+
import org.jruby.runtime.ThreadContext;
|
40
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
41
|
+
|
42
|
+
/**
|
43
|
+
* @author kares
|
44
|
+
*/
|
45
|
+
public class FirebirdRubyJdbcConnection extends RubyJdbcConnection {
|
46
|
+
|
47
|
+
protected FirebirdRubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
|
48
|
+
super(runtime, metaClass);
|
49
|
+
}
|
50
|
+
|
51
|
+
public static RubyClass createFirebirdJdbcConnectionClass(Ruby runtime, RubyClass jdbcConnection) {
|
52
|
+
final RubyClass clazz = RubyJdbcConnection.getConnectionAdapters(runtime).
|
53
|
+
defineClassUnder("FirebirdJdbcConnection", jdbcConnection, ALLOCATOR);
|
54
|
+
clazz.defineAnnotatedMethods(FirebirdRubyJdbcConnection.class);
|
55
|
+
return clazz;
|
56
|
+
}
|
57
|
+
|
58
|
+
private static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
|
59
|
+
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
|
60
|
+
return new FirebirdRubyJdbcConnection(runtime, klass);
|
61
|
+
}
|
62
|
+
};
|
63
|
+
|
64
|
+
@Override // resultSet.wasNull() might be falsy for '' treated as null
|
65
|
+
protected IRubyObject stringToRuby(final ThreadContext context,
|
66
|
+
final Ruby runtime, final ResultSet resultSet, final int column)
|
67
|
+
throws SQLException {
|
68
|
+
final String value = resultSet.getString(column);
|
69
|
+
if ( value == null ) return runtime.getNil();
|
70
|
+
return RubyString.newUnicodeString(runtime, value);
|
71
|
+
}
|
72
|
+
|
73
|
+
@Override // booleans are emulated can not setNull(index, Types.BOOLEAN)
|
74
|
+
protected void setBooleanParameter(final ThreadContext context,
|
75
|
+
final Connection connection, final PreparedStatement statement,
|
76
|
+
final int index, final Object value,
|
77
|
+
final IRubyObject column, final int type) throws SQLException {
|
78
|
+
if ( value instanceof IRubyObject ) {
|
79
|
+
setBooleanParameter(context, connection, statement, index, (IRubyObject) value, column, type);
|
80
|
+
}
|
81
|
+
else {
|
82
|
+
if ( value == null ) statement.setNull(index, Types.CHAR);
|
83
|
+
else {
|
84
|
+
statement.setBoolean(index, ((Boolean) value).booleanValue());
|
85
|
+
}
|
86
|
+
}
|
87
|
+
}
|
88
|
+
|
89
|
+
@Override // booleans are emulated can not setNull(index, Types.BOOLEAN)
|
90
|
+
protected void setBooleanParameter(final ThreadContext context,
|
91
|
+
final Connection connection, final PreparedStatement statement,
|
92
|
+
final int index, final IRubyObject value,
|
93
|
+
final IRubyObject column, final int type) throws SQLException {
|
94
|
+
if ( value.isNil() ) statement.setNull(index, Types.CHAR);
|
95
|
+
else {
|
96
|
+
statement.setBoolean(index, value.isTrue());
|
97
|
+
}
|
98
|
+
}
|
99
|
+
|
100
|
+
protected IRubyObject jdbcToRuby(
|
101
|
+
final ThreadContext context, final Ruby runtime,
|
102
|
+
final int column, final int type, final ResultSet resultSet)
|
103
|
+
throws SQLException {
|
104
|
+
|
105
|
+
switch (type) {
|
106
|
+
case SMALL_CHAR_1:
|
107
|
+
return smallChar1ToRuby(runtime, resultSet, column);
|
108
|
+
case SMALL_CHAR_2:
|
109
|
+
return smallChar2ToRuby(runtime, resultSet, column);
|
110
|
+
}
|
111
|
+
return super.jdbcToRuby(context, runtime, column, type, resultSet);
|
112
|
+
}
|
113
|
+
|
114
|
+
private static IRubyObject smallChar1ToRuby(
|
115
|
+
final Ruby runtime, final ResultSet resultSet, final int column)
|
116
|
+
throws SQLException {
|
117
|
+
String value = resultSet.getString(column);
|
118
|
+
if ( value == null ) return runtime.getNil();
|
119
|
+
if ( value.length() > 1 && value.charAt(1) == ' ' ) {
|
120
|
+
value = value.substring(0, 1);
|
121
|
+
}
|
122
|
+
return RubyString.newUnicodeString(runtime, value);
|
123
|
+
}
|
124
|
+
|
125
|
+
private static IRubyObject smallChar2ToRuby(
|
126
|
+
final Ruby runtime, final ResultSet resultSet, final int column)
|
127
|
+
throws SQLException {
|
128
|
+
String value = resultSet.getString(column);
|
129
|
+
if ( value == null ) return runtime.getNil();
|
130
|
+
if ( value.length() > 2 && value.charAt(2) == ' ' ) {
|
131
|
+
value = value.substring(0, 2);
|
132
|
+
}
|
133
|
+
return RubyString.newUnicodeString(runtime, value);
|
134
|
+
}
|
135
|
+
|
136
|
+
private final static int SMALL_CHAR_1 = 31431001;
|
137
|
+
private final static int SMALL_CHAR_2 = 31431002;
|
138
|
+
|
139
|
+
@Override
|
140
|
+
protected ColumnData[] extractColumns(final Ruby runtime,
|
141
|
+
final Connection connection, final ResultSet resultSet,
|
142
|
+
final boolean downCase) throws SQLException {
|
143
|
+
|
144
|
+
final ResultSetMetaData resultMetaData = resultSet.getMetaData();
|
145
|
+
|
146
|
+
final int columnCount = resultMetaData.getColumnCount();
|
147
|
+
final ColumnData[] columns = new ColumnData[columnCount];
|
148
|
+
|
149
|
+
for ( int i = 1; i <= columnCount; i++ ) { // metadata is one-based
|
150
|
+
String name = resultMetaData.getColumnLabel(i);
|
151
|
+
if ( downCase ) {
|
152
|
+
name = name.toLowerCase();
|
153
|
+
} else {
|
154
|
+
name = caseConvertIdentifierForRails(connection, name);
|
155
|
+
}
|
156
|
+
final RubyString columnName = RubyString.newUnicodeString(runtime, name);
|
157
|
+
|
158
|
+
int columnType = resultMetaData.getColumnType(i);
|
159
|
+
if (columnType == Types.CHAR) {
|
160
|
+
// CHAR(1) 'aligned' by JayBird to "1 "
|
161
|
+
final int prec = resultMetaData.getPrecision(i);
|
162
|
+
if ( prec == 1 ) {
|
163
|
+
columnType = SMALL_CHAR_1;
|
164
|
+
}
|
165
|
+
else if ( prec == 2 ) {
|
166
|
+
columnType = SMALL_CHAR_2;
|
167
|
+
}
|
168
|
+
}
|
169
|
+
|
170
|
+
columns[i - 1] = new ColumnData(columnName, columnType, i);
|
171
|
+
}
|
172
|
+
|
173
|
+
return columns;
|
174
|
+
}
|
175
|
+
|
176
|
+
// storesMixedCaseIdentifiers() return false;
|
177
|
+
// storesLowerCaseIdentifiers() return false;
|
178
|
+
// storesUpperCaseIdentifiers() return true;
|
179
|
+
|
180
|
+
@Override
|
181
|
+
protected String caseConvertIdentifierForRails(final Connection connection, final String value) {
|
182
|
+
return value == null ? null : value.toLowerCase();
|
183
|
+
}
|
184
|
+
|
185
|
+
@Override
|
186
|
+
protected String caseConvertIdentifierForJdbc(final Connection connection, final String value) {
|
187
|
+
return value == null ? null : value.toUpperCase();
|
188
|
+
}
|
189
|
+
|
190
|
+
}
|
@@ -54,6 +54,7 @@ import java.sql.Types;
|
|
54
54
|
import java.util.ArrayList;
|
55
55
|
import java.util.Calendar;
|
56
56
|
import java.util.Collection;
|
57
|
+
import java.util.HashMap;
|
57
58
|
import java.util.LinkedHashMap;
|
58
59
|
import java.util.List;
|
59
60
|
import java.util.Map;
|
@@ -152,6 +153,15 @@ public class RubyJdbcConnection extends RubyObject {
|
|
152
153
|
return getConnectionAdapters(runtime).getClass("IndexDefinition");
|
153
154
|
}
|
154
155
|
|
156
|
+
/**
|
157
|
+
* @param runtime
|
158
|
+
* @return <code>ActiveRecord::ConnectionAdapters::ForeignKeyDefinition</code>
|
159
|
+
* @note only since AR 4.2
|
160
|
+
*/
|
161
|
+
protected static RubyClass getForeignKeyDefinition(final Ruby runtime) {
|
162
|
+
return getConnectionAdapters(runtime).getClass("ForeignKeyDefinition");
|
163
|
+
}
|
164
|
+
|
155
165
|
/**
|
156
166
|
* @param runtime
|
157
167
|
* @return <code>ActiveRecord::JDBCError</code>
|
@@ -1168,7 +1178,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1168
1178
|
|
1169
1179
|
final DatabaseMetaData metaData = connection.getMetaData();
|
1170
1180
|
columns = metaData.getColumns(components.catalog, components.schema, components.name, null);
|
1171
|
-
return
|
1181
|
+
return mapColumnsResult(context, metaData, components, columns);
|
1172
1182
|
}
|
1173
1183
|
finally {
|
1174
1184
|
close(columns);
|
@@ -1205,7 +1215,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1205
1215
|
return withConnection(context, new Callable<IRubyObject>() {
|
1206
1216
|
public IRubyObject call(final Connection connection) throws SQLException {
|
1207
1217
|
final Ruby runtime = context.getRuntime();
|
1208
|
-
final RubyClass
|
1218
|
+
final RubyClass IndexDefinition = getIndexDefinition(context);
|
1209
1219
|
|
1210
1220
|
String _tableName = caseConvertIdentifierForJdbc(connection, tableName);
|
1211
1221
|
String _schemaName = caseConvertIdentifierForJdbc(connection, schemaName);
|
@@ -1248,7 +1258,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1248
1258
|
// orders, (since AR 3.2) where, type, using (AR 4.0)
|
1249
1259
|
};
|
1250
1260
|
|
1251
|
-
indexes.add(
|
1261
|
+
indexes.add( IndexDefinition.callMethod(context, "new", args) ); // IndexDefinition.new
|
1252
1262
|
}
|
1253
1263
|
|
1254
1264
|
// One or more columns can be associated with an index
|
@@ -1265,6 +1275,103 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1265
1275
|
});
|
1266
1276
|
}
|
1267
1277
|
|
1278
|
+
protected RubyClass getIndexDefinition(final ThreadContext context) {
|
1279
|
+
final RubyClass adapterClass = getAdapter(context).getMetaClass();
|
1280
|
+
IRubyObject IDef = adapterClass.getConstantAt("IndexDefinition");
|
1281
|
+
return IDef != null ? (RubyClass) IDef : getIndexDefinition(context.runtime);
|
1282
|
+
}
|
1283
|
+
|
1284
|
+
@JRubyMethod
|
1285
|
+
public IRubyObject foreign_keys(final ThreadContext context, IRubyObject table_name) {
|
1286
|
+
return foreignKeys(context, table_name.toString(), null, null);
|
1287
|
+
}
|
1288
|
+
|
1289
|
+
protected IRubyObject foreignKeys(final ThreadContext context, final String tableName, final String schemaName, final String catalog) {
|
1290
|
+
return withConnection(context, new Callable<IRubyObject>() {
|
1291
|
+
public IRubyObject call(final Connection connection) throws SQLException {
|
1292
|
+
final Ruby runtime = context.getRuntime();
|
1293
|
+
final RubyClass FKDefinition = getForeignKeyDefinition(context);
|
1294
|
+
|
1295
|
+
String _tableName = caseConvertIdentifierForJdbc(connection, tableName);
|
1296
|
+
String _schemaName = caseConvertIdentifierForJdbc(connection, schemaName);
|
1297
|
+
final TableName table = extractTableName(connection, catalog, _schemaName, _tableName);
|
1298
|
+
|
1299
|
+
ResultSet fkInfoSet = null;
|
1300
|
+
final List<IRubyObject> fKeys = new ArrayList<IRubyObject>(8);
|
1301
|
+
try {
|
1302
|
+
final DatabaseMetaData metaData = connection.getMetaData();
|
1303
|
+
fkInfoSet = metaData.getImportedKeys(table.catalog, table.schema, table.name);
|
1304
|
+
|
1305
|
+
while ( fkInfoSet.next() ) {
|
1306
|
+
final RubyHash options = RubyHash.newHash(runtime);
|
1307
|
+
|
1308
|
+
String fkName = fkInfoSet.getString("FK_NAME");
|
1309
|
+
if (fkName != null) {
|
1310
|
+
fkName = caseConvertIdentifierForRails(metaData, fkName);
|
1311
|
+
options.put(runtime.newSymbol("name"), fkName);
|
1312
|
+
}
|
1313
|
+
|
1314
|
+
String columnName = fkInfoSet.getString("FKCOLUMN_NAME");
|
1315
|
+
options.put(runtime.newSymbol("column"), caseConvertIdentifierForRails(metaData, columnName));
|
1316
|
+
|
1317
|
+
columnName = fkInfoSet.getString("PKCOLUMN_NAME");
|
1318
|
+
options.put(runtime.newSymbol("primary_key"), caseConvertIdentifierForRails(metaData, columnName));
|
1319
|
+
|
1320
|
+
String fkTableName = fkInfoSet.getString("FKTABLE_NAME");
|
1321
|
+
fkTableName = caseConvertIdentifierForRails(metaData, fkTableName);
|
1322
|
+
|
1323
|
+
String pkTableName = fkInfoSet.getString("PKTABLE_NAME");
|
1324
|
+
pkTableName = caseConvertIdentifierForRails(metaData, pkTableName);
|
1325
|
+
|
1326
|
+
final String onDelete = extractForeignKeyRule( fkInfoSet.getInt("DELETE_RULE") );
|
1327
|
+
if ( onDelete != null ) options.op_aset(context, runtime.newSymbol("on_delete"), runtime.newSymbol(onDelete));
|
1328
|
+
|
1329
|
+
final String onUpdate = extractForeignKeyRule( fkInfoSet.getInt("UPDATE_RULE") );
|
1330
|
+
if ( onUpdate != null ) options.op_aset(context, runtime.newSymbol("on_update"), runtime.newSymbol(onUpdate));
|
1331
|
+
|
1332
|
+
IRubyObject[] args = new IRubyObject[] {
|
1333
|
+
RubyString.newUnicodeString(runtime, fkTableName), // from_table
|
1334
|
+
RubyString.newUnicodeString(runtime, pkTableName), // to_table
|
1335
|
+
options
|
1336
|
+
};
|
1337
|
+
|
1338
|
+
fKeys.add( FKDefinition.callMethod(context, "new", args) ); // ForeignKeyDefinition.new
|
1339
|
+
}
|
1340
|
+
|
1341
|
+
return runtime.newArray(fKeys);
|
1342
|
+
|
1343
|
+
} finally { close(fkInfoSet); }
|
1344
|
+
}
|
1345
|
+
});
|
1346
|
+
}
|
1347
|
+
|
1348
|
+
protected String extractForeignKeyRule(final int rule) {
|
1349
|
+
switch (rule) {
|
1350
|
+
case DatabaseMetaData.importedKeyNoAction : return null ;
|
1351
|
+
case DatabaseMetaData.importedKeyCascade : return "cascade" ;
|
1352
|
+
case DatabaseMetaData.importedKeySetNull : return "nullify" ;
|
1353
|
+
case DatabaseMetaData.importedKeySetDefault: return "default" ;
|
1354
|
+
}
|
1355
|
+
return null;
|
1356
|
+
}
|
1357
|
+
|
1358
|
+
protected RubyClass getForeignKeyDefinition(final ThreadContext context) {
|
1359
|
+
final RubyClass adapterClass = getAdapter(context).getMetaClass();
|
1360
|
+
IRubyObject FKDef = adapterClass.getConstantAt("ForeignKeyDefinition");
|
1361
|
+
return FKDef != null ? (RubyClass) FKDef : getForeignKeyDefinition(context.runtime);
|
1362
|
+
}
|
1363
|
+
|
1364
|
+
|
1365
|
+
@JRubyMethod(name = "supports_foreign_keys?")
|
1366
|
+
public IRubyObject supports_foreign_keys_p(final ThreadContext context) throws SQLException {
|
1367
|
+
return withConnection(context, new Callable<IRubyObject>() {
|
1368
|
+
public IRubyObject call(final Connection connection) throws SQLException {
|
1369
|
+
final DatabaseMetaData metaData = connection.getMetaData();
|
1370
|
+
return context.getRuntime().newBoolean( metaData.supportsIntegrityEnhancementFacility() );
|
1371
|
+
}
|
1372
|
+
});
|
1373
|
+
}
|
1374
|
+
|
1268
1375
|
@JRubyMethod(name = "supports_views?")
|
1269
1376
|
public IRubyObject supports_views_p(final ThreadContext context) throws SQLException {
|
1270
1377
|
return withConnection(context, new Callable<IRubyObject>() {
|
@@ -1785,6 +1892,16 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1785
1892
|
return value;
|
1786
1893
|
}
|
1787
1894
|
|
1895
|
+
/**
|
1896
|
+
* @return AR::Type-casted value
|
1897
|
+
* @since 1.3.18
|
1898
|
+
*/
|
1899
|
+
protected static IRubyObject typeCastFromDatabase(final ThreadContext context,
|
1900
|
+
final IRubyObject adapter, final RubySymbol typeName, final RubyString value) {
|
1901
|
+
final IRubyObject type = adapter.callMethod(context, "lookup_cast_type", typeName);
|
1902
|
+
return type.callMethod(context, "type_cast_from_database", value);
|
1903
|
+
}
|
1904
|
+
|
1788
1905
|
protected IRubyObject dateToRuby(final ThreadContext context,
|
1789
1906
|
final Ruby runtime, final ResultSet resultSet, final int column)
|
1790
1907
|
throws SQLException {
|
@@ -1800,6 +1917,11 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1800
1917
|
|
1801
1918
|
final IRubyObject adapter = callMethod(context, "adapter"); // self.adapter
|
1802
1919
|
if ( adapter.isNil() ) return strValue; // NOTE: we warn on init_connection
|
1920
|
+
|
1921
|
+
if ( usesType(runtime) ) {
|
1922
|
+
// NOTE: this CAN NOT be 100% correct - as :date is just a type guess!
|
1923
|
+
return typeCastFromDatabase(context, adapter, runtime.newSymbol("date"), strValue);
|
1924
|
+
}
|
1803
1925
|
return adapter.callMethod(context, "_string_to_date", strValue);
|
1804
1926
|
}
|
1805
1927
|
|
@@ -1818,6 +1940,11 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1818
1940
|
|
1819
1941
|
final IRubyObject adapter = callMethod(context, "adapter"); // self.adapter
|
1820
1942
|
if ( adapter.isNil() ) return strValue; // NOTE: we warn on init_connection
|
1943
|
+
|
1944
|
+
if ( usesType(runtime) ) {
|
1945
|
+
// NOTE: this CAN NOT be 100% correct - as :time is just a type guess!
|
1946
|
+
return typeCastFromDatabase(context, adapter, runtime.newSymbol("time"), strValue);
|
1947
|
+
}
|
1821
1948
|
return adapter.callMethod(context, "_string_to_time", strValue);
|
1822
1949
|
}
|
1823
1950
|
|
@@ -1836,6 +1963,11 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1836
1963
|
|
1837
1964
|
final IRubyObject adapter = callMethod(context, "adapter"); // self.adapter
|
1838
1965
|
if ( adapter.isNil() ) return strValue; // NOTE: we warn on init_connection
|
1966
|
+
|
1967
|
+
if ( usesType(runtime) ) {
|
1968
|
+
// NOTE: this CAN NOT be 100% correct - as :timestamp is just a type guess!
|
1969
|
+
return typeCastFromDatabase(context, adapter, runtime.newSymbol("timestamp"), strValue);
|
1970
|
+
}
|
1839
1971
|
return adapter.callMethod(context, "_string_to_timestamp", strValue);
|
1840
1972
|
}
|
1841
1973
|
|
@@ -2032,9 +2164,17 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2032
2164
|
}
|
2033
2165
|
|
2034
2166
|
protected void setStatementParameter(final ThreadContext context,
|
2035
|
-
|
2036
|
-
|
2037
|
-
|
2167
|
+
final Ruby runtime, final Connection connection,
|
2168
|
+
final PreparedStatement statement, final int index,
|
2169
|
+
final Object rawValue, final IRubyObject column) throws SQLException {
|
2170
|
+
final Object value;
|
2171
|
+
|
2172
|
+
if ( isAr42(column) ) {
|
2173
|
+
final IRubyObject castType = column.callMethod(context, "cast_type");
|
2174
|
+
value = castType.callMethod(context, "type_cast_for_database", (IRubyObject) rawValue);
|
2175
|
+
} else {
|
2176
|
+
value = rawValue;
|
2177
|
+
}
|
2038
2178
|
|
2039
2179
|
final int type = jdbcTypeFor(context, runtime, column, value);
|
2040
2180
|
|
@@ -2078,7 +2218,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2078
2218
|
setXmlParameter(context, connection, statement, index, value, column, type);
|
2079
2219
|
break;
|
2080
2220
|
case Types.ARRAY:
|
2081
|
-
setArrayParameter(context, connection, statement, index,
|
2221
|
+
setArrayParameter(context, connection, statement, index, rawValue, column, type);
|
2082
2222
|
break;
|
2083
2223
|
case Types.JAVA_OBJECT:
|
2084
2224
|
case Types.OTHER:
|
@@ -2135,7 +2275,49 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2135
2275
|
if ( column == null || column.isNil() ) {
|
2136
2276
|
throw runtime.newArgumentError("nil column passed");
|
2137
2277
|
}
|
2138
|
-
|
2278
|
+
|
2279
|
+
final IRubyObject type = column.callMethod(context, "type");
|
2280
|
+
if ( type.isNil() || ! (type instanceof RubySymbol) ) {
|
2281
|
+
throw new IllegalStateException("unexpected type = " + type.inspect() + " for " + column.inspect());
|
2282
|
+
}
|
2283
|
+
return (RubySymbol) type;
|
2284
|
+
}
|
2285
|
+
|
2286
|
+
protected static final Map<String, Integer> JDBC_TYPE_FOR = new HashMap<String, Integer>(32, 1);
|
2287
|
+
static {
|
2288
|
+
JDBC_TYPE_FOR.put("string", Types.VARCHAR);
|
2289
|
+
JDBC_TYPE_FOR.put("text", Types.CLOB);
|
2290
|
+
JDBC_TYPE_FOR.put("integer", Types.INTEGER);
|
2291
|
+
JDBC_TYPE_FOR.put("float", Types.FLOAT);
|
2292
|
+
JDBC_TYPE_FOR.put("real", Types.REAL);
|
2293
|
+
JDBC_TYPE_FOR.put("decimal", Types.DECIMAL);
|
2294
|
+
JDBC_TYPE_FOR.put("date", Types.DATE);
|
2295
|
+
JDBC_TYPE_FOR.put("time", Types.TIME);
|
2296
|
+
JDBC_TYPE_FOR.put("datetime", Types.TIMESTAMP);
|
2297
|
+
JDBC_TYPE_FOR.put("timestamp", Types.TIMESTAMP);
|
2298
|
+
JDBC_TYPE_FOR.put("binary", Types.BLOB);
|
2299
|
+
JDBC_TYPE_FOR.put("boolean", Types.BOOLEAN);
|
2300
|
+
JDBC_TYPE_FOR.put("array", Types.ARRAY);
|
2301
|
+
JDBC_TYPE_FOR.put("xml", Types.SQLXML);
|
2302
|
+
|
2303
|
+
// also mapping standard SQL names :
|
2304
|
+
JDBC_TYPE_FOR.put("bit", Types.BIT);
|
2305
|
+
JDBC_TYPE_FOR.put("tinyint", Types.TINYINT);
|
2306
|
+
JDBC_TYPE_FOR.put("smallint", Types.SMALLINT);
|
2307
|
+
JDBC_TYPE_FOR.put("bigint", Types.BIGINT);
|
2308
|
+
JDBC_TYPE_FOR.put("int", Types.INTEGER);
|
2309
|
+
JDBC_TYPE_FOR.put("double", Types.DOUBLE);
|
2310
|
+
JDBC_TYPE_FOR.put("numeric", Types.NUMERIC);
|
2311
|
+
JDBC_TYPE_FOR.put("char", Types.CHAR);
|
2312
|
+
JDBC_TYPE_FOR.put("varchar", Types.VARCHAR);
|
2313
|
+
JDBC_TYPE_FOR.put("binary", Types.BINARY);
|
2314
|
+
JDBC_TYPE_FOR.put("varbinary", Types.VARBINARY);
|
2315
|
+
//JDBC_TYPE_FOR.put("struct", Types.STRUCT);
|
2316
|
+
JDBC_TYPE_FOR.put("blob", Types.BLOB);
|
2317
|
+
JDBC_TYPE_FOR.put("clob", Types.CLOB);
|
2318
|
+
JDBC_TYPE_FOR.put("nchar", Types.NCHAR);
|
2319
|
+
JDBC_TYPE_FOR.put("nvarchar", Types.NVARCHAR);
|
2320
|
+
JDBC_TYPE_FOR.put("nclob", Types.NCLOB);
|
2139
2321
|
}
|
2140
2322
|
|
2141
2323
|
protected int jdbcTypeFor(final ThreadContext context, final Ruby runtime,
|
@@ -2150,39 +2332,20 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2150
2332
|
internedType = "array";
|
2151
2333
|
}
|
2152
2334
|
else {
|
2153
|
-
|
2154
|
-
internedType = columnType.asJavaString();
|
2335
|
+
internedType = resolveColumnType(context, runtime, column).asJavaString();
|
2155
2336
|
}
|
2156
2337
|
}
|
2157
2338
|
else {
|
2158
|
-
if ( value instanceof RubyInteger )
|
2159
|
-
|
2160
|
-
|
2161
|
-
else
|
2162
|
-
internedType = "float";
|
2163
|
-
}
|
2164
|
-
else if ( value instanceof RubyTime ) {
|
2165
|
-
internedType = "timestamp";
|
2166
|
-
}
|
2167
|
-
else {
|
2168
|
-
internedType = "string";
|
2169
|
-
}
|
2339
|
+
if ( value instanceof RubyInteger ) internedType = "integer";
|
2340
|
+
else if ( value instanceof RubyNumeric ) internedType = "float";
|
2341
|
+
else if ( value instanceof RubyTime ) internedType = "timestamp";
|
2342
|
+
else internedType = "string";
|
2170
2343
|
}
|
2171
2344
|
|
2172
|
-
|
2173
|
-
|
2174
|
-
|
2175
|
-
|
2176
|
-
else if ( internedType == (Object) "float" ) return Types.FLOAT;
|
2177
|
-
else if ( internedType == (Object) "date" ) return Types.DATE;
|
2178
|
-
else if ( internedType == (Object) "time" ) return Types.TIME;
|
2179
|
-
else if ( internedType == (Object) "datetime" ) return Types.TIMESTAMP;
|
2180
|
-
else if ( internedType == (Object) "timestamp" ) return Types.TIMESTAMP;
|
2181
|
-
else if ( internedType == (Object) "binary" ) return Types.BLOB;
|
2182
|
-
else if ( internedType == (Object) "boolean" ) return Types.BOOLEAN;
|
2183
|
-
else if ( internedType == (Object) "xml" ) return Types.SQLXML;
|
2184
|
-
else if ( internedType == (Object) "array" ) return Types.ARRAY;
|
2185
|
-
else return Types.OTHER; // -1 as well as 0 are used in Types
|
2345
|
+
final Integer sqlType = JDBC_TYPE_FOR.get(internedType);
|
2346
|
+
if ( sqlType != null ) return sqlType.intValue();
|
2347
|
+
|
2348
|
+
return Types.OTHER; // -1 as well as 0 are used in Types
|
2186
2349
|
}
|
2187
2350
|
|
2188
2351
|
protected void setIntegerParameter(final ThreadContext context,
|
@@ -2338,6 +2501,9 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2338
2501
|
if ( value.getMetaClass().getName().indexOf("BigDecimal") != -1 ) {
|
2339
2502
|
statement.setBigDecimal(index, getBigDecimalValue(value));
|
2340
2503
|
}
|
2504
|
+
else if ( value instanceof RubyInteger ) {
|
2505
|
+
statement.setBigDecimal(index, new BigDecimal(((RubyInteger) value).getBigIntegerValue()));
|
2506
|
+
}
|
2341
2507
|
else if ( value instanceof RubyNumeric ) {
|
2342
2508
|
statement.setDouble(index, ((RubyNumeric) value).getDoubleValue());
|
2343
2509
|
}
|
@@ -2617,15 +2783,15 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2617
2783
|
}
|
2618
2784
|
|
2619
2785
|
protected void setArrayParameter(final ThreadContext context,
|
2620
|
-
|
2621
|
-
|
2622
|
-
|
2786
|
+
final Connection connection, final PreparedStatement statement,
|
2787
|
+
final int index, final Object value,
|
2788
|
+
final IRubyObject column, final int type) throws SQLException {
|
2623
2789
|
if ( value instanceof IRubyObject ) {
|
2624
2790
|
setArrayParameter(context, connection, statement, index, (IRubyObject) value, column, type);
|
2625
|
-
}
|
2626
|
-
|
2627
|
-
|
2628
|
-
else {
|
2791
|
+
} else {
|
2792
|
+
if ( value == null ) {
|
2793
|
+
statement.setNull(index, Types.ARRAY);
|
2794
|
+
} else {
|
2629
2795
|
String typeName = resolveArrayBaseTypeName(context, value, column, type);
|
2630
2796
|
Array array = connection.createArrayOf(typeName, (Object[]) value);
|
2631
2797
|
statement.setArray(index, array);
|
@@ -2637,8 +2803,9 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2637
2803
|
final Connection connection, final PreparedStatement statement,
|
2638
2804
|
final int index, final IRubyObject value,
|
2639
2805
|
final IRubyObject column, final int type) throws SQLException {
|
2640
|
-
if ( value.isNil() )
|
2641
|
-
|
2806
|
+
if ( value.isNil() ) {
|
2807
|
+
statement.setNull(index, Types.ARRAY);
|
2808
|
+
} else {
|
2642
2809
|
String typeName = resolveArrayBaseTypeName(context, value, column, type);
|
2643
2810
|
Array array = connection.createArrayOf(typeName, ((RubyArray) value).toArray());
|
2644
2811
|
statement.setArray(index, array);
|
@@ -2963,15 +3130,41 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2963
3130
|
return defaultValue == null ? runtime.getNil() : RubyString.newUnicodeString(runtime, defaultValue);
|
2964
3131
|
}
|
2965
3132
|
|
2966
|
-
|
3133
|
+
/**
|
3134
|
+
* Internal API that might be subject to change!
|
3135
|
+
* @since 1.3.18
|
3136
|
+
*/
|
3137
|
+
protected static boolean usesType(final Ruby runtime) { // AR 4.2
|
3138
|
+
return runtime.getModule("ActiveRecord").getConstantAt("Type") != null;
|
3139
|
+
}
|
3140
|
+
|
3141
|
+
/**
|
3142
|
+
* This method is considered internal and is not part of AR-JDBC's Java ext
|
3143
|
+
* API and thus might be subject to change in the future.
|
3144
|
+
* Please copy it to your own class if you rely on it to avoid issues.
|
3145
|
+
*/
|
3146
|
+
protected static boolean isAr42(IRubyObject column) {
|
3147
|
+
return column.respondsTo("cast_type");
|
3148
|
+
}
|
3149
|
+
|
3150
|
+
protected RubyArray mapColumnsResult(final ThreadContext context,
|
2967
3151
|
final DatabaseMetaData metaData, final TableName components, final ResultSet results)
|
2968
3152
|
throws SQLException {
|
2969
3153
|
|
2970
|
-
final
|
2971
|
-
final
|
2972
|
-
|
3154
|
+
final RubyClass Column = getJdbcColumnClass(context);
|
3155
|
+
final boolean lookupCastType = Column.isMethodBound("cast_type", false);
|
2973
3156
|
// NOTE: primary/primary= methods were removed from Column in AR 4.2
|
2974
|
-
|
3157
|
+
// setPrimary = ! lookupCastType by default ... it's better than checking
|
3158
|
+
// whether primary= is bound since it might be a left over in AR-JDBC ext
|
3159
|
+
return mapColumnsResult(context, metaData, components, results, Column, lookupCastType, ! lookupCastType);
|
3160
|
+
}
|
3161
|
+
|
3162
|
+
protected final RubyArray mapColumnsResult(final ThreadContext context,
|
3163
|
+
final DatabaseMetaData metaData, final TableName components, final ResultSet results,
|
3164
|
+
final RubyClass Column, final boolean lookupCastType, final boolean setPrimary)
|
3165
|
+
throws SQLException {
|
3166
|
+
|
3167
|
+
final Ruby runtime = context.getRuntime();
|
2975
3168
|
|
2976
3169
|
final Collection<String> primaryKeyNames =
|
2977
3170
|
setPrimary ? getPrimaryKeyNames(metaData, components) : null;
|
@@ -2980,22 +3173,27 @@ public class RubyJdbcConnection extends RubyObject {
|
|
2980
3173
|
final IRubyObject config = getConfig(context);
|
2981
3174
|
while ( results.next() ) {
|
2982
3175
|
final String colName = results.getString(COLUMN_NAME);
|
2983
|
-
|
2984
|
-
|
2985
|
-
|
2986
|
-
|
2987
|
-
|
2988
|
-
|
2989
|
-
|
2990
|
-
}
|
3176
|
+
final RubyString railsColumnName = RubyString.newUnicodeString(runtime, caseConvertIdentifierForRails(metaData, colName));
|
3177
|
+
final IRubyObject defaultValue = defaultValueFromResultSet( runtime, results );
|
3178
|
+
final RubyString sqlType = RubyString.newUnicodeString( runtime, typeFromResultSet(results) );
|
3179
|
+
final RubyBoolean nullable = runtime.newBoolean( ! results.getString(IS_NULLABLE).trim().equals("NO") );
|
3180
|
+
final IRubyObject[] args;
|
3181
|
+
if ( lookupCastType ) {
|
3182
|
+
final IRubyObject castType = getAdapter(context).callMethod(context, "lookup_cast_type", sqlType);
|
3183
|
+
args = new IRubyObject[] {config, railsColumnName, defaultValue, castType, sqlType, nullable};
|
3184
|
+
} else {
|
3185
|
+
args = new IRubyObject[] {config, railsColumnName, defaultValue, sqlType, nullable};
|
3186
|
+
}
|
3187
|
+
|
3188
|
+
IRubyObject column = Column.callMethod(context, "new", args);
|
2991
3189
|
columns.append(column);
|
2992
3190
|
|
2993
|
-
if ( primaryKeyNames != null
|
2994
|
-
|
3191
|
+
if ( primaryKeyNames != null ) {
|
3192
|
+
final RubyBoolean primary = runtime.newBoolean( primaryKeyNames.contains(colName) );
|
3193
|
+
column.getInstanceVariables().setInstanceVariable("@primary", primary);
|
2995
3194
|
}
|
2996
3195
|
}
|
2997
3196
|
return columns;
|
2998
|
-
|
2999
3197
|
}
|
3000
3198
|
|
3001
3199
|
private static Collection<String> getPrimaryKeyNames(final DatabaseMetaData metaData,
|