activerecord-jdbc-adapter 1.2.5 → 1.2.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (155) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +5 -1
  3. data/Appraisals +5 -5
  4. data/Gemfile +9 -1
  5. data/Gemfile.lock +44 -10
  6. data/History.txt +126 -2
  7. data/README.md +246 -0
  8. data/Rakefile +34 -25
  9. data/activerecord-jdbc-adapter.gemspec +1 -1
  10. data/gemfiles/rails23.gemfile +5 -3
  11. data/gemfiles/rails23.gemfile.lock +26 -18
  12. data/gemfiles/rails30.gemfile +4 -2
  13. data/gemfiles/rails30.gemfile.lock +16 -8
  14. data/gemfiles/rails31.gemfile +4 -2
  15. data/gemfiles/rails31.gemfile.lock +16 -9
  16. data/gemfiles/rails32.gemfile +4 -2
  17. data/gemfiles/rails32.gemfile.lock +15 -8
  18. data/lib/active_record/connection_adapters/db2_adapter.rb +1 -0
  19. data/lib/arel/visitors/sql_server.rb +3 -0
  20. data/lib/arjdbc.rb +3 -5
  21. data/lib/arjdbc/db2.rb +1 -0
  22. data/lib/arjdbc/db2/adapter.rb +302 -196
  23. data/lib/arjdbc/db2/connection_methods.rb +18 -0
  24. data/lib/arjdbc/derby/active_record_patch.rb +12 -0
  25. data/lib/arjdbc/derby/adapter.rb +180 -158
  26. data/lib/arjdbc/derby/connection_methods.rb +5 -1
  27. data/lib/arjdbc/firebird/adapter.rb +27 -19
  28. data/lib/arjdbc/h2/adapter.rb +162 -7
  29. data/lib/arjdbc/h2/connection_methods.rb +5 -1
  30. data/lib/arjdbc/hsqldb.rb +1 -1
  31. data/lib/arjdbc/hsqldb/adapter.rb +96 -61
  32. data/lib/arjdbc/hsqldb/connection_methods.rb +5 -1
  33. data/lib/arjdbc/hsqldb/explain_support.rb +35 -0
  34. data/lib/arjdbc/informix/adapter.rb +56 -55
  35. data/lib/arjdbc/jdbc/adapter.rb +173 -86
  36. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  37. data/lib/arjdbc/jdbc/column.rb +28 -23
  38. data/lib/arjdbc/jdbc/connection.rb +10 -6
  39. data/lib/arjdbc/jdbc/driver.rb +13 -5
  40. data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +21 -0
  41. data/lib/arjdbc/mssql.rb +1 -1
  42. data/lib/arjdbc/mssql/adapter.rb +51 -53
  43. data/lib/arjdbc/mssql/connection_methods.rb +8 -1
  44. data/lib/arjdbc/mysql.rb +1 -1
  45. data/lib/arjdbc/mysql/adapter.rb +186 -150
  46. data/lib/arjdbc/mysql/connection_methods.rb +9 -9
  47. data/lib/arjdbc/mysql/explain_support.rb +85 -0
  48. data/lib/arjdbc/oracle.rb +1 -1
  49. data/lib/arjdbc/oracle/adapter.rb +232 -125
  50. data/lib/arjdbc/oracle/connection_methods.rb +2 -2
  51. data/lib/arjdbc/postgresql.rb +1 -1
  52. data/lib/arjdbc/postgresql/adapter.rb +134 -86
  53. data/lib/arjdbc/postgresql/connection_methods.rb +6 -4
  54. data/lib/arjdbc/postgresql/explain_support.rb +55 -0
  55. data/lib/arjdbc/sqlite3.rb +1 -1
  56. data/lib/arjdbc/sqlite3/adapter.rb +176 -108
  57. data/lib/arjdbc/sqlite3/connection_methods.rb +5 -5
  58. data/lib/arjdbc/sqlite3/explain_support.rb +32 -0
  59. data/lib/arjdbc/sybase/adapter.rb +7 -6
  60. data/lib/arjdbc/version.rb +1 -1
  61. data/pom.xml +1 -1
  62. data/rakelib/02-test.rake +9 -11
  63. data/rakelib/rails.rake +18 -10
  64. data/src/java/arjdbc/db2/DB2Module.java +70 -0
  65. data/src/java/arjdbc/derby/DerbyModule.java +24 -5
  66. data/src/java/arjdbc/hsqldb/HSQLDBModule.java +66 -0
  67. data/src/java/arjdbc/jdbc/AdapterJavaService.java +14 -7
  68. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +111 -89
  69. data/src/java/arjdbc/mysql/MySQLModule.java +79 -70
  70. data/src/java/arjdbc/oracle/OracleModule.java +74 -0
  71. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +5 -10
  72. data/src/java/arjdbc/sqlite3/SQLite3Module.java +77 -0
  73. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +127 -0
  74. data/src/java/arjdbc/sqlite3/Sqlite3RubyJdbcConnection.java +25 -111
  75. data/src/java/arjdbc/util/QuotingUtils.java +104 -0
  76. data/test/abstract_db_create.rb +6 -6
  77. data/test/activerecord/connection_adapters/type_conversion_test.rb +2 -2
  78. data/test/assets/flowers.jpg +0 -0
  79. data/test/binary.rb +67 -0
  80. data/test/db/db2.rb +30 -7
  81. data/test/db/jdbc.rb +4 -2
  82. data/test/db/oracle.rb +18 -27
  83. data/test/db2_binary_test.rb +6 -0
  84. data/test/db2_serialize_test.rb +6 -0
  85. data/test/db2_simple_test.rb +20 -25
  86. data/test/db2_test.rb +71 -0
  87. data/test/derby_binary_test.rb +6 -0
  88. data/test/derby_migration_test.rb +42 -35
  89. data/test/derby_reset_column_information_test.rb +1 -0
  90. data/test/derby_row_locking_test.rb +17 -0
  91. data/test/derby_schema_dump_test.rb +9 -0
  92. data/test/derby_serialize_test.rb +6 -0
  93. data/test/derby_simple_test.rb +59 -17
  94. data/test/generic_jdbc_connection_test.rb +112 -5
  95. data/test/h2_binary_test.rb +6 -0
  96. data/test/h2_change_column_test.rb +1 -1
  97. data/test/h2_schema_dump_test.rb +25 -0
  98. data/test/h2_serialize_test.rb +6 -0
  99. data/test/h2_simple_test.rb +23 -9
  100. data/test/has_many_through.rb +18 -4
  101. data/test/hsqldb_binary_test.rb +6 -0
  102. data/test/hsqldb_schema_dump_test.rb +15 -0
  103. data/test/hsqldb_serialize_test.rb +6 -0
  104. data/test/hsqldb_simple_test.rb +1 -0
  105. data/test/informix_simple_test.rb +1 -1
  106. data/test/jdbc/db2.rb +23 -0
  107. data/test/jdbc/oracle.rb +23 -0
  108. data/test/jdbc_common.rb +3 -110
  109. data/test/jndi_callbacks_test.rb +0 -2
  110. data/test/jndi_test.rb +2 -0
  111. data/test/models/binary.rb +18 -0
  112. data/test/models/custom_pk_name.rb +1 -0
  113. data/test/models/data_types.rb +11 -2
  114. data/test/models/entry.rb +1 -1
  115. data/test/models/string_id.rb +2 -2
  116. data/test/models/thing.rb +1 -1
  117. data/test/models/topic.rb +32 -0
  118. data/test/mssql_legacy_types_test.rb +1 -1
  119. data/test/mssql_limit_offset_test.rb +13 -3
  120. data/test/mssql_serialize_test.rb +6 -0
  121. data/test/mysql_binary_test.rb +6 -0
  122. data/test/mysql_schema_dump_test.rb +220 -0
  123. data/test/mysql_serialize_test.rb +6 -0
  124. data/test/mysql_simple_test.rb +22 -2
  125. data/test/mysql_test.rb +93 -0
  126. data/test/oracle_binary_test.rb +6 -0
  127. data/test/oracle_limit_test.rb +2 -1
  128. data/test/oracle_serialize_test.rb +6 -0
  129. data/test/oracle_simple_test.rb +61 -0
  130. data/test/oracle_specific_test.rb +77 -26
  131. data/test/postgres_binary_test.rb +6 -0
  132. data/test/postgres_native_type_mapping_test.rb +12 -11
  133. data/test/postgres_nonseq_pkey_test.rb +1 -0
  134. data/test/postgres_reserved_test.rb +1 -0
  135. data/test/postgres_reset_column_information_test.rb +1 -0
  136. data/test/postgres_row_locking_test.rb +21 -0
  137. data/test/postgres_schema_dump_test.rb +88 -0
  138. data/test/postgres_schema_search_path_test.rb +1 -0
  139. data/test/postgres_simple_test.rb +62 -89
  140. data/test/postgres_table_alias_length_test.rb +1 -0
  141. data/test/postgres_test.rb +31 -0
  142. data/test/postgres_type_conversion_test.rb +16 -16
  143. data/test/row_locking.rb +69 -64
  144. data/test/schema_dump.rb +168 -0
  145. data/test/serialize.rb +277 -0
  146. data/test/simple.rb +326 -122
  147. data/test/sqlite3_serialize_test.rb +6 -0
  148. data/test/sqlite3_simple_test.rb +51 -84
  149. data/test/sqlite3_type_conversion_test.rb +101 -0
  150. data/test/test_helper.rb +224 -0
  151. metadata +325 -366
  152. data/README.rdoc +0 -214
  153. data/test/db/logger.rb +0 -3
  154. data/test/derby_multibyte_test.rb +0 -11
  155. data/test/mysql_info_test.rb +0 -123
@@ -24,110 +24,119 @@
24
24
 
25
25
  package arjdbc.mysql;
26
26
 
27
+ import static arjdbc.util.QuotingUtils.BYTES_0;
28
+ import static arjdbc.util.QuotingUtils.BYTES_1;
29
+
27
30
  import java.sql.Connection;
28
31
 
32
+ import org.jcodings.specific.UTF8Encoding;
33
+
34
+ import org.jruby.Ruby;
29
35
  import org.jruby.RubyModule;
30
36
  import org.jruby.RubyString;
31
-
32
37
  import org.jruby.anno.JRubyMethod;
33
38
  import org.jruby.runtime.ThreadContext;
34
39
  import org.jruby.runtime.builtin.IRubyObject;
35
-
36
40
  import org.jruby.util.ByteList;
37
41
 
38
42
  public class MySQLModule {
43
+
39
44
  public static void load(RubyModule arJdbc) {
40
- RubyModule mysql = arJdbc.defineModuleUnder("MySQL");
41
- mysql.defineAnnotatedMethods(MySQLModule.class);
45
+ RubyModule mySQL = arJdbc.defineModuleUnder("MySQL");
46
+ mySQL.defineAnnotatedMethods(MySQLModule.class);
42
47
  }
43
48
 
44
- private final static byte BACKQUOTE = '`';
45
- private final static byte[] QUOTED_DOT = new byte[] {'`', '.', '`'};
46
-
47
- private final static byte[] ZERO = new byte[] {'\\','0'};
48
- private final static byte[] NEWLINE = new byte[] {'\\','n'};
49
- private final static byte[] CARRIAGE = new byte[] {'\\','r'};
50
- private final static byte[] ZED = new byte[] {'\\','Z'};
51
- private final static byte[] DBL = new byte[] {'\\','"'};
52
- private final static byte[] SINGLE = new byte[] {'\\','\''};
53
- private final static byte[] ESCAPE = new byte[] {'\\','\\'};
54
-
55
- @JRubyMethod(name = "quote_string", required = 1, frame=false)
56
- public static IRubyObject quote_string(ThreadContext context, IRubyObject recv, IRubyObject string) {
57
- ByteList bytes = ((RubyString) string).getByteList();
58
- ByteList newBytes = new ByteList();
59
-
60
- newBytes.append(bytes);
61
-
62
- for(int i = newBytes.begin; i < newBytes.begin + newBytes.realSize; i++) {
63
- byte[] rep = null;
64
- switch (newBytes.bytes[i]) {
65
- case 0: rep = ZERO; break;
66
- case '\n': rep = NEWLINE; break;
67
- case '\r': rep = CARRIAGE; break;
68
- case 26: rep = ZED; break;
69
- case '"': rep = DBL; break;
70
- case '\'': rep = SINGLE; break;
71
- case '\\': rep = ESCAPE; break;
49
+ //private final static byte[] ZERO = new byte[] {'\\','0'};
50
+ //private final static byte[] NEWLINE = new byte[] {'\\','n'};
51
+ //private final static byte[] CARRIAGE = new byte[] {'\\','r'};
52
+ //private final static byte[] ZED = new byte[] {'\\','Z'};
53
+ //private final static byte[] DBL = new byte[] {'\\','"'};
54
+ //private final static byte[] SINGLE = new byte[] {'\\','\''};
55
+ //private final static byte[] ESCAPE = new byte[] {'\\','\\'};
56
+
57
+ private static final int STRING_QUOTES_OPTIMISTIC_QUESS = 24;
58
+
59
+ @JRubyMethod(name = "quote_string", required = 1, frame = false)
60
+ public static IRubyObject quote_string(final ThreadContext context,
61
+ final IRubyObject recv, final IRubyObject string) {
62
+
63
+ final ByteList stringBytes = ((RubyString) string).getByteList();
64
+ final byte[] bytes = stringBytes.bytes; // unsafeBytes();
65
+ final int begin = stringBytes.begin; // getBegin();
66
+ final int realSize = stringBytes.realSize; // getRealSize();
67
+
68
+ ByteList quotedBytes = null; int appendFrom = begin;
69
+ for ( int i = begin; i < begin + realSize; i++ ) {
70
+ final byte byte2;
71
+ switch ( bytes[i] ) {
72
+ case 0 : byte2 = '0'; break;
73
+ case '\n' : byte2 = 'n'; break;
74
+ case '\r' : byte2 = 'r'; break;
75
+ case 26 : byte2 = 'Z'; break;
76
+ case '"' : byte2 = '"'; break;
77
+ case '\'' : byte2 = '\''; break;
78
+ case '\\' : byte2 = '\\'; break;
79
+ default : byte2 = 0;
72
80
  }
73
-
74
- if (rep != null) {
75
- newBytes.replace(i, 1, rep);
76
- i += rep.length - 1; // We subtract one since for loop already adds one
81
+ if ( byte2 != 0 ) {
82
+ if ( quotedBytes == null ) {
83
+ quotedBytes = new ByteList(
84
+ new byte[realSize + STRING_QUOTES_OPTIMISTIC_QUESS],
85
+ stringBytes.encoding // getEncoding()
86
+ );
87
+ quotedBytes.begin = 0; // setBegin(0);
88
+ quotedBytes.realSize = 0; // setRealSize(0);
89
+ } // copy string on-first quote we "optimize" for non-quoted
90
+ quotedBytes.append(bytes, appendFrom, i - appendFrom);
91
+ quotedBytes.append('\\').append(byte2);
92
+ appendFrom = i + 1;
77
93
  }
78
94
  }
95
+ if ( quotedBytes != null ) { // append what's left in the end :
96
+ quotedBytes.append(bytes, appendFrom, begin + realSize - appendFrom);
97
+ }
98
+ else return string; // nothing changed, can return original
79
99
 
80
- // Nothing changed, can return original
81
- if (newBytes.length() == bytes.length()) return string;
82
-
83
- return context.getRuntime().newString(newBytes);
100
+ final Ruby runtime = context.getRuntime();
101
+ final RubyString quoted = runtime.newString(quotedBytes);
102
+ if ( runtime.is1_9() ) { // only due mysql2 compatibility
103
+ quoted.associateEncoding( UTF8Encoding.INSTANCE );
104
+ }
105
+ return quoted;
84
106
  }
85
107
 
86
- @JRubyMethod(name = "quote_column_name", frame=false)
87
- public static IRubyObject quote_column_name(ThreadContext context, IRubyObject recv, IRubyObject arg) {
88
- ByteList bytes = arg.asString().getByteList();
89
- ByteList newBytes = new ByteList();
90
-
91
- newBytes.insert(0, BACKQUOTE);
92
- newBytes.append(bytes);
93
- newBytes.append(BACKQUOTE);
94
-
95
- return context.getRuntime().newString(newBytes);
108
+ @JRubyMethod(name = "quoted_true", required = 0, frame = false)
109
+ public static IRubyObject quoted_true(
110
+ final ThreadContext context,
111
+ final IRubyObject self) {
112
+ return RubyString.newString(context.getRuntime(), BYTES_1);
96
113
  }
97
-
98
- @JRubyMethod(name = "quote_table_name", frame=false)
99
- public static IRubyObject quote_table_name(ThreadContext context, IRubyObject recv, IRubyObject arg) {
100
- ByteList bytes = arg.asString().getByteList();
101
- ByteList newBytes = new ByteList();
102
-
103
- newBytes.insert(0, BACKQUOTE);
104
- newBytes.append(bytes);
105
- int i = 0, j = 0;
106
- while ((i = newBytes.indexOf('.', j)) != -1) {
107
- newBytes.replace(i, 1, QUOTED_DOT);
108
- j = i+3;
109
- }
110
- newBytes.append(BACKQUOTE);
111
-
112
- return context.getRuntime().newString(newBytes);
114
+
115
+ @JRubyMethod(name = "quoted_false", required = 0, frame = false)
116
+ public static IRubyObject quoted_false(
117
+ final ThreadContext context,
118
+ final IRubyObject self) {
119
+ return RubyString.newString(context.getRuntime(), BYTES_0);
113
120
  }
114
-
121
+
115
122
  /**
116
123
  * HACK HACK HACK See http://bugs.mysql.com/bug.php?id=36565
117
124
  * MySQL's statement cancel timer can cause memory leaks, so cancel it
118
125
  * if we loaded MySQL classes from the same classloader as JRuby
119
126
  */
120
127
  @JRubyMethod(module = true, frame = false)
121
- public static IRubyObject kill_cancel_timer(ThreadContext context, IRubyObject recv, IRubyObject raw_connection) {
122
- Connection conn = (Connection) raw_connection.dataGetStruct();
128
+ public static IRubyObject kill_cancel_timer(final ThreadContext context,
129
+ final IRubyObject recv, final IRubyObject raw_connection) {
130
+
131
+ final Connection conn = (Connection) raw_connection.dataGetStruct();
123
132
  if (conn != null && conn.getClass().getClassLoader() == recv.getRuntime().getJRubyClassLoader()) {
124
133
  try {
125
134
  java.lang.reflect.Field f = conn.getClass().getDeclaredField("cancelTimer");
126
135
  f.setAccessible(true);
127
136
  java.util.Timer timer = (java.util.Timer) f.get(null);
128
137
  timer.cancel();
129
- } catch (Exception e) {
130
138
  }
139
+ catch (Exception e) { /* ignored */ }
131
140
  }
132
141
  return recv.getRuntime().getNil();
133
142
  }
@@ -0,0 +1,74 @@
1
+ /*
2
+ * The MIT License
3
+ *
4
+ * Copyright 2013 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.oracle;
25
+
26
+ import static arjdbc.util.QuotingUtils.BYTES_0;
27
+ import static arjdbc.util.QuotingUtils.BYTES_1;
28
+ import static arjdbc.util.QuotingUtils.quoteCharWith;
29
+
30
+ import org.jruby.RubyModule;
31
+ import org.jruby.RubyString;
32
+ import org.jruby.anno.JRubyMethod;
33
+ import org.jruby.runtime.ThreadContext;
34
+ import org.jruby.runtime.builtin.IRubyObject;
35
+
36
+ /**
37
+ * ArJdbc::Oracle
38
+ *
39
+ * @author kares
40
+ */
41
+ public class OracleModule {
42
+
43
+ public static void load(final RubyModule arJdbc) {
44
+ RubyModule oracle = arJdbc.defineModuleUnder("Oracle");
45
+ oracle.defineAnnotatedMethods( OracleModule.class );
46
+ }
47
+
48
+ @JRubyMethod(name = "quote_string", required = 1, frame = false)
49
+ public static IRubyObject quote_string(
50
+ final ThreadContext context,
51
+ final IRubyObject self,
52
+ final IRubyObject string) { // string.gsub("'", "''") :
53
+ final char single = '\'';
54
+ final RubyString quoted = quoteCharWith(
55
+ context, (RubyString) string, single, single
56
+ );
57
+ return quoted;
58
+ }
59
+
60
+ @JRubyMethod(name = "quoted_true", required = 0, frame = false)
61
+ public static IRubyObject quoted_true(
62
+ final ThreadContext context,
63
+ final IRubyObject self) {
64
+ return RubyString.newString(context.getRuntime(), BYTES_1);
65
+ }
66
+
67
+ @JRubyMethod(name = "quoted_false", required = 0, frame = false)
68
+ public static IRubyObject quoted_false(
69
+ final ThreadContext context,
70
+ final IRubyObject self) {
71
+ return RubyString.newString(context.getRuntime(), BYTES_0);
72
+ }
73
+
74
+ }
@@ -63,23 +63,18 @@ public class OracleRubyJdbcConnection extends RubyJdbcConnection {
63
63
  * from NUMBER(x) or NUMBER(x,y).
64
64
  */
65
65
  @Override
66
- protected String typeFromResultSet(ResultSet resultSet) throws SQLException {
66
+ protected String typeFromResultSet(final ResultSet resultSet) throws SQLException {
67
67
  int precision = intFromResultSet(resultSet, COLUMN_SIZE);
68
68
  int scale = intFromResultSet(resultSet, DECIMAL_DIGITS);
69
69
 
70
70
  // According to http://forums.oracle.com/forums/thread.jspa?threadID=658646
71
71
  // Unadorned NUMBER reports scale == null, so we look for that here.
72
- if (scale < 0 && resultSet.getInt(DATA_TYPE) == java.sql.Types.DECIMAL) {
72
+ if ( scale < 0 && resultSet.getInt(DATA_TYPE) == java.sql.Types.DECIMAL ) {
73
73
  precision = -1;
74
74
  }
75
75
 
76
- String type = resultSet.getString(TYPE_NAME);
77
- if (precision > 0) {
78
- type += "(" + precision;
79
- if(scale > 0) type += "," + scale;
80
- type += ")";
81
- }
82
-
83
- return type;
76
+ final String type = resultSet.getString(TYPE_NAME);
77
+ return formatTypeWithPrecisionAndScale(type, precision, scale);
84
78
  }
79
+
85
80
  }
@@ -0,0 +1,77 @@
1
+ /*
2
+ * The MIT License
3
+ *
4
+ * Copyright 2013 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.sqlite3;
25
+
26
+ import static arjdbc.util.QuotingUtils.quoteCharWith;
27
+
28
+ import org.jruby.RubyModule;
29
+ import org.jruby.RubyString;
30
+ import org.jruby.anno.JRubyMethod;
31
+ import org.jruby.runtime.ThreadContext;
32
+ import org.jruby.runtime.builtin.IRubyObject;
33
+ import org.jruby.util.ByteList;
34
+
35
+ /**
36
+ * ArJdbc::SQLite3
37
+ *
38
+ * @author kares
39
+ */
40
+ public class SQLite3Module {
41
+
42
+ public static void load(final RubyModule arJdbc) {
43
+ RubyModule sqlite3 = arJdbc.defineModuleUnder("SQLite3");
44
+ sqlite3.defineAnnotatedMethods( SQLite3Module.class );
45
+ }
46
+
47
+ @JRubyMethod(name = "quote_string", required = 1, frame = false)
48
+ public static IRubyObject quote_string(
49
+ final ThreadContext context,
50
+ final IRubyObject self,
51
+ final IRubyObject string) { // string.gsub("'", "''") :
52
+ final char single = '\'';
53
+ final RubyString quoted = quoteCharWith(
54
+ context, (RubyString) string, single, single
55
+ );
56
+ return quoted;
57
+ }
58
+
59
+ private static final ByteList Q_TRUE = new ByteList(new byte[] { '\'', 't', '\'' }, false);
60
+
61
+ @JRubyMethod(name = "quoted_true", required = 0, frame = false)
62
+ public static IRubyObject quoted_true(
63
+ final ThreadContext context,
64
+ final IRubyObject self) {
65
+ return RubyString.newString(context.getRuntime(), Q_TRUE);
66
+ }
67
+
68
+ private static final ByteList Q_FALSE = new ByteList(new byte[] { '\'', 'f', '\'' }, false);
69
+
70
+ @JRubyMethod(name = "quoted_false", required = 0, frame = false)
71
+ public static IRubyObject quoted_false(
72
+ final ThreadContext context,
73
+ final IRubyObject self) {
74
+ return RubyString.newString(context.getRuntime(), Q_FALSE);
75
+ }
76
+
77
+ }
@@ -0,0 +1,127 @@
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.sqlite3;
28
+
29
+ import java.io.ByteArrayInputStream;
30
+ import java.io.IOException;
31
+ import java.sql.Connection;
32
+ import java.sql.ResultSet;
33
+ import java.sql.ResultSetMetaData;
34
+ import java.sql.SQLException;
35
+ import java.sql.Statement;
36
+ import java.sql.Types;
37
+
38
+ import arjdbc.jdbc.RubyJdbcConnection;
39
+ import arjdbc.jdbc.SQLBlock;
40
+
41
+ import org.jruby.Ruby;
42
+ import org.jruby.RubyClass;
43
+ import org.jruby.anno.JRubyMethod;
44
+ import org.jruby.runtime.ObjectAllocator;
45
+ import org.jruby.runtime.ThreadContext;
46
+ import org.jruby.runtime.builtin.IRubyObject;
47
+
48
+ /**
49
+ *
50
+ * @author enebo
51
+ */
52
+ public class SQLite3RubyJdbcConnection extends RubyJdbcConnection {
53
+
54
+ protected SQLite3RubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
55
+ super(runtime, metaClass);
56
+ }
57
+
58
+ public static RubyClass createSQLite3JdbcConnectionClass(Ruby runtime, RubyClass jdbcConnection) {
59
+ final RubyClass clazz = getConnectionAdapters(runtime). // ActiveRecord::ConnectionAdapters
60
+ defineClassUnder("SQLite3JdbcConnection", jdbcConnection, SQLITE3_JDBCCONNECTION_ALLOCATOR);
61
+ clazz.defineAnnotatedMethods( SQLite3RubyJdbcConnection.class );
62
+ getConnectionAdapters(runtime).setConstant("Sqlite3JdbcConnection", clazz); // backwards-compat
63
+ return clazz;
64
+ }
65
+
66
+ private static ObjectAllocator SQLITE3_JDBCCONNECTION_ALLOCATOR = new ObjectAllocator() {
67
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
68
+ return new SQLite3RubyJdbcConnection(runtime, klass);
69
+ }
70
+ };
71
+
72
+ @JRubyMethod(name = "last_insert_row_id")
73
+ public IRubyObject getLastInsertRowId(final ThreadContext context)
74
+ throws SQLException {
75
+ return (IRubyObject) withConnectionAndRetry(context, new SQLBlock() {
76
+ public Object call(Connection c) throws SQLException {
77
+ Statement stmt = null;
78
+ try {
79
+ stmt = c.createStatement();
80
+ return unmarshal_id_result(context.getRuntime(),
81
+ stmt.getGeneratedKeys());
82
+ } catch (SQLException sqe) {
83
+ if (context.getRuntime().isDebug()) {
84
+ System.out.println("Error SQL:" + sqe.getMessage());
85
+ }
86
+ throw sqe;
87
+ } finally {
88
+ close(stmt);
89
+ }
90
+ }
91
+ });
92
+ }
93
+
94
+ @Override
95
+ protected IRubyObject tables(ThreadContext context, String catalog, String schemaPattern, String tablePattern, String[] types) {
96
+ return (IRubyObject) withConnectionAndRetry(context, tableLookupBlock(context.getRuntime(), catalog, schemaPattern, tablePattern, types, true));
97
+ }
98
+
99
+ @Override
100
+ protected IRubyObject jdbcToRuby(Ruby runtime, int column, int type, ResultSet resultSet)
101
+ throws SQLException {
102
+ try {
103
+ // This is rather gross, and only needed because the resultset metadata for SQLite tries to be overly
104
+ // clever, and returns a type for the column of the "current" row, so an integer value stored in a
105
+ // decimal column is returned as Types.INTEGER. Therefore, if the first row of a resultset was an
106
+ // integer value, all rows of that result set would get truncated.
107
+ if( resultSet instanceof ResultSetMetaData ) {
108
+ type = ((ResultSetMetaData)resultSet).getColumnType(column);
109
+ }
110
+ switch (type) {
111
+ case Types.BINARY:
112
+ case Types.BLOB:
113
+ case Types.LONGVARBINARY:
114
+ case Types.VARBINARY:
115
+ return streamToRuby(runtime, resultSet, new ByteArrayInputStream(resultSet.getBytes(column)));
116
+ case Types.LONGVARCHAR:
117
+ return runtime.is1_9() ?
118
+ readerToRuby(runtime, resultSet, resultSet.getCharacterStream(column)) :
119
+ streamToRuby(runtime, resultSet, new ByteArrayInputStream(resultSet.getBytes(column)));
120
+ default:
121
+ return super.jdbcToRuby(runtime, column, type, resultSet);
122
+ }
123
+ } catch (IOException ioe) {
124
+ throw (SQLException) new SQLException(ioe.getMessage()).initCause(ioe);
125
+ }
126
+ }
127
+ }