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

Sign up to get free protection for your applications and to get access to all the features.
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
  }