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.
- data/.gitignore +1 -0
- data/.travis.yml +5 -1
- data/Appraisals +5 -5
- data/Gemfile +9 -1
- data/Gemfile.lock +44 -10
- data/History.txt +126 -2
- data/README.md +246 -0
- data/Rakefile +34 -25
- data/activerecord-jdbc-adapter.gemspec +1 -1
- data/gemfiles/rails23.gemfile +5 -3
- data/gemfiles/rails23.gemfile.lock +26 -18
- data/gemfiles/rails30.gemfile +4 -2
- data/gemfiles/rails30.gemfile.lock +16 -8
- data/gemfiles/rails31.gemfile +4 -2
- data/gemfiles/rails31.gemfile.lock +16 -9
- data/gemfiles/rails32.gemfile +4 -2
- data/gemfiles/rails32.gemfile.lock +15 -8
- data/lib/active_record/connection_adapters/db2_adapter.rb +1 -0
- data/lib/arel/visitors/sql_server.rb +3 -0
- data/lib/arjdbc.rb +3 -5
- data/lib/arjdbc/db2.rb +1 -0
- data/lib/arjdbc/db2/adapter.rb +302 -196
- data/lib/arjdbc/db2/connection_methods.rb +18 -0
- data/lib/arjdbc/derby/active_record_patch.rb +12 -0
- data/lib/arjdbc/derby/adapter.rb +180 -158
- data/lib/arjdbc/derby/connection_methods.rb +5 -1
- data/lib/arjdbc/firebird/adapter.rb +27 -19
- data/lib/arjdbc/h2/adapter.rb +162 -7
- data/lib/arjdbc/h2/connection_methods.rb +5 -1
- data/lib/arjdbc/hsqldb.rb +1 -1
- data/lib/arjdbc/hsqldb/adapter.rb +96 -61
- data/lib/arjdbc/hsqldb/connection_methods.rb +5 -1
- data/lib/arjdbc/hsqldb/explain_support.rb +35 -0
- data/lib/arjdbc/informix/adapter.rb +56 -55
- data/lib/arjdbc/jdbc/adapter.rb +173 -86
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/jdbc/column.rb +28 -23
- data/lib/arjdbc/jdbc/connection.rb +10 -6
- data/lib/arjdbc/jdbc/driver.rb +13 -5
- data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +21 -0
- data/lib/arjdbc/mssql.rb +1 -1
- data/lib/arjdbc/mssql/adapter.rb +51 -53
- data/lib/arjdbc/mssql/connection_methods.rb +8 -1
- data/lib/arjdbc/mysql.rb +1 -1
- data/lib/arjdbc/mysql/adapter.rb +186 -150
- data/lib/arjdbc/mysql/connection_methods.rb +9 -9
- data/lib/arjdbc/mysql/explain_support.rb +85 -0
- data/lib/arjdbc/oracle.rb +1 -1
- data/lib/arjdbc/oracle/adapter.rb +232 -125
- data/lib/arjdbc/oracle/connection_methods.rb +2 -2
- data/lib/arjdbc/postgresql.rb +1 -1
- data/lib/arjdbc/postgresql/adapter.rb +134 -86
- data/lib/arjdbc/postgresql/connection_methods.rb +6 -4
- data/lib/arjdbc/postgresql/explain_support.rb +55 -0
- data/lib/arjdbc/sqlite3.rb +1 -1
- data/lib/arjdbc/sqlite3/adapter.rb +176 -108
- data/lib/arjdbc/sqlite3/connection_methods.rb +5 -5
- data/lib/arjdbc/sqlite3/explain_support.rb +32 -0
- data/lib/arjdbc/sybase/adapter.rb +7 -6
- data/lib/arjdbc/version.rb +1 -1
- data/pom.xml +1 -1
- data/rakelib/02-test.rake +9 -11
- data/rakelib/rails.rake +18 -10
- data/src/java/arjdbc/db2/DB2Module.java +70 -0
- data/src/java/arjdbc/derby/DerbyModule.java +24 -5
- data/src/java/arjdbc/hsqldb/HSQLDBModule.java +66 -0
- data/src/java/arjdbc/jdbc/AdapterJavaService.java +14 -7
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +111 -89
- data/src/java/arjdbc/mysql/MySQLModule.java +79 -70
- data/src/java/arjdbc/oracle/OracleModule.java +74 -0
- data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +5 -10
- data/src/java/arjdbc/sqlite3/SQLite3Module.java +77 -0
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +127 -0
- data/src/java/arjdbc/sqlite3/Sqlite3RubyJdbcConnection.java +25 -111
- data/src/java/arjdbc/util/QuotingUtils.java +104 -0
- data/test/abstract_db_create.rb +6 -6
- data/test/activerecord/connection_adapters/type_conversion_test.rb +2 -2
- data/test/assets/flowers.jpg +0 -0
- data/test/binary.rb +67 -0
- data/test/db/db2.rb +30 -7
- data/test/db/jdbc.rb +4 -2
- data/test/db/oracle.rb +18 -27
- data/test/db2_binary_test.rb +6 -0
- data/test/db2_serialize_test.rb +6 -0
- data/test/db2_simple_test.rb +20 -25
- data/test/db2_test.rb +71 -0
- data/test/derby_binary_test.rb +6 -0
- data/test/derby_migration_test.rb +42 -35
- data/test/derby_reset_column_information_test.rb +1 -0
- data/test/derby_row_locking_test.rb +17 -0
- data/test/derby_schema_dump_test.rb +9 -0
- data/test/derby_serialize_test.rb +6 -0
- data/test/derby_simple_test.rb +59 -17
- data/test/generic_jdbc_connection_test.rb +112 -5
- data/test/h2_binary_test.rb +6 -0
- data/test/h2_change_column_test.rb +1 -1
- data/test/h2_schema_dump_test.rb +25 -0
- data/test/h2_serialize_test.rb +6 -0
- data/test/h2_simple_test.rb +23 -9
- data/test/has_many_through.rb +18 -4
- data/test/hsqldb_binary_test.rb +6 -0
- data/test/hsqldb_schema_dump_test.rb +15 -0
- data/test/hsqldb_serialize_test.rb +6 -0
- data/test/hsqldb_simple_test.rb +1 -0
- data/test/informix_simple_test.rb +1 -1
- data/test/jdbc/db2.rb +23 -0
- data/test/jdbc/oracle.rb +23 -0
- data/test/jdbc_common.rb +3 -110
- data/test/jndi_callbacks_test.rb +0 -2
- data/test/jndi_test.rb +2 -0
- data/test/models/binary.rb +18 -0
- data/test/models/custom_pk_name.rb +1 -0
- data/test/models/data_types.rb +11 -2
- data/test/models/entry.rb +1 -1
- data/test/models/string_id.rb +2 -2
- data/test/models/thing.rb +1 -1
- data/test/models/topic.rb +32 -0
- data/test/mssql_legacy_types_test.rb +1 -1
- data/test/mssql_limit_offset_test.rb +13 -3
- data/test/mssql_serialize_test.rb +6 -0
- data/test/mysql_binary_test.rb +6 -0
- data/test/mysql_schema_dump_test.rb +220 -0
- data/test/mysql_serialize_test.rb +6 -0
- data/test/mysql_simple_test.rb +22 -2
- data/test/mysql_test.rb +93 -0
- data/test/oracle_binary_test.rb +6 -0
- data/test/oracle_limit_test.rb +2 -1
- data/test/oracle_serialize_test.rb +6 -0
- data/test/oracle_simple_test.rb +61 -0
- data/test/oracle_specific_test.rb +77 -26
- data/test/postgres_binary_test.rb +6 -0
- data/test/postgres_native_type_mapping_test.rb +12 -11
- data/test/postgres_nonseq_pkey_test.rb +1 -0
- data/test/postgres_reserved_test.rb +1 -0
- data/test/postgres_reset_column_information_test.rb +1 -0
- data/test/postgres_row_locking_test.rb +21 -0
- data/test/postgres_schema_dump_test.rb +88 -0
- data/test/postgres_schema_search_path_test.rb +1 -0
- data/test/postgres_simple_test.rb +62 -89
- data/test/postgres_table_alias_length_test.rb +1 -0
- data/test/postgres_test.rb +31 -0
- data/test/postgres_type_conversion_test.rb +16 -16
- data/test/row_locking.rb +69 -64
- data/test/schema_dump.rb +168 -0
- data/test/serialize.rb +277 -0
- data/test/simple.rb +326 -122
- data/test/sqlite3_serialize_test.rb +6 -0
- data/test/sqlite3_simple_test.rb +51 -84
- data/test/sqlite3_type_conversion_test.rb +101 -0
- data/test/test_helper.rb +224 -0
- metadata +325 -366
- data/README.rdoc +0 -214
- data/test/db/logger.rb +0 -3
- data/test/derby_multibyte_test.rb +0 -11
- 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
|
41
|
-
|
45
|
+
RubyModule mySQL = arJdbc.defineModuleUnder("MySQL");
|
46
|
+
mySQL.defineAnnotatedMethods(MySQLModule.class);
|
42
47
|
}
|
43
48
|
|
44
|
-
private final static byte
|
45
|
-
private final static byte[]
|
46
|
-
|
47
|
-
private final static byte[]
|
48
|
-
private final static byte[]
|
49
|
-
private final static byte[]
|
50
|
-
private final static byte[]
|
51
|
-
|
52
|
-
private final
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
ByteList
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
-
|
75
|
-
|
76
|
-
|
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
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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 = "
|
87
|
-
public static IRubyObject
|
88
|
-
|
89
|
-
|
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 = "
|
99
|
-
public static IRubyObject
|
100
|
-
|
101
|
-
|
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,
|
122
|
-
|
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
|
-
|
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
|
+
}
|