activerecord-jdbc-adapter 1.0.0.beta1-java → 1.0.0.beta2-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. data/History.txt +37 -0
  2. data/Manifest.txt +8 -0
  3. data/README.txt +41 -88
  4. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -0
  5. data/lib/arel/engines/sql/compilers/hsqldb_compiler.rb +9 -0
  6. data/lib/arel/engines/sql/compilers/mssql_compiler.rb +34 -0
  7. data/lib/arjdbc/db2/adapter.rb +232 -52
  8. data/lib/arjdbc/derby/adapter.rb +28 -1
  9. data/lib/arjdbc/derby/connection_methods.rb +1 -1
  10. data/lib/arjdbc/discover.rb +1 -1
  11. data/lib/arjdbc/firebird/adapter.rb +26 -0
  12. data/lib/arjdbc/h2/adapter.rb +13 -0
  13. data/lib/arjdbc/hsqldb/adapter.rb +8 -6
  14. data/lib/arjdbc/informix/adapter.rb +4 -0
  15. data/lib/arjdbc/jdbc/adapter.rb +27 -5
  16. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  17. data/lib/arjdbc/jdbc/connection.rb +76 -45
  18. data/lib/arjdbc/jdbc/jdbc.rake +22 -20
  19. data/lib/arjdbc/jdbc/type_converter.rb +9 -2
  20. data/lib/arjdbc/mssql/adapter.rb +102 -24
  21. data/lib/arjdbc/mssql/connection_methods.rb +19 -2
  22. data/lib/arjdbc/mssql/tsql_helper.rb +1 -0
  23. data/lib/arjdbc/mysql/adapter.rb +6 -0
  24. data/lib/arjdbc/mysql/connection_methods.rb +8 -7
  25. data/lib/arjdbc/oracle/adapter.rb +8 -6
  26. data/lib/arjdbc/postgresql/adapter.rb +51 -19
  27. data/lib/arjdbc/version.rb +1 -1
  28. data/lib/jdbc_adapter/rake_tasks.rb +3 -0
  29. data/rails_generators/templates/lib/tasks/jdbc.rake +2 -2
  30. data/rakelib/package.rake +2 -0
  31. data/rakelib/test.rake +6 -3
  32. data/src/java/arjdbc/derby/DerbyModule.java +30 -1
  33. data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +74 -0
  34. data/src/java/arjdbc/jdbc/AdapterJavaService.java +7 -3
  35. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +45 -30
  36. data/src/java/arjdbc/mssql/MssqlRubyJdbcConnection.java +54 -1
  37. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +85 -0
  38. data/test/abstract_db_create.rb +6 -1
  39. data/test/db/jndi_config.rb +20 -10
  40. data/test/db2_simple_test.rb +34 -1
  41. data/test/derby_simple_test.rb +78 -0
  42. data/test/generic_jdbc_connection_test.rb +21 -1
  43. data/test/jndi_callbacks_test.rb +2 -1
  44. data/test/jndi_test.rb +1 -11
  45. data/test/models/entry.rb +20 -0
  46. data/test/mssql_limit_offset_test.rb +28 -0
  47. data/test/mssql_simple_test.rb +7 -1
  48. data/test/mysql_info_test.rb +49 -6
  49. data/test/mysql_simple_test.rb +4 -0
  50. data/test/oracle_simple_test.rb +3 -47
  51. data/test/oracle_specific_test.rb +83 -0
  52. data/test/postgres_db_create_test.rb +6 -0
  53. data/test/postgres_drop_db_test.rb +16 -0
  54. data/test/postgres_simple_test.rb +17 -0
  55. data/test/postgres_table_alias_length_test.rb +15 -0
  56. data/test/simple.rb +17 -4
  57. metadata +33 -7
@@ -1,6 +1,6 @@
1
1
  module ArJdbc
2
2
  module Version
3
- VERSION = "1.0.0.beta1"
3
+ VERSION = "1.0.0.beta2"
4
4
  end
5
5
  end
6
6
  # Compatibility with older versions of ar-jdbc for other extensions out there
@@ -0,0 +1,3 @@
1
+ warn "DEPRECATED: require 'arjdbc/rake_tasks' instead of 'jdbc_adapter/rake_tasks'."
2
+ require 'arjdbc/jdbc/rake_tasks'
3
+
@@ -3,6 +3,6 @@
3
3
  #
4
4
  # This file allows you to use Rails' various db:* tasks with JDBC.
5
5
  if defined?(JRUBY_VERSION)
6
- require 'jdbc_adapter'
7
- require 'jdbc_adapter/rake_tasks'
6
+ require 'arjdbc'
7
+ require 'arjdbc/jdbc/rake_tasks'
8
8
  end
data/rakelib/package.rake CHANGED
@@ -24,6 +24,8 @@ begin
24
24
  p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
25
25
  p.description = p.paragraphs_of('README.txt', 0...1).join("\n\n")
26
26
  end
27
+ hoe.spec.rdoc_options += ["-SHN", "-f", "darkfish"]
28
+
27
29
  task :gemspec do
28
30
  File.open("#{hoe.name}.gemspec", "w") {|f| f << hoe.spec.to_ruby }
29
31
  end
data/rakelib/test.rake CHANGED
@@ -4,6 +4,9 @@ if defined?(JRUBY_VERSION)
4
4
  if find_executable?("psql") && `psql -c '\\l'` && $?.exitstatus == 0
5
5
  databases << :test_postgres
6
6
  end
7
+ if File.exist?('test/fscontext.jar')
8
+ databases << :test_jndi
9
+ end
7
10
  task :test => databases
8
11
  else
9
12
  task :test => [:test_mysql]
@@ -37,12 +40,12 @@ declare_test_task_for :postgres
37
40
  declare_test_task_for :sqlite3
38
41
 
39
42
  Rake::TestTask.new(:test_jdbc) do |t|
40
- t.test_files = FileList['test/generic_jdbc_connection_test.rb', 'test/jndi_callbacks_test.rb']
43
+ t.test_files = FileList['test/generic_jdbc_connection_test.rb']
41
44
  t.libs << 'test' << 'drivers/mysql/lib'
42
45
  end
43
46
 
44
47
  Rake::TestTask.new(:test_jndi) do |t|
45
- t.test_files = FileList['test/jndi_test.rb']
48
+ t.test_files = FileList['test/jndi*_test.rb']
46
49
  t.libs << 'test' << 'drivers/derby/lib'
47
50
  end
48
51
 
@@ -52,7 +55,7 @@ task :test_pgsql => [:test_postgres]
52
55
  # Ensure driver for these DBs is on your classpath
53
56
  %w(oracle db2 cachedb informix).each do |d|
54
57
  Rake::TestTask.new("test_#{d}") do |t|
55
- t.test_files = FileList["test/#{d}_simple_test.rb"]
58
+ t.test_files = FileList["test/#{d}*_test.rb"]
56
59
  t.libs = []
57
60
  t.libs << 'lib' if defined?(JRUBY_VERSION)
58
61
  t.libs << 'test'
@@ -111,6 +111,10 @@ public class DerbyModule {
111
111
  if (args.length > 1) {
112
112
  IRubyObject col = args[1];
113
113
  String type = rubyApi.callMethod(col, "type").toString();
114
+ // intercept and change value, maybe, if the column type is :text or :string
115
+ if (type.equals("text") || type.equals("string")) {
116
+ value = make_ruby_string_for_text_column(context, recv, runtime, value);
117
+ }
114
118
  if (value instanceof RubyString) {
115
119
  if (type.equals("string")) {
116
120
  return quote_string_with_surround(runtime, "'", (RubyString)value, "'");
@@ -135,7 +139,32 @@ public class DerbyModule {
135
139
  return super_quote(context, recv, runtime, value, runtime.getNil());
136
140
  }
137
141
 
138
- private final static ByteList NULL = new ByteList("NULL".getBytes());
142
+ /*
143
+ * Derby is not permissive like MySql. Try and send an Integer to a CLOB or VARCHAR column and Derby will vomit.
144
+ * This method turns non stringy things into strings.
145
+ */
146
+ private static IRubyObject make_ruby_string_for_text_column(ThreadContext context, IRubyObject recv, Ruby runtime, IRubyObject value) {
147
+ RubyModule multibyteChars = (RubyModule)
148
+ ((RubyModule) ((RubyModule) runtime.getModule("ActiveSupport")).getConstant("Multibyte")).getConstantAt("Chars");
149
+ if (value instanceof RubyString || rubyApi.isKindOf(value, multibyteChars) || value.isNil()) {
150
+ return value;
151
+ }
152
+ if (value instanceof RubyBoolean) {
153
+ return value.isTrue() ? runtime.newString("1") : runtime.newString("0");
154
+ } else if (value instanceof RubyFloat || value instanceof RubyFixnum || value instanceof RubyBignum) {
155
+ return RubyString.objAsString(context, value);
156
+ } else if ( value instanceof RubyBigDecimal) {
157
+ return rubyApi.callMethod(value, "to_s", runtime.newString("F"));
158
+ } else {
159
+ if (rubyApi.callMethod(value, "acts_like?", runtime.newString("date")).isTrue() || rubyApi.callMethod(value, "acts_like?", runtime.newString("time")).isTrue()) {
160
+ return (RubyString)rubyApi.callMethod(recv, "quoted_date", value);
161
+ } else {
162
+ return (RubyString)rubyApi.callMethod(value, "to_yaml");
163
+ }
164
+ }
165
+ }
166
+
167
+ private final static ByteList NULL = new ByteList("NULL".getBytes());
139
168
 
140
169
  private static IRubyObject super_quote(ThreadContext context, IRubyObject recv, Ruby runtime, IRubyObject value, IRubyObject col) {
141
170
  if (value.respondsTo("quoted_id")) {
@@ -0,0 +1,74 @@
1
+ /*
2
+ **** BEGIN LICENSE BLOCK *****
3
+ * Copyright (c) 2006-2010 Nick Sieger <nick@nicksieger.com>
4
+ * Copyright (c) 2006-2007 Ola Bini <ola.bini@gmail.com>
5
+ * Copyright (c) 2008-2009 Thomas E Enebo <enebo@acm.org>
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person obtaining
8
+ * a copy of this software and associated documentation files (the
9
+ * "Software"), to deal in the Software without restriction, including
10
+ * without limitation the rights to use, copy, modify, merge, publish,
11
+ * distribute, sublicense, and/or sell copies of the Software, and to
12
+ * permit persons to whom the Software is furnished to do so, subject to
13
+ * the following conditions:
14
+ *
15
+ * The above copyright notice and this permission notice shall be
16
+ * included in all copies or substantial portions of the Software.
17
+ *
18
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+ ***** END LICENSE BLOCK *****/
26
+
27
+ package arjdbc.informix;
28
+
29
+ import java.sql.ResultSet;
30
+ import java.sql.SQLException;
31
+ import java.sql.Types;
32
+
33
+ import arjdbc.jdbc.RubyJdbcConnection;
34
+
35
+ import org.jruby.Ruby;
36
+ import org.jruby.RubyClass;
37
+ import org.jruby.runtime.ObjectAllocator;
38
+ import org.jruby.runtime.builtin.IRubyObject;
39
+
40
+ /**
41
+ *
42
+ * @author nicksieger
43
+ */
44
+ public class InformixRubyJdbcConnection extends RubyJdbcConnection {
45
+ protected InformixRubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
46
+ super(runtime, metaClass);
47
+ }
48
+
49
+ public static RubyClass createInformixJdbcConnectionClass(Ruby runtime, RubyClass jdbcConnection) {
50
+ RubyClass clazz = RubyJdbcConnection.getConnectionAdapters(runtime).defineClassUnder("InformixJdbcConnection",
51
+ jdbcConnection, INFORMIX_JDBCCONNECTION_ALLOCATOR);
52
+ clazz.defineAnnotatedMethods(InformixRubyJdbcConnection.class);
53
+
54
+ return clazz;
55
+ }
56
+
57
+ private static ObjectAllocator INFORMIX_JDBCCONNECTION_ALLOCATOR = new ObjectAllocator() {
58
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
59
+ return new InformixRubyJdbcConnection(runtime, klass);
60
+ }
61
+ };
62
+
63
+ /**
64
+ * Treat LONGVARCHAR as CLOB on Informix for purposes of converting a JDBC value to Ruby.
65
+ */
66
+ @Override
67
+ protected IRubyObject jdbcToRuby(Ruby runtime, int column, int type, ResultSet resultSet)
68
+ throws SQLException {
69
+ if (type == Types.LONGVARCHAR) {
70
+ type = Types.CLOB;
71
+ }
72
+ return super.jdbcToRuby(runtime, column, type, resultSet);
73
+ }
74
+ }
@@ -28,11 +28,13 @@ package arjdbc.jdbc;
28
28
 
29
29
  import java.io.IOException;
30
30
 
31
- import arjdbc.postgresql.PostgresqlRubyJdbcConnection;
31
+ import arjdbc.derby.DerbyModule;
32
+ import arjdbc.informix.InformixRubyJdbcConnection;
32
33
  import arjdbc.mssql.MssqlRubyJdbcConnection;
33
- import arjdbc.sqlite3.Sqlite3RubyJdbcConnection;
34
34
  import arjdbc.mysql.MySQLModule;
35
- import arjdbc.derby.DerbyModule;
35
+ import arjdbc.oracle.OracleRubyJdbcConnection;
36
+ import arjdbc.postgresql.PostgresqlRubyJdbcConnection;
37
+ import arjdbc.sqlite3.Sqlite3RubyJdbcConnection;
36
38
 
37
39
  import org.jruby.Ruby;
38
40
  import org.jruby.RubyClass;
@@ -48,6 +50,8 @@ public class AdapterJavaService implements BasicLibraryService {
48
50
  RubyClass jdbcConnection = RubyJdbcConnection.createJdbcConnectionClass(runtime);
49
51
  PostgresqlRubyJdbcConnection.createPostgresqlJdbcConnectionClass(runtime, jdbcConnection);
50
52
  MssqlRubyJdbcConnection.createMssqlJdbcConnectionClass(runtime, jdbcConnection);
53
+ InformixRubyJdbcConnection.createInformixJdbcConnectionClass(runtime, jdbcConnection);
54
+ OracleRubyJdbcConnection.createOracleJdbcConnectionClass(runtime, jdbcConnection);
51
55
  Sqlite3RubyJdbcConnection.createSqlite3JdbcConnectionClass(runtime, jdbcConnection);
52
56
  RubyModule arJdbc = runtime.getOrCreateModule("ArJdbc");
53
57
 
@@ -123,21 +123,32 @@ public class RubyJdbcConnection extends RubyObject {
123
123
  String table_name = rubyApi.convertToRubyString(args[0]).getUnicodeValue();
124
124
  String schemaName = null;
125
125
 
126
- int index = table_name.indexOf(".");
127
- if(index != -1) {
128
- schemaName = table_name.substring(0, index);
129
- table_name = table_name.substring(index + 1);
126
+ final String[] name_parts = table_name.split( "\\." );
127
+ if ( name_parts.length > 3 ) {
128
+ throw new SQLException("Table name '" + table_name + "' should not contain more than 2 '.'");
130
129
  }
131
130
 
132
131
  DatabaseMetaData metadata = c.getMetaData();
132
+ String clzName = metadata.getClass().getName().toLowerCase();
133
+ boolean isDB2 = clzName.indexOf("db2") != -1 || clzName.indexOf("as400") != -1;
134
+
135
+ String catalog = c.getCatalog();
136
+ if( name_parts.length == 2 ) {
137
+ schemaName = name_parts[0];
138
+ table_name = name_parts[1];
139
+ }
140
+ else if ( name_parts.length == 3 ) {
141
+ catalog = name_parts[0];
142
+ schemaName = name_parts[1];
143
+ table_name = name_parts[2];
144
+ }
133
145
 
134
146
  if(args.length > 2 && schemaName == null) schemaName = toStringOrNull(args[2]);
135
147
 
136
148
  if (schemaName != null) schemaName = caseConvertIdentifierForJdbc(metadata, schemaName);
137
149
  table_name = caseConvertIdentifierForJdbc(metadata, table_name);
138
150
 
139
- String catalog = c.getCatalog();
140
- if (schemaName != null) { catalog = schemaName; }
151
+ if (schemaName != null && !isDB2 && !databaseSupportsSchemas()) { catalog = schemaName; }
141
152
 
142
153
  String[] tableTypes = new String[]{"TABLE","VIEW","SYNONYM"};
143
154
  RubyArray matchingTables = (RubyArray) tableLookupBlock(context.getRuntime(),
@@ -645,10 +656,11 @@ public class RubyJdbcConnection extends RubyObject {
645
656
  public static String caseConvertIdentifierForJdbc(DatabaseMetaData metadata, String value)
646
657
  throws SQLException {
647
658
  if (value == null) return null;
659
+ boolean isPostgres = metadata.getDatabaseProductName().equals("PostgreSQL");
648
660
 
649
661
  if (metadata.storesUpperCaseIdentifiers()) {
650
662
  return value.toUpperCase();
651
- } else if (metadata.storesLowerCaseIdentifiers()) {
663
+ } else if (metadata.storesLowerCaseIdentifiers() && ! isPostgres) {
652
664
  return value.toLowerCase();
653
665
  }
654
666
 
@@ -839,8 +851,9 @@ public class RubyJdbcConnection extends RubyObject {
839
851
  RubyHash row = RubyHash.newHash(runtime);
840
852
 
841
853
  for (int i = 0; i < columnCount; i++) {
842
- row.op_aset(context, columns[i].name, jdbcToRuby(runtime, i + 1, columns[i].type, resultSet));
854
+ row.op_aset(context, columns[i].name, jdbcToRuby(runtime, columns[i].index, columns[i].type, resultSet));
843
855
  }
856
+
844
857
  results.add(row);
845
858
  }
846
859
  }
@@ -970,10 +983,12 @@ public class RubyJdbcConnection extends RubyObject {
970
983
  DatabaseMetaData metadata = c.getMetaData();
971
984
  String clzName = metadata.getClass().getName().toLowerCase();
972
985
  boolean isOracle = clzName.indexOf("oracle") != -1 || clzName.indexOf("oci") != -1;
986
+ boolean isDerby = clzName.indexOf("derby") != 1;
973
987
 
974
988
  String realschema = schemapat;
975
989
  String realtablepat = tablepat;
976
990
 
991
+ if (isDerby && realschema != null && realschema.equals("")) realschema = null; // Derby doesn't like empty-string schema name
977
992
  if (realtablepat != null) realtablepat = caseConvertIdentifierForJdbc(metadata, realtablepat);
978
993
  if (realschema != null) realschema = caseConvertIdentifierForJdbc(metadata, realschema);
979
994
 
@@ -1015,15 +1030,15 @@ public class RubyJdbcConnection extends RubyObject {
1015
1030
  return RubyString.newUnicodeString(runtime, str);
1016
1031
  }
1017
1032
 
1018
- private static final int COLUMN_NAME = 4;
1019
- private static final int DATA_TYPE = 5;
1020
- private static final int TYPE_NAME = 6;
1021
- private static final int COLUMN_SIZE = 7;
1022
- private static final int DECIMAL_DIGITS = 9;
1023
- private static final int COLUMN_DEF = 13;
1024
- private static final int IS_NULLABLE = 18;
1033
+ protected static final int COLUMN_NAME = 4;
1034
+ protected static final int DATA_TYPE = 5;
1035
+ protected static final int TYPE_NAME = 6;
1036
+ protected static final int COLUMN_SIZE = 7;
1037
+ protected static final int DECIMAL_DIGITS = 9;
1038
+ protected static final int COLUMN_DEF = 13;
1039
+ protected static final int IS_NULLABLE = 18;
1025
1040
 
1026
- private int intFromResultSet(ResultSet resultSet, int column) throws SQLException {
1041
+ protected int intFromResultSet(ResultSet resultSet, int column) throws SQLException {
1027
1042
  int precision = resultSet.getInt(column);
1028
1043
 
1029
1044
  return precision == 0 && resultSet.wasNull() ? -1 : precision;
@@ -1032,20 +1047,11 @@ public class RubyJdbcConnection extends RubyObject {
1032
1047
  /**
1033
1048
  * Create a string which represents a sql type usable by Rails from the resultSet column
1034
1049
  * metadata object.
1035
- *
1036
- * @param numberAsBoolean the database uses decimal as a boolean data type
1037
- * because it does not support optional SQL92 type or mandatory SQL99
1038
- * booleans.
1039
1050
  */
1040
- private String typeFromResultSet(ResultSet resultSet, boolean numberAsBoolean) throws SQLException {
1051
+ protected String typeFromResultSet(ResultSet resultSet) throws SQLException {
1041
1052
  int precision = intFromResultSet(resultSet, COLUMN_SIZE);
1042
1053
  int scale = intFromResultSet(resultSet, DECIMAL_DIGITS);
1043
1054
 
1044
- // Assume db's which use decimal for boolean will not also specify a
1045
- // valid precision 1 decimal also. Seems sketchy to me...
1046
- if (numberAsBoolean && precision != 1 &&
1047
- resultSet.getInt(DATA_TYPE) == java.sql.Types.DECIMAL) precision = -1;
1048
-
1049
1055
  String type = resultSet.getString(TYPE_NAME);
1050
1056
  if (precision > 0) {
1051
1057
  type += "(" + precision;
@@ -1070,7 +1076,6 @@ public class RubyJdbcConnection extends RubyObject {
1070
1076
  List columns = new ArrayList();
1071
1077
  List pkeyNames = new ArrayList();
1072
1078
  String clzName = metadata.getClass().getName().toLowerCase();
1073
- boolean isOracle = clzName.indexOf("oracle") != -1 || clzName.indexOf("oci") != -1;
1074
1079
 
1075
1080
  RubyHash types = (RubyHash) native_database_types();
1076
1081
  IRubyObject jdbcCol = getJdbcColumnClass(context);
@@ -1087,7 +1092,7 @@ public class RubyJdbcConnection extends RubyObject {
1087
1092
  RubyString.newUnicodeString(runtime,
1088
1093
  caseConvertIdentifierForRails(metadata, colName)),
1089
1094
  defaultValueFromResultSet(runtime, rs),
1090
- RubyString.newUnicodeString(runtime, typeFromResultSet(rs, isOracle)),
1095
+ RubyString.newUnicodeString(runtime, typeFromResultSet(rs)),
1091
1096
  runtime.newBoolean(!rs.getString(IS_NULLABLE).trim().equals("NO"))
1092
1097
  });
1093
1098
  columns.add(column);
@@ -1192,6 +1197,14 @@ public class RubyJdbcConnection extends RubyObject {
1192
1197
  return Java.java_to_ruby(this, JavaObject.wrap(getRuntime(), c), Block.NULL_BLOCK);
1193
1198
  }
1194
1199
 
1200
+ /**
1201
+ * Some databases support schemas and others do not.
1202
+ * For ones which do this method should return true, aiding in decisions regarding schema vs database determination.
1203
+ */
1204
+ protected boolean databaseSupportsSchemas() {
1205
+ return false;
1206
+ }
1207
+
1195
1208
  private static int whitespace(int start, ByteList bl) {
1196
1209
  int end = bl.begin + bl.realSize;
1197
1210
 
@@ -1222,11 +1235,13 @@ public class RubyJdbcConnection extends RubyObject {
1222
1235
 
1223
1236
  public static class ColumnData {
1224
1237
  public IRubyObject name;
1238
+ public int index;
1225
1239
  public int type;
1226
1240
 
1227
- public ColumnData(IRubyObject name, int type) {
1241
+ public ColumnData(IRubyObject name, int type, int idx) {
1228
1242
  this.name = name;
1229
1243
  this.type = type;
1244
+ this.index = idx;
1230
1245
  }
1231
1246
 
1232
1247
  public static ColumnData[] setup(Ruby runtime, DatabaseMetaData databaseMetadata,
@@ -1242,7 +1257,7 @@ public class RubyJdbcConnection extends RubyObject {
1242
1257
  name = RubyJdbcConnection.caseConvertIdentifierForRails(databaseMetadata, metadata.getColumnLabel(i));
1243
1258
  }
1244
1259
 
1245
- columns[i - 1] = new ColumnData(RubyString.newUnicodeString(runtime, name), metadata.getColumnType(i));
1260
+ columns[i - 1] = new ColumnData(RubyString.newUnicodeString(runtime, name), metadata.getColumnType(i), i);
1246
1261
  }
1247
1262
 
1248
1263
  return columns;
@@ -28,12 +28,16 @@ package arjdbc.mssql;
28
28
  import java.sql.ResultSet;
29
29
  import java.sql.SQLException;
30
30
  import java.sql.Types;
31
+ import java.util.List;
31
32
 
32
33
  import arjdbc.jdbc.RubyJdbcConnection;
34
+ import static arjdbc.jdbc.RubyJdbcConnection.ColumnData;
33
35
 
34
36
  import org.jruby.Ruby;
35
37
  import org.jruby.RubyClass;
38
+ import org.jruby.RubyString;
36
39
  import org.jruby.runtime.ObjectAllocator;
40
+ import org.jruby.runtime.ThreadContext;
37
41
  import org.jruby.runtime.builtin.IRubyObject;
38
42
 
39
43
  /**
@@ -42,8 +46,11 @@ import org.jruby.runtime.builtin.IRubyObject;
42
46
  */
43
47
  public class MssqlRubyJdbcConnection extends RubyJdbcConnection {
44
48
 
49
+ private RubyString _row_num;
50
+
45
51
  protected MssqlRubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
46
52
  super(runtime, metaClass);
53
+ _row_num = runtime.newString("_row_num");
47
54
  }
48
55
 
49
56
  public static RubyClass createMssqlJdbcConnectionClass(Ruby runtime, RubyClass jdbcConnection) {
@@ -53,22 +60,68 @@ public class MssqlRubyJdbcConnection extends RubyJdbcConnection {
53
60
 
54
61
  return clazz;
55
62
  }
56
- private static ObjectAllocator MSSQL_JDBCCONNECTION_ALLOCATOR = new ObjectAllocator() {
57
63
 
64
+ private static ObjectAllocator MSSQL_JDBCCONNECTION_ALLOCATOR = new ObjectAllocator() {
58
65
  public IRubyObject allocate(Ruby runtime, RubyClass klass) {
59
66
  return new MssqlRubyJdbcConnection(runtime, klass);
60
67
  }
61
68
  };
62
69
 
70
+ protected static IRubyObject booleanToRuby(Ruby runtime, ResultSet resultSet, boolean booleanValue)
71
+ throws SQLException {
72
+ if (booleanValue == false && resultSet.wasNull()) return runtime.getNil();
73
+ return runtime.newBoolean(booleanValue);
74
+ }
75
+
63
76
  /**
64
77
  * Treat LONGVARCHAR as CLOB on Mssql for purposes of converting a JDBC value to Ruby.
78
+ * Treat BOOLEAN/BIT as Boolean, rather than the default behaviour of conversion to string
65
79
  */
66
80
  @Override
67
81
  protected IRubyObject jdbcToRuby(Ruby runtime, int column, int type, ResultSet resultSet)
68
82
  throws SQLException {
83
+ if ( Types.BOOLEAN == type || Types.BIT == type ) {
84
+ return booleanToRuby(runtime, resultSet, resultSet.getBoolean(column));
85
+ }
69
86
  if (type == Types.LONGVARCHAR) {
70
87
  type = Types.CLOB;
71
88
  }
72
89
  return super.jdbcToRuby(runtime, column, type, resultSet);
73
90
  }
91
+
92
+ /**
93
+ * Microsoft SQL 2000+ support schemas
94
+ */
95
+ @Override
96
+ protected boolean databaseSupportsSchemas() {
97
+ return true;
98
+ }
99
+
100
+ @Override
101
+ protected void populateFromResultSet(ThreadContext context, Ruby runtime, List results,
102
+ ResultSet resultSet, ColumnData[] columns) throws SQLException {
103
+ super.populateFromResultSet(context, runtime, results, resultSet, filterRowNumFromColumns(columns));
104
+ }
105
+
106
+ /**
107
+ * Filter out the <tt>_row_num</tt> column from results.
108
+ */
109
+ private ColumnData[] filterRowNumFromColumns(ColumnData[] columns) {
110
+ for (int i = 0; i < columns.length; i++) {
111
+ if (columns[i].name.equals(_row_num)) {
112
+ ColumnData[] filtered = new ColumnData[columns.length - 1];
113
+ if (i > 0) {
114
+ System.arraycopy(columns, 0, filtered, 0, i);
115
+ }
116
+
117
+ if (i + 1 < columns.length) {
118
+ System.arraycopy(columns, i + 1, filtered, i, columns.length - (i + 1));
119
+ }
120
+
121
+ return filtered;
122
+ }
123
+ }
124
+
125
+ return columns;
126
+ }
74
127
  }