activerecord-jdbc-adapter 0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +61 -0
- data/LICENSE +21 -0
- data/Manifest.txt +64 -0
- data/README.txt +116 -0
- data/Rakefile +146 -0
- data/lib/active_record/connection_adapters/derby_adapter.rb +13 -0
- data/lib/active_record/connection_adapters/h2_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/hsqldb_adapter.rb +13 -0
- data/lib/active_record/connection_adapters/jdbc_adapter.rb +575 -0
- data/lib/active_record/connection_adapters/jdbc_adapter_spec.rb +10 -0
- data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +13 -0
- data/lib/active_record/connection_adapters/oracle_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +13 -0
- data/lib/jdbc_adapter.rb +32 -0
- data/lib/jdbc_adapter/jdbc_db2.rb +104 -0
- data/lib/jdbc_adapter/jdbc_derby.rb +362 -0
- data/lib/jdbc_adapter/jdbc_firebird.rb +109 -0
- data/lib/jdbc_adapter/jdbc_hsqldb.rb +168 -0
- data/lib/jdbc_adapter/jdbc_mimer.rb +134 -0
- data/lib/jdbc_adapter/jdbc_mssql.rb +356 -0
- data/lib/jdbc_adapter/jdbc_mysql.rb +168 -0
- data/lib/jdbc_adapter/jdbc_oracle.rb +340 -0
- data/lib/jdbc_adapter/jdbc_postgre.rb +347 -0
- data/lib/jdbc_adapter/missing_functionality_helper.rb +72 -0
- data/lib/jdbc_adapter/version.rb +5 -0
- data/lib/jdbc_adapter_internal.jar +0 -0
- data/lib/tasks/jdbc_databases.rake +72 -0
- data/src/java/JDBCDerbySpec.java +323 -0
- data/src/java/JDBCMySQLSpec.java +89 -0
- data/src/java/JdbcAdapterInternalService.java +953 -0
- data/test/activerecord/connection_adapters/type_conversion_test.rb +31 -0
- data/test/activerecord/connections/native_jdbc_mysql/connection.rb +25 -0
- data/test/db/derby.rb +18 -0
- data/test/db/h2.rb +11 -0
- data/test/db/hsqldb.rb +15 -0
- data/test/db/jdbc.rb +11 -0
- data/test/db/jndi_config.rb +30 -0
- data/test/db/logger.rb +3 -0
- data/test/db/mysql.rb +9 -0
- data/test/db/postgres.rb +9 -0
- data/test/derby_multibyte_test.rb +12 -0
- data/test/derby_simple_test.rb +12 -0
- data/test/generic_jdbc_connection_test.rb +9 -0
- data/test/h2_simple_test.rb +7 -0
- data/test/hsqldb_simple_test.rb +6 -0
- data/test/jdbc_adapter/jdbc_db2_test.rb +21 -0
- data/test/jdbc_common.rb +6 -0
- data/test/jndi_test.rb +37 -0
- data/test/manualTestDatabase.rb +195 -0
- data/test/minirunit.rb +109 -0
- data/test/minirunit/testConnect.rb +14 -0
- data/test/minirunit/testH2.rb +73 -0
- data/test/minirunit/testHsqldb.rb +73 -0
- data/test/minirunit/testLoadActiveRecord.rb +3 -0
- data/test/minirunit/testMysql.rb +83 -0
- data/test/minirunit/testRawSelect.rb +24 -0
- data/test/models/auto_id.rb +18 -0
- data/test/models/data_types.rb +18 -0
- data/test/models/entry.rb +20 -0
- data/test/mysql_multibyte_test.rb +6 -0
- data/test/mysql_simple_test.rb +13 -0
- data/test/postgres_simple_test.rb +12 -0
- data/test/simple.rb +157 -0
- metadata +112 -0
@@ -0,0 +1,323 @@
|
|
1
|
+
/***** BEGIN LICENSE BLOCK *****
|
2
|
+
* Version: CPL 1.0/GPL 2.0/LGPL 2.1
|
3
|
+
*
|
4
|
+
* The contents of this file are subject to the Common Public
|
5
|
+
* License Version 1.0 (the "License"); you may not use this file
|
6
|
+
* except in compliance with the License. You may obtain a copy of
|
7
|
+
* the License at http://www.eclipse.org/legal/cpl-v10.html
|
8
|
+
*
|
9
|
+
* Software distributed under the License is distributed on an "AS
|
10
|
+
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
11
|
+
* implied. See the License for the specific language governing
|
12
|
+
* rights and limitations under the License.
|
13
|
+
*
|
14
|
+
* Copyright (C) 2007 Ola Bini <ola.bini@gmail.com>
|
15
|
+
*
|
16
|
+
* Alternatively, the contents of this file may be used under the terms of
|
17
|
+
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
18
|
+
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
19
|
+
* in which case the provisions of the GPL or the LGPL are applicable instead
|
20
|
+
* of those above. If you wish to allow use of your version of this file only
|
21
|
+
* under the terms of either the GPL or the LGPL, and not to allow others to
|
22
|
+
* use your version of this file under the terms of the CPL, indicate your
|
23
|
+
* decision by deleting the provisions above and replace them with the notice
|
24
|
+
* and other provisions required by the GPL or the LGPL. If you do not delete
|
25
|
+
* the provisions above, a recipient may use your version of this file under
|
26
|
+
* the terms of any one of the CPL, the GPL or the LGPL.
|
27
|
+
***** END LICENSE BLOCK *****/
|
28
|
+
|
29
|
+
import org.jruby.Ruby;
|
30
|
+
import org.jruby.RubyModule;
|
31
|
+
import org.jruby.RubyString;
|
32
|
+
import org.jruby.RubyFloat;
|
33
|
+
import org.jruby.RubyFixnum;
|
34
|
+
import org.jruby.RubyBignum;
|
35
|
+
import org.jruby.RubyBoolean;
|
36
|
+
import org.jruby.RubyBigDecimal;
|
37
|
+
import org.jruby.RubyRange;
|
38
|
+
import org.jruby.RubyNumeric;
|
39
|
+
|
40
|
+
import org.jruby.runtime.Arity;
|
41
|
+
import org.jruby.runtime.CallbackFactory;
|
42
|
+
import org.jruby.runtime.MethodIndex;
|
43
|
+
import org.jruby.runtime.ThreadContext;
|
44
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
45
|
+
|
46
|
+
import org.jruby.util.ByteList;
|
47
|
+
|
48
|
+
import java.sql.SQLException;
|
49
|
+
|
50
|
+
public class JDBCDerbySpec {
|
51
|
+
public static void load(Ruby runtime, RubyModule jdbcSpec) {
|
52
|
+
RubyModule derby = jdbcSpec.defineModuleUnder("Derby");
|
53
|
+
CallbackFactory cf = runtime.callbackFactory(JDBCDerbySpec.class);
|
54
|
+
derby.defineFastMethod("quote_string",cf.getFastSingletonMethod("quote_string",IRubyObject.class));
|
55
|
+
derby.defineFastMethod("quote",cf.getFastOptSingletonMethod("quote"));
|
56
|
+
derby.defineFastMethod("_execute",cf.getFastOptSingletonMethod("_execute"));
|
57
|
+
derby.defineFastMethod("add_limit_offset!",cf.getFastSingletonMethod("add_limit_offset", IRubyObject.class, IRubyObject.class));
|
58
|
+
derby.defineFastMethod("select_all",cf.getFastOptSingletonMethod("select_all"));
|
59
|
+
derby.defineFastMethod("select_one",cf.getFastOptSingletonMethod("select_one"));
|
60
|
+
RubyModule col = derby.defineModuleUnder("Column");
|
61
|
+
col.defineFastMethod("type_cast",cf.getFastSingletonMethod("type_cast", IRubyObject.class));
|
62
|
+
}
|
63
|
+
|
64
|
+
/*
|
65
|
+
* JdbcSpec::Derby::Column.type_cast(value)
|
66
|
+
*/
|
67
|
+
public static IRubyObject type_cast(IRubyObject recv, IRubyObject value) {
|
68
|
+
Ruby runtime = recv.getRuntime();
|
69
|
+
|
70
|
+
if(value.isNil() || ((value instanceof RubyString) && value.toString().trim().equalsIgnoreCase("null"))) {
|
71
|
+
return runtime.getNil();
|
72
|
+
}
|
73
|
+
|
74
|
+
String type = recv.getInstanceVariable("@type").toString();
|
75
|
+
|
76
|
+
switch(type.charAt(0)) {
|
77
|
+
case 's': //string
|
78
|
+
return value;
|
79
|
+
case 't': //text, timestamp, time
|
80
|
+
if(type.equals("text")) {
|
81
|
+
return value;
|
82
|
+
} else {
|
83
|
+
return recv.callMethod(runtime.getCurrentContext(), "cast_to_time", value);
|
84
|
+
}
|
85
|
+
case 'i': //integer
|
86
|
+
case 'p': //primary key
|
87
|
+
if(value.respondsTo("to_i")) {
|
88
|
+
return value.callMethod(runtime.getCurrentContext(), "to_i");
|
89
|
+
} else {
|
90
|
+
return runtime.newFixnum( value.isTrue() ? 1 : 0 );
|
91
|
+
}
|
92
|
+
case 'd': //decimal, datetime, date
|
93
|
+
if(type.equals("datetime")) {
|
94
|
+
return recv.callMethod(runtime.getCurrentContext(), "cast_to_date_or_time", value);
|
95
|
+
} else if(type.equals("date")) {
|
96
|
+
return recv.getMetaClass().callMethod(runtime.getCurrentContext(), "string_to_date", value);
|
97
|
+
} else {
|
98
|
+
return recv.getMetaClass().callMethod(runtime.getCurrentContext(), "value_to_decimal", value);
|
99
|
+
}
|
100
|
+
case 'f': //float
|
101
|
+
return value.callMethod(runtime.getCurrentContext(),"to_f");
|
102
|
+
case 'b': //binary, boolean
|
103
|
+
if(type.equals("binary")) {
|
104
|
+
return recv.callMethod(runtime.getCurrentContext(), "value_to_binary", value);
|
105
|
+
} else {
|
106
|
+
return recv.getMetaClass().callMethod(runtime.getCurrentContext(), "value_to_boolean", value);
|
107
|
+
}
|
108
|
+
}
|
109
|
+
return value;
|
110
|
+
}
|
111
|
+
|
112
|
+
public static IRubyObject quote(IRubyObject recv, IRubyObject[] args) {
|
113
|
+
Ruby runtime = recv.getRuntime();
|
114
|
+
Arity.checkArgumentCount(runtime, args, 1, 2);
|
115
|
+
IRubyObject value = args[0];
|
116
|
+
if(args.length > 1) {
|
117
|
+
IRubyObject col = args[1];
|
118
|
+
IRubyObject type = col.callMethod(runtime.getCurrentContext(),"type");
|
119
|
+
if(value instanceof RubyString) {
|
120
|
+
if(type == runtime.newSymbol("string")) {
|
121
|
+
return quote_string_with_surround(runtime, "'", (RubyString)value, "'");
|
122
|
+
} else if(type == runtime.newSymbol("text")) {
|
123
|
+
return quote_string_with_surround(runtime, "CAST('", (RubyString)value, "' AS CLOB)");
|
124
|
+
} else if(type == runtime.newSymbol("binary")) {
|
125
|
+
return hexquote_string_with_surround(runtime, "CAST('", (RubyString)value, "' AS BLOB)");
|
126
|
+
} else {
|
127
|
+
// column type :integer or other numeric or date version
|
128
|
+
if(only_digits((RubyString)value)) {
|
129
|
+
return value;
|
130
|
+
} else {
|
131
|
+
return super_quote(recv, runtime, value, col);
|
132
|
+
}
|
133
|
+
}
|
134
|
+
} else if((value instanceof RubyFloat) || (value instanceof RubyFixnum) || (value instanceof RubyBignum)) {
|
135
|
+
if(type == runtime.newSymbol("string")) {
|
136
|
+
return quote_string_with_surround(runtime, "'", RubyString.objAsString(value), "'");
|
137
|
+
}
|
138
|
+
}
|
139
|
+
}
|
140
|
+
return super_quote(recv, runtime, value, runtime.getNil());
|
141
|
+
}
|
142
|
+
|
143
|
+
private final static ByteList NULL = new ByteList("NULL".getBytes());
|
144
|
+
|
145
|
+
public static IRubyObject super_quote(IRubyObject recv, Ruby runtime, IRubyObject value, IRubyObject col) {
|
146
|
+
if(value.respondsTo("quoted_id")) {
|
147
|
+
return value.callMethod(runtime.getCurrentContext(), "quoted_id");
|
148
|
+
}
|
149
|
+
|
150
|
+
IRubyObject type = (col.isNil()) ? col : col.callMethod(runtime.getCurrentContext(),"type");
|
151
|
+
if(value instanceof RubyString ||
|
152
|
+
value.isKindOf((RubyModule)(((RubyModule)((RubyModule)runtime.getModule("ActiveSupport")).getConstant("Multibyte")).getConstantAt("Chars")))) {
|
153
|
+
RubyString svalue = RubyString.objAsString(value);
|
154
|
+
if(type == runtime.newSymbol("binary") && col.getType().respondsTo("string_to_binary")) {
|
155
|
+
return quote_string_with_surround(runtime, "'", (RubyString)(col.getType().callMethod(runtime.getCurrentContext(), "string_to_binary", svalue)), "'");
|
156
|
+
} else if(type == runtime.newSymbol("integer") || type == runtime.newSymbol("float")) {
|
157
|
+
return RubyString.objAsString(((type == runtime.newSymbol("integer")) ?
|
158
|
+
svalue.callMethod(runtime.getCurrentContext(), "to_i") :
|
159
|
+
svalue.callMethod(runtime.getCurrentContext(), "to_f")));
|
160
|
+
} else {
|
161
|
+
return quote_string_with_surround(runtime, "'", svalue, "'");
|
162
|
+
}
|
163
|
+
} else if(value.isNil()) {
|
164
|
+
return runtime.newStringShared(NULL);
|
165
|
+
} else if(value instanceof RubyBoolean) {
|
166
|
+
return (value.isTrue() ?
|
167
|
+
(type == runtime.newSymbol(":integer")) ? runtime.newString("1") : recv.callMethod(runtime.getCurrentContext(),"quoted_true") :
|
168
|
+
(type == runtime.newSymbol(":integer")) ? runtime.newString("0") : recv.callMethod(runtime.getCurrentContext(),"quoted_false"));
|
169
|
+
} else if((value instanceof RubyFloat) || (value instanceof RubyFixnum) || (value instanceof RubyBignum)) {
|
170
|
+
return RubyString.objAsString(value);
|
171
|
+
} else if(value instanceof RubyBigDecimal) {
|
172
|
+
return value.callMethod(runtime.getCurrentContext(), "to_s", runtime.newString("F"));
|
173
|
+
} else if(value.isKindOf(runtime.getModule("Date"))) {
|
174
|
+
return quote_string_with_surround(runtime, "'", RubyString.objAsString(value), "'");
|
175
|
+
} else if(value.isKindOf(runtime.getModule("Time")) || value.isKindOf(runtime.getModule("DateTime"))) {
|
176
|
+
return quote_string_with_surround(runtime, "'", (RubyString)(recv.callMethod(runtime.getCurrentContext(), "quoted_date", value)), "'");
|
177
|
+
} else {
|
178
|
+
return quote_string_with_surround(runtime, "'", (RubyString)(value.callMethod(runtime.getCurrentContext(), "to_yaml")), "'");
|
179
|
+
}
|
180
|
+
}
|
181
|
+
|
182
|
+
private final static ByteList TWO_SINGLE = new ByteList(new byte[]{'\'','\''});
|
183
|
+
|
184
|
+
public static IRubyObject quote_string_with_surround(Ruby runtime, String before, RubyString string, String after) {
|
185
|
+
ByteList input = string.getByteList();
|
186
|
+
ByteList output = new ByteList(before.getBytes());
|
187
|
+
for(int i = input.begin; i< input.begin + input.realSize; i++) {
|
188
|
+
switch(input.bytes[i]) {
|
189
|
+
case '\'':
|
190
|
+
output.append(input.bytes[i]);
|
191
|
+
//FALLTHROUGH
|
192
|
+
default:
|
193
|
+
output.append(input.bytes[i]);
|
194
|
+
}
|
195
|
+
|
196
|
+
}
|
197
|
+
|
198
|
+
output.append(after.getBytes());
|
199
|
+
|
200
|
+
return runtime.newStringShared(output);
|
201
|
+
}
|
202
|
+
|
203
|
+
private final static byte[] HEX = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
|
204
|
+
|
205
|
+
public static IRubyObject hexquote_string_with_surround(Ruby runtime, String before, RubyString string, String after) {
|
206
|
+
ByteList input = string.getByteList();
|
207
|
+
ByteList output = new ByteList(before.getBytes());
|
208
|
+
for(int i = input.begin; i< input.begin + input.realSize; i++) {
|
209
|
+
byte b1 = input.bytes[i];
|
210
|
+
byte higher = HEX[(((char)b1)>>4)%16];
|
211
|
+
byte lower = HEX[((char)b1)%16];
|
212
|
+
if(b1 == '\'') {
|
213
|
+
output.append(higher);
|
214
|
+
output.append(lower);
|
215
|
+
}
|
216
|
+
output.append(higher);
|
217
|
+
output.append(lower);
|
218
|
+
}
|
219
|
+
|
220
|
+
output.append(after.getBytes());
|
221
|
+
return runtime.newStringShared(output);
|
222
|
+
}
|
223
|
+
|
224
|
+
private static boolean only_digits(RubyString inp) {
|
225
|
+
ByteList input = inp.getByteList();
|
226
|
+
for(int i = input.begin; i< input.begin + input.realSize; i++) {
|
227
|
+
if(input.bytes[i] < '0' || input.bytes[i] > '9') {
|
228
|
+
return false;
|
229
|
+
}
|
230
|
+
}
|
231
|
+
return true;
|
232
|
+
}
|
233
|
+
|
234
|
+
public static IRubyObject quote_string(IRubyObject recv, IRubyObject string) {
|
235
|
+
boolean replacementFound = false;
|
236
|
+
ByteList bl = ((RubyString) string).getByteList();
|
237
|
+
|
238
|
+
for(int i = bl.begin; i < bl.begin + bl.realSize; i++) {
|
239
|
+
switch (bl.bytes[i]) {
|
240
|
+
case '\'': break;
|
241
|
+
default: continue;
|
242
|
+
}
|
243
|
+
|
244
|
+
// On first replacement allocate a different bytelist so we don't manip original
|
245
|
+
if(!replacementFound) {
|
246
|
+
i-= bl.begin;
|
247
|
+
bl = new ByteList(bl);
|
248
|
+
replacementFound = true;
|
249
|
+
}
|
250
|
+
|
251
|
+
bl.replace(i, 1, TWO_SINGLE);
|
252
|
+
i+=1;
|
253
|
+
}
|
254
|
+
if(replacementFound) {
|
255
|
+
return recv.getRuntime().newStringShared(bl);
|
256
|
+
} else {
|
257
|
+
return string;
|
258
|
+
}
|
259
|
+
}
|
260
|
+
|
261
|
+
public static IRubyObject select_all(IRubyObject recv, IRubyObject[] args) {
|
262
|
+
return recv.callMethod(recv.getRuntime().getCurrentContext(),"execute",args);
|
263
|
+
}
|
264
|
+
|
265
|
+
public static IRubyObject select_one(IRubyObject recv, IRubyObject[] args) {
|
266
|
+
IRubyObject limit = recv.getInstanceVariable("@limit");
|
267
|
+
if(limit == null || limit.isNil()) {
|
268
|
+
recv.setInstanceVariable("@limit", recv.getRuntime().newFixnum(1));
|
269
|
+
}
|
270
|
+
try {
|
271
|
+
return recv.callMethod(recv.getRuntime().getCurrentContext(),"execute",args).callMethod(recv.getRuntime().getCurrentContext(), "first");
|
272
|
+
} finally {
|
273
|
+
recv.setInstanceVariable("@limit", recv.getRuntime().getNil());
|
274
|
+
}
|
275
|
+
}
|
276
|
+
|
277
|
+
public static IRubyObject add_limit_offset(IRubyObject recv, IRubyObject sql, IRubyObject options) {
|
278
|
+
recv.setInstanceVariable("@limit", options.callMethod(recv.getRuntime().getCurrentContext(), MethodIndex.AREF, "[]", recv.getRuntime().newSymbol("limit")));
|
279
|
+
return recv.setInstanceVariable("@offset", options.callMethod(recv.getRuntime().getCurrentContext(), MethodIndex.AREF, "[]", recv.getRuntime().newSymbol("offset")));
|
280
|
+
}
|
281
|
+
|
282
|
+
public static IRubyObject _execute(IRubyObject recv, IRubyObject[] args) throws SQLException, java.io.IOException {
|
283
|
+
Ruby runtime = recv.getRuntime();
|
284
|
+
try {
|
285
|
+
IRubyObject conn = recv.getInstanceVariable("@connection");
|
286
|
+
String sql = args[0].toString().trim().toLowerCase();
|
287
|
+
if(sql.charAt(0) == '(') {
|
288
|
+
sql = sql.substring(1).trim();
|
289
|
+
}
|
290
|
+
if(sql.startsWith("insert")) {
|
291
|
+
return JdbcAdapterInternalService.execute_insert(conn, args[0]);
|
292
|
+
} else if(sql.startsWith("select") || sql.startsWith("show")) {
|
293
|
+
IRubyObject offset = recv.getInstanceVariable("@offset");
|
294
|
+
if(offset == null || offset.isNil()) {
|
295
|
+
offset = RubyFixnum.zero(runtime);
|
296
|
+
}
|
297
|
+
IRubyObject limit = recv.getInstanceVariable("@limit");
|
298
|
+
IRubyObject range;
|
299
|
+
IRubyObject max;
|
300
|
+
if(limit == null || limit.isNil() || RubyNumeric.fix2int(limit) == -1) {
|
301
|
+
range = RubyRange.newRange(runtime, offset, runtime.newFixnum(-1), false);
|
302
|
+
max = RubyFixnum.zero(runtime);
|
303
|
+
} else {
|
304
|
+
IRubyObject v1 = offset.callMethod(runtime.getCurrentContext(), MethodIndex.OP_PLUS, "+", limit);
|
305
|
+
range = RubyRange.newRange(runtime, offset, v1, true);
|
306
|
+
max = v1.callMethod(runtime.getCurrentContext(), MethodIndex.OP_PLUS, "+", RubyFixnum.one(runtime));
|
307
|
+
}
|
308
|
+
IRubyObject ret = JdbcAdapterInternalService.execute_query(conn, new IRubyObject[]{args[0], max}).
|
309
|
+
callMethod(runtime.getCurrentContext(), MethodIndex.AREF, "[]", range);
|
310
|
+
if(ret.isNil()) {
|
311
|
+
return runtime.newArray();
|
312
|
+
} else {
|
313
|
+
return ret;
|
314
|
+
}
|
315
|
+
} else {
|
316
|
+
return JdbcAdapterInternalService.execute_update(conn, args[0]);
|
317
|
+
}
|
318
|
+
} finally {
|
319
|
+
recv.setInstanceVariable("@limit",runtime.getNil());
|
320
|
+
recv.setInstanceVariable("@offset",runtime.getNil());
|
321
|
+
}
|
322
|
+
}
|
323
|
+
}
|
@@ -0,0 +1,89 @@
|
|
1
|
+
/***** BEGIN LICENSE BLOCK *****
|
2
|
+
* Version: CPL 1.0/GPL 2.0/LGPL 2.1
|
3
|
+
*
|
4
|
+
* The contents of this file are subject to the Common Public
|
5
|
+
* License Version 1.0 (the "License"); you may not use this file
|
6
|
+
* except in compliance with the License. You may obtain a copy of
|
7
|
+
* the License at http://www.eclipse.org/legal/cpl-v10.html
|
8
|
+
*
|
9
|
+
* Software distributed under the License is distributed on an "AS
|
10
|
+
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
11
|
+
* implied. See the License for the specific language governing
|
12
|
+
* rights and limitations under the License.
|
13
|
+
*
|
14
|
+
* Copyright (C) 2007 Ola Bini <ola.bini@gmail.com>
|
15
|
+
*
|
16
|
+
* Alternatively, the contents of this file may be used under the terms of
|
17
|
+
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
18
|
+
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
19
|
+
* in which case the provisions of the GPL or the LGPL are applicable instead
|
20
|
+
* of those above. If you wish to allow use of your version of this file only
|
21
|
+
* under the terms of either the GPL or the LGPL, and not to allow others to
|
22
|
+
* use your version of this file under the terms of the CPL, indicate your
|
23
|
+
* decision by deleting the provisions above and replace them with the notice
|
24
|
+
* and other provisions required by the GPL or the LGPL. If you do not delete
|
25
|
+
* the provisions above, a recipient may use your version of this file under
|
26
|
+
* the terms of any one of the CPL, the GPL or the LGPL.
|
27
|
+
***** END LICENSE BLOCK *****/
|
28
|
+
|
29
|
+
import org.jruby.Ruby;
|
30
|
+
import org.jruby.RubyModule;
|
31
|
+
import org.jruby.RubyString;
|
32
|
+
|
33
|
+
import org.jruby.runtime.CallbackFactory;
|
34
|
+
import org.jruby.runtime.ThreadContext;
|
35
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
36
|
+
|
37
|
+
import org.jruby.util.ByteList;
|
38
|
+
|
39
|
+
public class JDBCMySQLSpec {
|
40
|
+
public static void load(Ruby runtime, RubyModule jdbcSpec) {
|
41
|
+
RubyModule mysql = jdbcSpec.defineModuleUnder("MySQL");
|
42
|
+
CallbackFactory cf = runtime.callbackFactory(JDBCMySQLSpec.class);
|
43
|
+
mysql.defineFastMethod("quote_string",cf.getFastSingletonMethod("quote_string",IRubyObject.class));
|
44
|
+
}
|
45
|
+
|
46
|
+
private final static ByteList ZERO = new ByteList(new byte[]{'\\','0'});
|
47
|
+
private final static ByteList NEWLINE = new ByteList(new byte[]{'\\','n'});
|
48
|
+
private final static ByteList CARRIAGE = new ByteList(new byte[]{'\\','r'});
|
49
|
+
private final static ByteList ZED = new ByteList(new byte[]{'\\','Z'});
|
50
|
+
private final static ByteList DBL = new ByteList(new byte[]{'\\','"'});
|
51
|
+
private final static ByteList SINGLE = new ByteList(new byte[]{'\\','\''});
|
52
|
+
private final static ByteList ESCAPE = new ByteList(new byte[]{'\\','\\'});
|
53
|
+
|
54
|
+
public static IRubyObject quote_string(IRubyObject recv, IRubyObject string) {
|
55
|
+
boolean replacementFound = false;
|
56
|
+
ByteList bl = ((RubyString) string).getByteList();
|
57
|
+
|
58
|
+
for(int i = bl.begin; i < bl.begin + bl.realSize; i++) {
|
59
|
+
ByteList rep = null;
|
60
|
+
switch (bl.bytes[i]) {
|
61
|
+
case 0: rep = ZERO; break;
|
62
|
+
case '\n': rep = NEWLINE; break;
|
63
|
+
case '\r': rep = CARRIAGE; break;
|
64
|
+
case 26: rep = ZED; break;
|
65
|
+
case '"': rep = DBL; break;
|
66
|
+
case '\'': rep = SINGLE; break;
|
67
|
+
case '\\': rep = ESCAPE; break;
|
68
|
+
default: continue;
|
69
|
+
}
|
70
|
+
|
71
|
+
// On first replacement allocate a different bytelist so we don't manip original
|
72
|
+
if(!replacementFound) {
|
73
|
+
i-= bl.begin;
|
74
|
+
bl = new ByteList(bl);
|
75
|
+
replacementFound = true;
|
76
|
+
}
|
77
|
+
|
78
|
+
bl.replace(i, 1, rep);
|
79
|
+
i+=1;
|
80
|
+
}
|
81
|
+
|
82
|
+
if(!replacementFound) {
|
83
|
+
return string;
|
84
|
+
}
|
85
|
+
|
86
|
+
|
87
|
+
return recv.getRuntime().newStringShared(bl);
|
88
|
+
}
|
89
|
+
}
|
@@ -0,0 +1,953 @@
|
|
1
|
+
/***** BEGIN LICENSE BLOCK *****
|
2
|
+
* Version: CPL 1.0/GPL 2.0/LGPL 2.1
|
3
|
+
*
|
4
|
+
* The contents of this file are subject to the Common Public
|
5
|
+
* License Version 1.0 (the "License"); you may not use this file
|
6
|
+
* except in compliance with the License. You may obtain a copy of
|
7
|
+
* the License at http://www.eclipse.org/legal/cpl-v10.html
|
8
|
+
*
|
9
|
+
* Software distributed under the License is distributed on an "AS
|
10
|
+
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
11
|
+
* implied. See the License for the specific language governing
|
12
|
+
* rights and limitations under the License.
|
13
|
+
*
|
14
|
+
* Copyright (C) 2007 Ola Bini <ola@ologix.com>
|
15
|
+
*
|
16
|
+
* Alternatively, the contents of this file may be used under the terms of
|
17
|
+
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
18
|
+
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
19
|
+
* in which case the provisions of the GPL or the LGPL are applicable instead
|
20
|
+
* of those above. If you wish to allow use of your version of this file only
|
21
|
+
* under the terms of either the GPL or the LGPL, and not to allow others to
|
22
|
+
* use your version of this file under the terms of the CPL, indicate your
|
23
|
+
* decision by deleting the provisions above and replace them with the notice
|
24
|
+
* and other provisions required by the GPL or the LGPL. If you do not delete
|
25
|
+
* the provisions above, a recipient may use your version of this file under
|
26
|
+
* the terms of any one of the CPL, the GPL or the LGPL.
|
27
|
+
***** END LICENSE BLOCK *****/
|
28
|
+
import java.io.IOException;
|
29
|
+
import java.io.Reader;
|
30
|
+
import java.io.InputStream;
|
31
|
+
import java.io.ByteArrayInputStream;
|
32
|
+
import java.io.StringReader;
|
33
|
+
|
34
|
+
import java.sql.Connection;
|
35
|
+
import java.sql.DatabaseMetaData;
|
36
|
+
import java.sql.PreparedStatement;
|
37
|
+
import java.sql.ResultSetMetaData;
|
38
|
+
import java.sql.ResultSet;
|
39
|
+
import java.sql.SQLException;
|
40
|
+
import java.sql.Statement;
|
41
|
+
import java.sql.PreparedStatement;
|
42
|
+
import java.sql.Timestamp;
|
43
|
+
import java.sql.Types;
|
44
|
+
|
45
|
+
import java.text.DateFormat;
|
46
|
+
import java.text.SimpleDateFormat;
|
47
|
+
|
48
|
+
import java.util.ArrayList;
|
49
|
+
import java.util.Calendar;
|
50
|
+
import java.util.Date;
|
51
|
+
import java.util.List;
|
52
|
+
import java.util.Map;
|
53
|
+
import java.util.Iterator;
|
54
|
+
import java.util.HashMap;
|
55
|
+
|
56
|
+
import org.jruby.Ruby;
|
57
|
+
import org.jruby.RubyArray;
|
58
|
+
import org.jruby.RubyBigDecimal;
|
59
|
+
import org.jruby.RubyClass;
|
60
|
+
import org.jruby.RubyHash;
|
61
|
+
import org.jruby.RubyModule;
|
62
|
+
import org.jruby.RubyNumeric;
|
63
|
+
import org.jruby.RubyObject;
|
64
|
+
import org.jruby.RubyString;
|
65
|
+
import org.jruby.RubySymbol;
|
66
|
+
import org.jruby.RubyTime;
|
67
|
+
import org.jruby.javasupport.JavaObject;
|
68
|
+
import org.jruby.runtime.Arity;
|
69
|
+
import org.jruby.runtime.Block;
|
70
|
+
import org.jruby.runtime.CallbackFactory;
|
71
|
+
import org.jruby.runtime.ThreadContext;
|
72
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
73
|
+
import org.jruby.runtime.load.BasicLibraryService;
|
74
|
+
import org.jruby.util.ByteList;
|
75
|
+
|
76
|
+
public class JdbcAdapterInternalService implements BasicLibraryService {
|
77
|
+
public boolean basicLoad(final Ruby runtime) throws IOException {
|
78
|
+
RubyClass cJdbcConn = ((RubyModule)(runtime.getModule("ActiveRecord").getConstant("ConnectionAdapters"))).
|
79
|
+
defineClassUnder("JdbcConnection",runtime.getObject(),runtime.getObject().getAllocator());
|
80
|
+
|
81
|
+
CallbackFactory cf = runtime.callbackFactory(JdbcAdapterInternalService.class);
|
82
|
+
cJdbcConn.defineMethod("unmarshal_result",cf.getSingletonMethod("unmarshal_result", IRubyObject.class));
|
83
|
+
cJdbcConn.defineFastMethod("set_connection",cf.getFastSingletonMethod("set_connection", IRubyObject.class));
|
84
|
+
cJdbcConn.defineFastMethod("execute_update",cf.getFastSingletonMethod("execute_update", IRubyObject.class));
|
85
|
+
cJdbcConn.defineFastMethod("execute_query",cf.getFastOptSingletonMethod("execute_query"));
|
86
|
+
cJdbcConn.defineFastMethod("execute_insert",cf.getFastSingletonMethod("execute_insert", IRubyObject.class));
|
87
|
+
cJdbcConn.defineFastMethod("execute_id_insert",cf.getFastSingletonMethod("execute_id_insert", IRubyObject.class, IRubyObject.class));
|
88
|
+
cJdbcConn.defineFastMethod("primary_keys",cf.getFastSingletonMethod("primary_keys", IRubyObject.class));
|
89
|
+
cJdbcConn.defineFastMethod("set_native_database_types",cf.getFastSingletonMethod("set_native_database_types"));
|
90
|
+
cJdbcConn.defineFastMethod("native_database_types",cf.getFastSingletonMethod("native_database_types"));
|
91
|
+
cJdbcConn.defineFastMethod("begin",cf.getFastSingletonMethod("begin"));
|
92
|
+
cJdbcConn.defineFastMethod("commit",cf.getFastSingletonMethod("commit"));
|
93
|
+
cJdbcConn.defineFastMethod("rollback",cf.getFastSingletonMethod("rollback"));
|
94
|
+
cJdbcConn.defineFastMethod("database_name",cf.getFastSingletonMethod("database_name"));
|
95
|
+
cJdbcConn.defineFastMethod("columns",cf.getFastOptSingletonMethod("columns_internal"));
|
96
|
+
cJdbcConn.defineFastMethod("columns_internal",cf.getFastOptSingletonMethod("columns_internal"));
|
97
|
+
cJdbcConn.defineFastMethod("tables",cf.getFastOptSingletonMethod("tables"));
|
98
|
+
|
99
|
+
cJdbcConn.defineFastMethod("insert_bind",cf.getFastOptSingletonMethod("insert_bind"));
|
100
|
+
cJdbcConn.defineFastMethod("update_bind",cf.getFastOptSingletonMethod("update_bind"));
|
101
|
+
|
102
|
+
cJdbcConn.defineFastMethod("write_large_object",cf.getFastOptSingletonMethod("write_large_object"));
|
103
|
+
|
104
|
+
cJdbcConn.getMetaClass().defineFastMethod("insert?",cf.getFastSingletonMethod("insert_p", IRubyObject.class));
|
105
|
+
cJdbcConn.getMetaClass().defineFastMethod("select?",cf.getFastSingletonMethod("select_p", IRubyObject.class));
|
106
|
+
|
107
|
+
RubyModule jdbcSpec = runtime.getOrCreateModule("JdbcSpec");
|
108
|
+
JDBCMySQLSpec.load(runtime, jdbcSpec);
|
109
|
+
JDBCDerbySpec.load(runtime, jdbcSpec);
|
110
|
+
|
111
|
+
return true;
|
112
|
+
}
|
113
|
+
|
114
|
+
private static int whitespace(int p, final int pend, ByteList bl) {
|
115
|
+
while(p < pend) {
|
116
|
+
switch(bl.bytes[p]) {
|
117
|
+
case ' ':
|
118
|
+
case '\n':
|
119
|
+
case '\r':
|
120
|
+
case '\t':
|
121
|
+
p++;
|
122
|
+
break;
|
123
|
+
default:
|
124
|
+
return p;
|
125
|
+
}
|
126
|
+
}
|
127
|
+
return p;
|
128
|
+
}
|
129
|
+
|
130
|
+
public static IRubyObject insert_p(IRubyObject recv, IRubyObject _sql) {
|
131
|
+
ByteList bl = _sql.convertToString().getByteList();
|
132
|
+
|
133
|
+
int p = bl.begin;
|
134
|
+
int pend = p + bl.realSize;
|
135
|
+
|
136
|
+
p = whitespace(p, pend, bl);
|
137
|
+
|
138
|
+
if(pend - p >= 6) {
|
139
|
+
switch(bl.bytes[p++]) {
|
140
|
+
case 'i':
|
141
|
+
case 'I':
|
142
|
+
switch(bl.bytes[p++]) {
|
143
|
+
case 'n':
|
144
|
+
case 'N':
|
145
|
+
switch(bl.bytes[p++]) {
|
146
|
+
case 's':
|
147
|
+
case 'S':
|
148
|
+
switch(bl.bytes[p++]) {
|
149
|
+
case 'e':
|
150
|
+
case 'E':
|
151
|
+
switch(bl.bytes[p++]) {
|
152
|
+
case 'r':
|
153
|
+
case 'R':
|
154
|
+
switch(bl.bytes[p++]) {
|
155
|
+
case 't':
|
156
|
+
case 'T':
|
157
|
+
return recv.getRuntime().getTrue();
|
158
|
+
}
|
159
|
+
}
|
160
|
+
}
|
161
|
+
}
|
162
|
+
}
|
163
|
+
}
|
164
|
+
}
|
165
|
+
return recv.getRuntime().getFalse();
|
166
|
+
}
|
167
|
+
|
168
|
+
public static IRubyObject select_p(IRubyObject recv, IRubyObject _sql) {
|
169
|
+
ByteList bl = _sql.convertToString().getByteList();
|
170
|
+
|
171
|
+
int p = bl.begin;
|
172
|
+
int pend = p + bl.realSize;
|
173
|
+
|
174
|
+
p = whitespace(p, pend, bl);
|
175
|
+
|
176
|
+
if(pend - p >= 6) {
|
177
|
+
if(bl.bytes[p] == '(') {
|
178
|
+
p++;
|
179
|
+
p = whitespace(p, pend, bl);
|
180
|
+
}
|
181
|
+
if(pend - p >= 6) {
|
182
|
+
switch(bl.bytes[p++]) {
|
183
|
+
case 's':
|
184
|
+
case 'S':
|
185
|
+
switch(bl.bytes[p++]) {
|
186
|
+
case 'e':
|
187
|
+
case 'E':
|
188
|
+
switch(bl.bytes[p++]) {
|
189
|
+
case 'l':
|
190
|
+
case 'L':
|
191
|
+
switch(bl.bytes[p++]) {
|
192
|
+
case 'e':
|
193
|
+
case 'E':
|
194
|
+
switch(bl.bytes[p++]) {
|
195
|
+
case 'c':
|
196
|
+
case 'C':
|
197
|
+
switch(bl.bytes[p++]) {
|
198
|
+
case 't':
|
199
|
+
case 'T':
|
200
|
+
return recv.getRuntime().getTrue();
|
201
|
+
}
|
202
|
+
}
|
203
|
+
}
|
204
|
+
}
|
205
|
+
case 'h':
|
206
|
+
case 'H':
|
207
|
+
switch(bl.bytes[p++]) {
|
208
|
+
case 'o':
|
209
|
+
case 'O':
|
210
|
+
switch(bl.bytes[p++]) {
|
211
|
+
case 'w':
|
212
|
+
case 'W':
|
213
|
+
return recv.getRuntime().getTrue();
|
214
|
+
}
|
215
|
+
}
|
216
|
+
}
|
217
|
+
}
|
218
|
+
}
|
219
|
+
}
|
220
|
+
return recv.getRuntime().getFalse();
|
221
|
+
}
|
222
|
+
|
223
|
+
private static ResultSet intoResultSet(IRubyObject inp) {
|
224
|
+
return (ResultSet)((inp instanceof JavaObject ? ((JavaObject)inp) : (((JavaObject)(inp.getInstanceVariable("@java_object"))))).getValue());
|
225
|
+
}
|
226
|
+
|
227
|
+
public static IRubyObject set_connection(IRubyObject recv, IRubyObject conn) {
|
228
|
+
Connection c = (Connection)recv.dataGetStruct();
|
229
|
+
if(c != null) {
|
230
|
+
try {
|
231
|
+
c.close();
|
232
|
+
} catch(Exception e) {}
|
233
|
+
}
|
234
|
+
recv.setInstanceVariable("@connection",conn);
|
235
|
+
c = (Connection)(((JavaObject)conn.getInstanceVariable("@java_object")).getValue());
|
236
|
+
recv.dataWrapStruct(c);
|
237
|
+
return recv;
|
238
|
+
}
|
239
|
+
|
240
|
+
public static IRubyObject tables(IRubyObject recv, IRubyObject[] args) throws SQLException, IOException {
|
241
|
+
Ruby runtime = recv.getRuntime();
|
242
|
+
String catalog = null, schemapat = null, tablepat = null;
|
243
|
+
String[] types = new String[]{"TABLE"};
|
244
|
+
if (args != null) {
|
245
|
+
if (args.length > 0) {
|
246
|
+
catalog = convertToStringOrNull(args[0]);
|
247
|
+
}
|
248
|
+
if (args.length > 1) {
|
249
|
+
schemapat = convertToStringOrNull(args[1]);
|
250
|
+
}
|
251
|
+
if (args.length > 2) {
|
252
|
+
tablepat = convertToStringOrNull(args[2]);
|
253
|
+
}
|
254
|
+
if (args.length > 3) {
|
255
|
+
IRubyObject typearr = args[3];
|
256
|
+
if (typearr instanceof RubyArray) {
|
257
|
+
IRubyObject[] arr = ((RubyArray) typearr).toJavaArray();
|
258
|
+
types = new String[arr.length];
|
259
|
+
for (int i = 0; i < types.length; i++) {
|
260
|
+
types[i] = arr[i].toString();
|
261
|
+
}
|
262
|
+
} else {
|
263
|
+
types = new String[] {types.toString()};
|
264
|
+
}
|
265
|
+
}
|
266
|
+
}
|
267
|
+
while (true) {
|
268
|
+
Connection c = (Connection) recv.dataGetStruct();
|
269
|
+
ResultSet rs = null;
|
270
|
+
try {
|
271
|
+
DatabaseMetaData metadata = c.getMetaData();
|
272
|
+
String clzName = metadata.getClass().getName().toLowerCase();
|
273
|
+
boolean isOracle = clzName.indexOf("oracle") != -1 || clzName.indexOf("oci") != -1;
|
274
|
+
|
275
|
+
if(schemapat == null && isOracle) {
|
276
|
+
ResultSet schemas = metadata.getSchemas();
|
277
|
+
String username = metadata.getUserName();
|
278
|
+
while(schemas.next()) {
|
279
|
+
if(schemas.getString(1).equalsIgnoreCase(username)) {
|
280
|
+
schemapat = schemas.getString(1);
|
281
|
+
break;
|
282
|
+
}
|
283
|
+
}
|
284
|
+
schemas.close();
|
285
|
+
}
|
286
|
+
rs = metadata.getTables(catalog, schemapat, tablepat, types);
|
287
|
+
List arr = new ArrayList();
|
288
|
+
while (rs.next()) {
|
289
|
+
String name = rs.getString(3).toLowerCase();
|
290
|
+
if(!isOracle || !name.startsWith("bin$")) { //Handle stupid Oracle 10g RecycleBin feature
|
291
|
+
arr.add(runtime.newString(name));
|
292
|
+
}
|
293
|
+
}
|
294
|
+
return runtime.newArray(arr);
|
295
|
+
} catch (SQLException e) {
|
296
|
+
if(c.isClosed()) {
|
297
|
+
recv = recv.callMethod(recv.getRuntime().getCurrentContext(),"reconnect!");
|
298
|
+
if(!((Connection)recv.dataGetStruct()).isClosed()) {
|
299
|
+
continue;
|
300
|
+
}
|
301
|
+
}
|
302
|
+
throw wrap(recv, e);
|
303
|
+
} finally {
|
304
|
+
try {
|
305
|
+
rs.close();
|
306
|
+
} catch(Exception e) {}
|
307
|
+
}
|
308
|
+
|
309
|
+
}
|
310
|
+
}
|
311
|
+
|
312
|
+
public static IRubyObject native_database_types(IRubyObject recv) {
|
313
|
+
return recv.getInstanceVariable("@tps");
|
314
|
+
}
|
315
|
+
|
316
|
+
public static IRubyObject set_native_database_types(IRubyObject recv) throws SQLException, IOException {
|
317
|
+
Ruby runtime = recv.getRuntime();
|
318
|
+
ThreadContext ctx = runtime.getCurrentContext();
|
319
|
+
IRubyObject types = unmarshal_result_downcase(recv, ((Connection)recv.dataGetStruct()).getMetaData().getTypeInfo());
|
320
|
+
recv.setInstanceVariable("@native_types",
|
321
|
+
((RubyModule)(runtime.getModule("ActiveRecord").getConstant("ConnectionAdapters"))).
|
322
|
+
getConstant("JdbcTypeConverter").callMethod(ctx,"new", types).
|
323
|
+
callMethod(ctx, "choose_best_types"));
|
324
|
+
return runtime.getNil();
|
325
|
+
}
|
326
|
+
|
327
|
+
public static IRubyObject database_name(IRubyObject recv) throws SQLException {
|
328
|
+
String name = ((Connection)recv.dataGetStruct()).getCatalog();
|
329
|
+
if(null == name) {
|
330
|
+
name = ((Connection)recv.dataGetStruct()).getMetaData().getUserName();
|
331
|
+
if(null == name) {
|
332
|
+
name = "db1";
|
333
|
+
}
|
334
|
+
}
|
335
|
+
return recv.getRuntime().newString(name);
|
336
|
+
}
|
337
|
+
|
338
|
+
public static IRubyObject begin(IRubyObject recv) throws SQLException {
|
339
|
+
((Connection)recv.dataGetStruct()).setAutoCommit(false);
|
340
|
+
return recv.getRuntime().getNil();
|
341
|
+
}
|
342
|
+
|
343
|
+
public static IRubyObject commit(IRubyObject recv) throws SQLException {
|
344
|
+
try {
|
345
|
+
((Connection)recv.dataGetStruct()).commit();
|
346
|
+
return recv.getRuntime().getNil();
|
347
|
+
} finally {
|
348
|
+
((Connection)recv.dataGetStruct()).setAutoCommit(true);
|
349
|
+
}
|
350
|
+
}
|
351
|
+
|
352
|
+
public static IRubyObject rollback(IRubyObject recv) throws SQLException {
|
353
|
+
try {
|
354
|
+
((Connection)recv.dataGetStruct()).rollback();
|
355
|
+
return recv.getRuntime().getNil();
|
356
|
+
} finally {
|
357
|
+
((Connection)recv.dataGetStruct()).setAutoCommit(true);
|
358
|
+
}
|
359
|
+
}
|
360
|
+
|
361
|
+
public static IRubyObject columns_internal(IRubyObject recv, IRubyObject[] args) throws SQLException, IOException {
|
362
|
+
String table_name = args[0].convertToString().getUnicodeValue();
|
363
|
+
int tries = 10;
|
364
|
+
while(true) {
|
365
|
+
Connection c = (Connection)recv.dataGetStruct();
|
366
|
+
try {
|
367
|
+
DatabaseMetaData metadata = c.getMetaData();
|
368
|
+
String clzName = metadata.getClass().getName().toLowerCase();
|
369
|
+
boolean isDerby = clzName.indexOf("derby") != -1;
|
370
|
+
boolean isOracle = clzName.indexOf("oracle") != -1 || clzName.indexOf("oci") != -1;
|
371
|
+
String schemaName = null;
|
372
|
+
if(args.length>2) {
|
373
|
+
schemaName = args[2].toString();
|
374
|
+
}
|
375
|
+
if(metadata.storesUpperCaseIdentifiers()) {
|
376
|
+
table_name = table_name.toUpperCase();
|
377
|
+
} else if(metadata.storesLowerCaseIdentifiers()) {
|
378
|
+
table_name = table_name.toLowerCase();
|
379
|
+
}
|
380
|
+
if(schemaName == null && (isDerby || isOracle)) {
|
381
|
+
ResultSet schemas = metadata.getSchemas();
|
382
|
+
String username = metadata.getUserName();
|
383
|
+
while(schemas.next()) {
|
384
|
+
if(schemas.getString(1).equalsIgnoreCase(username)) {
|
385
|
+
schemaName = schemas.getString(1);
|
386
|
+
break;
|
387
|
+
}
|
388
|
+
}
|
389
|
+
schemas.close();
|
390
|
+
}
|
391
|
+
|
392
|
+
ResultSet results = metadata.getColumns(c.getCatalog(),schemaName,table_name,null);
|
393
|
+
return unmarshal_columns(recv, metadata, results);
|
394
|
+
} catch(SQLException e) {
|
395
|
+
if(c.isClosed()) {
|
396
|
+
recv = recv.callMethod(recv.getRuntime().getCurrentContext(),"reconnect!");
|
397
|
+
if(!((Connection)recv.dataGetStruct()).isClosed() && --tries > 0) {
|
398
|
+
continue;
|
399
|
+
}
|
400
|
+
}
|
401
|
+
throw wrap(recv, e);
|
402
|
+
}
|
403
|
+
}
|
404
|
+
}
|
405
|
+
|
406
|
+
private static final java.util.regex.Pattern HAS_SMALL = java.util.regex.Pattern.compile("[a-z]");
|
407
|
+
private static final java.util.regex.Pattern HAS_LARGE = java.util.regex.Pattern.compile("[A-Z]");
|
408
|
+
private static IRubyObject unmarshal_columns(IRubyObject recv, DatabaseMetaData metadata, ResultSet rs) throws SQLException, IOException {
|
409
|
+
try {
|
410
|
+
List columns = new ArrayList();
|
411
|
+
String clzName = metadata.getClass().getName().toLowerCase();
|
412
|
+
boolean isDerby = clzName.indexOf("derby") != -1;
|
413
|
+
boolean isOracle = clzName.indexOf("oracle") != -1 || clzName.indexOf("oci") != -1;
|
414
|
+
Ruby runtime = recv.getRuntime();
|
415
|
+
ThreadContext ctx = runtime.getCurrentContext();
|
416
|
+
|
417
|
+
RubyHash tps = ((RubyHash)(recv.callMethod(ctx, "adapter").callMethod(ctx,"native_database_types")));
|
418
|
+
|
419
|
+
IRubyObject jdbcCol = ((RubyModule)(runtime.getModule("ActiveRecord").getConstant("ConnectionAdapters"))).getConstant("JdbcColumn");
|
420
|
+
|
421
|
+
while(rs.next()) {
|
422
|
+
String column_name = rs.getString(4);
|
423
|
+
if(metadata.storesUpperCaseIdentifiers() && !HAS_SMALL.matcher(column_name).find()) {
|
424
|
+
column_name = column_name.toLowerCase();
|
425
|
+
}
|
426
|
+
|
427
|
+
String prec = rs.getString(7);
|
428
|
+
String scal = rs.getString(9);
|
429
|
+
int precision = -1;
|
430
|
+
int scale = -1;
|
431
|
+
if(prec != null) {
|
432
|
+
precision = Integer.parseInt(prec);
|
433
|
+
if(scal != null) {
|
434
|
+
scale = Integer.parseInt(scal);
|
435
|
+
}
|
436
|
+
}
|
437
|
+
String type = rs.getString(6);
|
438
|
+
if(prec != null && precision > 0) {
|
439
|
+
type += "(" + precision;
|
440
|
+
if(scal != null && scale > 0) {
|
441
|
+
type += "," + scale;
|
442
|
+
}
|
443
|
+
type += ")";
|
444
|
+
}
|
445
|
+
String def = rs.getString(13);
|
446
|
+
IRubyObject _def;
|
447
|
+
if(def == null || (isOracle && def.toLowerCase().trim().equals("null"))) {
|
448
|
+
_def = runtime.getNil();
|
449
|
+
} else {
|
450
|
+
if(isOracle) {
|
451
|
+
def = def.trim();
|
452
|
+
}
|
453
|
+
if((isDerby || isOracle) && def.length() > 0 && def.charAt(0) == '\'') {
|
454
|
+
def = def.substring(1, def.length()-1);
|
455
|
+
}
|
456
|
+
_def = runtime.newString(def);
|
457
|
+
}
|
458
|
+
IRubyObject c = jdbcCol.callMethod(ctx,"new", new IRubyObject[]{recv.getInstanceVariable("@config"), runtime.newString(column_name),
|
459
|
+
_def, runtime.newString(type),
|
460
|
+
runtime.newBoolean(!rs.getString(18).trim().equals("NO"))});
|
461
|
+
columns.add(c);
|
462
|
+
|
463
|
+
IRubyObject tp = (IRubyObject)tps.fastARef(c.callMethod(ctx,"type"));
|
464
|
+
if(tp != null && !tp.isNil() && tp.callMethod(ctx,"[]",runtime.newSymbol("limit")).isNil()) {
|
465
|
+
c.callMethod(ctx,"limit=", runtime.getNil());
|
466
|
+
if(!c.callMethod(ctx,"type").equals(runtime.newSymbol("decimal"))) {
|
467
|
+
c.callMethod(ctx,"precision=", runtime.getNil());
|
468
|
+
}
|
469
|
+
}
|
470
|
+
}
|
471
|
+
return runtime.newArray(columns);
|
472
|
+
} finally {
|
473
|
+
try {
|
474
|
+
rs.close();
|
475
|
+
} catch(Exception e) {}
|
476
|
+
}
|
477
|
+
}
|
478
|
+
|
479
|
+
public static IRubyObject primary_keys(IRubyObject recv, IRubyObject _table_name) throws SQLException {
|
480
|
+
Connection c = (Connection)recv.dataGetStruct();
|
481
|
+
DatabaseMetaData metadata = c.getMetaData();
|
482
|
+
String table_name = _table_name.toString();
|
483
|
+
if(metadata.storesUpperCaseIdentifiers()) {
|
484
|
+
table_name = table_name.toUpperCase();
|
485
|
+
} else if(metadata.storesLowerCaseIdentifiers()) {
|
486
|
+
table_name = table_name.toLowerCase();
|
487
|
+
}
|
488
|
+
ResultSet result_set = metadata.getPrimaryKeys(null,null,table_name);
|
489
|
+
List keyNames = new ArrayList();
|
490
|
+
Ruby runtime = recv.getRuntime();
|
491
|
+
while(result_set.next()) {
|
492
|
+
String s1 = result_set.getString(4);
|
493
|
+
if(metadata.storesUpperCaseIdentifiers() && !HAS_SMALL.matcher(s1).find()) {
|
494
|
+
s1 = s1.toLowerCase();
|
495
|
+
}
|
496
|
+
keyNames.add(runtime.newString(s1));
|
497
|
+
}
|
498
|
+
|
499
|
+
try {
|
500
|
+
result_set.close();
|
501
|
+
} catch(Exception e) {}
|
502
|
+
|
503
|
+
return runtime.newArray(keyNames);
|
504
|
+
}
|
505
|
+
|
506
|
+
public static IRubyObject execute_id_insert(IRubyObject recv, IRubyObject sql, IRubyObject id) throws SQLException {
|
507
|
+
Connection c = (Connection)recv.dataGetStruct();
|
508
|
+
PreparedStatement ps = c.prepareStatement(sql.convertToString().getUnicodeValue());
|
509
|
+
try {
|
510
|
+
ps.setLong(1,RubyNumeric.fix2long(id));
|
511
|
+
ps.executeUpdate();
|
512
|
+
} finally {
|
513
|
+
ps.close();
|
514
|
+
}
|
515
|
+
return id;
|
516
|
+
}
|
517
|
+
|
518
|
+
private static RuntimeException wrap(IRubyObject recv, Throwable exception) {
|
519
|
+
return recv.getRuntime().newArgumentError(exception.getMessage());
|
520
|
+
}
|
521
|
+
|
522
|
+
public static IRubyObject execute_update(IRubyObject recv, IRubyObject sql) throws SQLException {
|
523
|
+
int tries = 10;
|
524
|
+
while(true) {
|
525
|
+
Connection c = (Connection)recv.dataGetStruct();
|
526
|
+
Statement stmt = null;
|
527
|
+
try {
|
528
|
+
stmt = c.createStatement();
|
529
|
+
return recv.getRuntime().newFixnum(stmt.executeUpdate(sql.convertToString().getUnicodeValue()));
|
530
|
+
} catch(SQLException e) {
|
531
|
+
if(c.isClosed()) {
|
532
|
+
recv = recv.callMethod(recv.getRuntime().getCurrentContext(),"reconnect!");
|
533
|
+
if(!((Connection)recv.dataGetStruct()).isClosed() && --tries > 0) {
|
534
|
+
continue;
|
535
|
+
}
|
536
|
+
}
|
537
|
+
|
538
|
+
throw wrap(recv, e);
|
539
|
+
} finally {
|
540
|
+
if(null != stmt) {
|
541
|
+
try {
|
542
|
+
stmt.close();
|
543
|
+
} catch(Exception e) {}
|
544
|
+
}
|
545
|
+
}
|
546
|
+
}
|
547
|
+
}
|
548
|
+
|
549
|
+
public static IRubyObject execute_query(IRubyObject recv, IRubyObject[] args) throws SQLException, IOException {
|
550
|
+
IRubyObject sql = args[0];
|
551
|
+
int maxrows = 0;
|
552
|
+
if(args.length > 1) {
|
553
|
+
maxrows = RubyNumeric.fix2int(args[1]);
|
554
|
+
}
|
555
|
+
int tries = 10;
|
556
|
+
while(true) {
|
557
|
+
Connection c = (Connection)recv.dataGetStruct();
|
558
|
+
Statement stmt = null;
|
559
|
+
try {
|
560
|
+
stmt = c.createStatement();
|
561
|
+
stmt.setMaxRows(maxrows);
|
562
|
+
return unmarshal_result(recv, stmt.executeQuery(sql.convertToString().getUnicodeValue()));
|
563
|
+
} catch(SQLException e) {
|
564
|
+
if(c.isClosed()) {
|
565
|
+
recv = recv.callMethod(recv.getRuntime().getCurrentContext(),"reconnect!");
|
566
|
+
if(!((Connection)recv.dataGetStruct()).isClosed() && --tries > 0) {
|
567
|
+
continue;
|
568
|
+
}
|
569
|
+
}
|
570
|
+
throw wrap(recv, e);
|
571
|
+
} finally {
|
572
|
+
if(null != stmt) {
|
573
|
+
try {
|
574
|
+
stmt.close();
|
575
|
+
} catch(Exception e) {}
|
576
|
+
}
|
577
|
+
}
|
578
|
+
}
|
579
|
+
}
|
580
|
+
|
581
|
+
public static IRubyObject execute_insert(IRubyObject recv, IRubyObject sql) throws SQLException {
|
582
|
+
int tries = 10;
|
583
|
+
while(true) {
|
584
|
+
Connection c = (Connection)recv.dataGetStruct();
|
585
|
+
Statement stmt = null;
|
586
|
+
try {
|
587
|
+
stmt = c.createStatement();
|
588
|
+
stmt.executeUpdate(sql.convertToString().getUnicodeValue(), Statement.RETURN_GENERATED_KEYS);
|
589
|
+
return unmarshal_id_result(recv.getRuntime(), stmt.getGeneratedKeys());
|
590
|
+
} catch(SQLException e) {
|
591
|
+
if(c.isClosed()) {
|
592
|
+
recv = recv.callMethod(recv.getRuntime().getCurrentContext(),"reconnect!");
|
593
|
+
if(!((Connection)recv.dataGetStruct()).isClosed() && --tries > 0) {
|
594
|
+
continue;
|
595
|
+
}
|
596
|
+
}
|
597
|
+
throw wrap(recv, e);
|
598
|
+
} finally {
|
599
|
+
if(null != stmt) {
|
600
|
+
try {
|
601
|
+
stmt.close();
|
602
|
+
} catch(Exception e) {}
|
603
|
+
}
|
604
|
+
}
|
605
|
+
}
|
606
|
+
}
|
607
|
+
|
608
|
+
public static IRubyObject unmarshal_result_downcase(IRubyObject recv, ResultSet rs) throws SQLException, IOException {
|
609
|
+
List results = new ArrayList();
|
610
|
+
Ruby runtime = recv.getRuntime();
|
611
|
+
try {
|
612
|
+
ResultSetMetaData metadata = rs.getMetaData();
|
613
|
+
int col_count = metadata.getColumnCount();
|
614
|
+
IRubyObject[] col_names = new IRubyObject[col_count];
|
615
|
+
int[] col_types = new int[col_count];
|
616
|
+
int[] col_scale = new int[col_count];
|
617
|
+
|
618
|
+
for(int i=0;i<col_count;i++) {
|
619
|
+
col_names[i] = runtime.newString(metadata.getColumnName(i+1).toLowerCase());
|
620
|
+
col_types[i] = metadata.getColumnType(i+1);
|
621
|
+
col_scale[i] = metadata.getScale(i+1);
|
622
|
+
}
|
623
|
+
|
624
|
+
while(rs.next()) {
|
625
|
+
RubyHash row = RubyHash.newHash(runtime);
|
626
|
+
for(int i=0;i<col_count;i++) {
|
627
|
+
row.aset(col_names[i], jdbc_to_ruby(runtime, i+1, col_types[i], col_scale[i], rs));
|
628
|
+
}
|
629
|
+
results.add(row);
|
630
|
+
}
|
631
|
+
} finally {
|
632
|
+
try {
|
633
|
+
rs.close();
|
634
|
+
} catch(Exception e) {}
|
635
|
+
}
|
636
|
+
|
637
|
+
return runtime.newArray(results);
|
638
|
+
}
|
639
|
+
|
640
|
+
public static IRubyObject unmarshal_result(IRubyObject recv, ResultSet rs) throws SQLException, IOException {
|
641
|
+
Ruby runtime = recv.getRuntime();
|
642
|
+
List results = new ArrayList();
|
643
|
+
try {
|
644
|
+
ResultSetMetaData metadata = rs.getMetaData();
|
645
|
+
boolean storesUpper = rs.getStatement().getConnection().getMetaData().storesUpperCaseIdentifiers();
|
646
|
+
int col_count = metadata.getColumnCount();
|
647
|
+
IRubyObject[] col_names = new IRubyObject[col_count];
|
648
|
+
int[] col_types = new int[col_count];
|
649
|
+
int[] col_scale = new int[col_count];
|
650
|
+
|
651
|
+
for(int i=0;i<col_count;i++) {
|
652
|
+
String s1 = metadata.getColumnName(i+1);
|
653
|
+
if(storesUpper && !HAS_SMALL.matcher(s1).find()) {
|
654
|
+
s1 = s1.toLowerCase();
|
655
|
+
}
|
656
|
+
col_names[i] = runtime.newString(s1);
|
657
|
+
col_types[i] = metadata.getColumnType(i+1);
|
658
|
+
col_scale[i] = metadata.getScale(i+1);
|
659
|
+
}
|
660
|
+
|
661
|
+
while(rs.next()) {
|
662
|
+
RubyHash row = RubyHash.newHash(runtime);
|
663
|
+
for(int i=0;i<col_count;i++) {
|
664
|
+
row.aset(col_names[i], jdbc_to_ruby(runtime, i+1, col_types[i], col_scale[i], rs));
|
665
|
+
}
|
666
|
+
results.add(row);
|
667
|
+
}
|
668
|
+
} finally {
|
669
|
+
try {
|
670
|
+
rs.close();
|
671
|
+
} catch(Exception e) {}
|
672
|
+
}
|
673
|
+
return runtime.newArray(results);
|
674
|
+
}
|
675
|
+
|
676
|
+
public static IRubyObject unmarshal_result(IRubyObject recv, IRubyObject resultset, Block row_filter) throws SQLException, IOException {
|
677
|
+
Ruby runtime = recv.getRuntime();
|
678
|
+
ResultSet rs = intoResultSet(resultset);
|
679
|
+
List results = new ArrayList();
|
680
|
+
try {
|
681
|
+
ResultSetMetaData metadata = rs.getMetaData();
|
682
|
+
int col_count = metadata.getColumnCount();
|
683
|
+
IRubyObject[] col_names = new IRubyObject[col_count];
|
684
|
+
int[] col_types = new int[col_count];
|
685
|
+
int[] col_scale = new int[col_count];
|
686
|
+
|
687
|
+
for(int i=0;i<col_count;i++) {
|
688
|
+
col_names[i] = runtime.newString(metadata.getColumnName(i+1));
|
689
|
+
col_types[i] = metadata.getColumnType(i+1);
|
690
|
+
col_scale[i] = metadata.getScale(i+1);
|
691
|
+
}
|
692
|
+
|
693
|
+
if(row_filter.isGiven()) {
|
694
|
+
while(rs.next()) {
|
695
|
+
if(row_filter.yield(runtime.getCurrentContext(),resultset).isTrue()) {
|
696
|
+
RubyHash row = RubyHash.newHash(runtime);
|
697
|
+
for(int i=0;i<col_count;i++) {
|
698
|
+
row.aset(col_names[i], jdbc_to_ruby(runtime, i+1, col_types[i], col_scale[i], rs));
|
699
|
+
}
|
700
|
+
results.add(row);
|
701
|
+
}
|
702
|
+
}
|
703
|
+
} else {
|
704
|
+
while(rs.next()) {
|
705
|
+
RubyHash row = RubyHash.newHash(runtime);
|
706
|
+
for(int i=0;i<col_count;i++) {
|
707
|
+
row.aset(col_names[i], jdbc_to_ruby(runtime, i+1, col_types[i], col_scale[i], rs));
|
708
|
+
}
|
709
|
+
results.add(row);
|
710
|
+
}
|
711
|
+
}
|
712
|
+
|
713
|
+
} finally {
|
714
|
+
try {
|
715
|
+
rs.close();
|
716
|
+
} catch(Exception e) {}
|
717
|
+
}
|
718
|
+
|
719
|
+
return runtime.newArray(results);
|
720
|
+
}
|
721
|
+
|
722
|
+
private static IRubyObject jdbc_to_ruby(Ruby runtime, int row, int type, int scale, ResultSet rs) throws SQLException, IOException {
|
723
|
+
int n;
|
724
|
+
switch(type) {
|
725
|
+
case Types.BINARY:
|
726
|
+
case Types.BLOB:
|
727
|
+
case Types.LONGVARBINARY:
|
728
|
+
case Types.VARBINARY:
|
729
|
+
InputStream is = rs.getBinaryStream(row);
|
730
|
+
if(is == null || rs.wasNull()) {
|
731
|
+
return runtime.getNil();
|
732
|
+
}
|
733
|
+
ByteList str = new ByteList(2048);
|
734
|
+
byte[] buf = new byte[2048];
|
735
|
+
while((n = is.read(buf)) != -1) {
|
736
|
+
str.append(buf, 0, n);
|
737
|
+
}
|
738
|
+
is.close();
|
739
|
+
return runtime.newString(str);
|
740
|
+
case Types.LONGVARCHAR:
|
741
|
+
case Types.CLOB:
|
742
|
+
Reader rss = rs.getCharacterStream(row);
|
743
|
+
if(rss == null || rs.wasNull()) {
|
744
|
+
return runtime.getNil();
|
745
|
+
}
|
746
|
+
StringBuffer str2 = new StringBuffer(2048);
|
747
|
+
char[] cuf = new char[2048];
|
748
|
+
while((n = rss.read(cuf)) != -1) {
|
749
|
+
str2.append(cuf, 0, n);
|
750
|
+
}
|
751
|
+
rss.close();
|
752
|
+
return RubyString.newUnicodeString(runtime, str2.toString());
|
753
|
+
case Types.TIMESTAMP:
|
754
|
+
Timestamp time = rs.getTimestamp(row);
|
755
|
+
if (time == null || rs.wasNull()) {
|
756
|
+
return runtime.getNil();
|
757
|
+
}
|
758
|
+
String sttr = time.toString();
|
759
|
+
if(sttr.endsWith(" 00:00:00.0")) {
|
760
|
+
sttr = sttr.substring(0, sttr.length()-(" 00:00:00.0".length()));
|
761
|
+
}
|
762
|
+
return RubyString.newUnicodeString(runtime, sttr);
|
763
|
+
default:
|
764
|
+
String vs = rs.getString(row);
|
765
|
+
if(vs == null || rs.wasNull()) {
|
766
|
+
return runtime.getNil();
|
767
|
+
}
|
768
|
+
|
769
|
+
return RubyString.newUnicodeString(runtime, vs);
|
770
|
+
}
|
771
|
+
}
|
772
|
+
|
773
|
+
public static IRubyObject unmarshal_id_result(Ruby runtime, ResultSet rs) throws SQLException {
|
774
|
+
try {
|
775
|
+
if(rs.next()) {
|
776
|
+
if(rs.getMetaData().getColumnCount() > 0) {
|
777
|
+
return runtime.newFixnum(rs.getLong(1));
|
778
|
+
}
|
779
|
+
}
|
780
|
+
return runtime.getNil();
|
781
|
+
} finally {
|
782
|
+
try {
|
783
|
+
rs.close();
|
784
|
+
} catch(Exception e) {}
|
785
|
+
}
|
786
|
+
}
|
787
|
+
|
788
|
+
private static String convertToStringOrNull(IRubyObject obj) {
|
789
|
+
if (obj.isNil()) {
|
790
|
+
return null;
|
791
|
+
}
|
792
|
+
return obj.toString();
|
793
|
+
}
|
794
|
+
|
795
|
+
private static int getTypeValueFor(Ruby runtime, IRubyObject type) throws SQLException {
|
796
|
+
if(!(type instanceof RubySymbol)) {
|
797
|
+
type = type.callMethod(runtime.getCurrentContext(),"type");
|
798
|
+
}
|
799
|
+
if(type == runtime.newSymbol("string")) {
|
800
|
+
return Types.VARCHAR;
|
801
|
+
} else if(type == runtime.newSymbol("text")) {
|
802
|
+
return Types.CLOB;
|
803
|
+
} else if(type == runtime.newSymbol("integer")) {
|
804
|
+
return Types.INTEGER;
|
805
|
+
} else if(type == runtime.newSymbol("decimal")) {
|
806
|
+
return Types.DECIMAL;
|
807
|
+
} else if(type == runtime.newSymbol("float")) {
|
808
|
+
return Types.FLOAT;
|
809
|
+
} else if(type == runtime.newSymbol("datetime")) {
|
810
|
+
return Types.TIMESTAMP;
|
811
|
+
} else if(type == runtime.newSymbol("timestamp")) {
|
812
|
+
return Types.TIMESTAMP;
|
813
|
+
} else if(type == runtime.newSymbol("time")) {
|
814
|
+
return Types.TIME;
|
815
|
+
} else if(type == runtime.newSymbol("date")) {
|
816
|
+
return Types.DATE;
|
817
|
+
} else if(type == runtime.newSymbol("binary")) {
|
818
|
+
return Types.BLOB;
|
819
|
+
} else if(type == runtime.newSymbol("boolean")) {
|
820
|
+
return Types.BOOLEAN;
|
821
|
+
} else {
|
822
|
+
return -1;
|
823
|
+
}
|
824
|
+
}
|
825
|
+
|
826
|
+
private final static DateFormat FORMAT = new SimpleDateFormat("%y-%M-%d %H:%m:%s");
|
827
|
+
|
828
|
+
private static void setValue(PreparedStatement ps, int index, Ruby runtime, IRubyObject value, IRubyObject type) throws SQLException {
|
829
|
+
final int tp = getTypeValueFor(runtime, type);
|
830
|
+
if(value.isNil()) {
|
831
|
+
ps.setNull(index, tp);
|
832
|
+
return;
|
833
|
+
}
|
834
|
+
|
835
|
+
switch(tp) {
|
836
|
+
case Types.VARCHAR:
|
837
|
+
case Types.CLOB:
|
838
|
+
ps.setString(index, RubyString.objAsString(value).toString());
|
839
|
+
break;
|
840
|
+
case Types.INTEGER:
|
841
|
+
ps.setLong(index, RubyNumeric.fix2long(value));
|
842
|
+
break;
|
843
|
+
case Types.FLOAT:
|
844
|
+
ps.setDouble(index, ((RubyNumeric)value).getDoubleValue());
|
845
|
+
break;
|
846
|
+
case Types.TIMESTAMP:
|
847
|
+
case Types.TIME:
|
848
|
+
case Types.DATE:
|
849
|
+
if(!(value instanceof RubyTime)) {
|
850
|
+
try {
|
851
|
+
Date dd = FORMAT.parse(RubyString.objAsString(value).toString());
|
852
|
+
ps.setTimestamp(index, new java.sql.Timestamp(dd.getTime()), Calendar.getInstance());
|
853
|
+
} catch(Exception e) {
|
854
|
+
ps.setString(index, RubyString.objAsString(value).toString());
|
855
|
+
}
|
856
|
+
} else {
|
857
|
+
RubyTime rubyTime = (RubyTime) value;
|
858
|
+
java.util.Date date = rubyTime.getJavaDate();
|
859
|
+
long millis = date.getTime();
|
860
|
+
long micros = rubyTime.microseconds() - millis / 1000;
|
861
|
+
java.sql.Timestamp ts = new java.sql.Timestamp(millis);
|
862
|
+
java.util.Calendar cal = Calendar.getInstance();
|
863
|
+
cal.setTime(date);
|
864
|
+
ts.setNanos((int)(micros * 1000));
|
865
|
+
ps.setTimestamp(index, ts, cal);
|
866
|
+
}
|
867
|
+
break;
|
868
|
+
case Types.BOOLEAN:
|
869
|
+
ps.setBoolean(index, value.isTrue());
|
870
|
+
break;
|
871
|
+
default: throw new RuntimeException("type " + type + " not supported in _bind yet");
|
872
|
+
}
|
873
|
+
}
|
874
|
+
|
875
|
+
private static void setValuesOnPS(PreparedStatement ps, Ruby runtime, IRubyObject values, IRubyObject types) throws SQLException {
|
876
|
+
RubyArray vals = (RubyArray)values;
|
877
|
+
RubyArray tps = (RubyArray)types;
|
878
|
+
|
879
|
+
for(int i=0, j=vals.getLength(); i<j; i++) {
|
880
|
+
setValue(ps, i+1, runtime, vals.eltInternal(i), tps.eltInternal(i));
|
881
|
+
}
|
882
|
+
}
|
883
|
+
|
884
|
+
/*
|
885
|
+
* sql, values, types, name = nil, pk = nil, id_value = nil, sequence_name = nil
|
886
|
+
*/
|
887
|
+
public static IRubyObject insert_bind(IRubyObject recv, IRubyObject[] args) throws SQLException {
|
888
|
+
Ruby runtime = recv.getRuntime();
|
889
|
+
Arity.checkArgumentCount(runtime, args, 3, 7);
|
890
|
+
Connection c = (Connection)recv.dataGetStruct();
|
891
|
+
PreparedStatement ps = null;
|
892
|
+
try {
|
893
|
+
ps = c.prepareStatement(RubyString.objAsString(args[0]).toString(), Statement.RETURN_GENERATED_KEYS);
|
894
|
+
setValuesOnPS(ps, runtime, args[1], args[2]);
|
895
|
+
ps.executeUpdate();
|
896
|
+
return unmarshal_id_result(runtime, ps.getGeneratedKeys());
|
897
|
+
} finally {
|
898
|
+
try {
|
899
|
+
ps.close();
|
900
|
+
} catch(Exception e) {}
|
901
|
+
}
|
902
|
+
}
|
903
|
+
|
904
|
+
/*
|
905
|
+
* sql, values, types, name = nil
|
906
|
+
*/
|
907
|
+
public static IRubyObject update_bind(IRubyObject recv, IRubyObject[] args) throws SQLException {
|
908
|
+
Ruby runtime = recv.getRuntime();
|
909
|
+
Arity.checkArgumentCount(runtime, args, 3, 4);
|
910
|
+
Connection c = (Connection)recv.dataGetStruct();
|
911
|
+
PreparedStatement ps = null;
|
912
|
+
try {
|
913
|
+
ps = c.prepareStatement(RubyString.objAsString(args[0]).toString());
|
914
|
+
setValuesOnPS(ps, runtime, args[1], args[2]);
|
915
|
+
ps.executeUpdate();
|
916
|
+
} finally {
|
917
|
+
try {
|
918
|
+
ps.close();
|
919
|
+
} catch(Exception e) {}
|
920
|
+
}
|
921
|
+
return runtime.getNil();
|
922
|
+
}
|
923
|
+
|
924
|
+
|
925
|
+
private final static String LOB_UPDATE = "UPDATE ? WHERE ";
|
926
|
+
|
927
|
+
/*
|
928
|
+
* (is binary?, colname, tablename, primary key, id, value)
|
929
|
+
*/
|
930
|
+
public static IRubyObject write_large_object(IRubyObject recv, IRubyObject[] args) throws SQLException, IOException {
|
931
|
+
Ruby runtime = recv.getRuntime();
|
932
|
+
Arity.checkArgumentCount(runtime, args, 6, 6);
|
933
|
+
Connection c = (Connection)recv.dataGetStruct();
|
934
|
+
String sql = "UPDATE " + args[2].toString() + " SET " + args[1].toString() + " = ? WHERE " + args[3] + "=" + args[4];
|
935
|
+
PreparedStatement ps = null;
|
936
|
+
try {
|
937
|
+
ByteList outp = RubyString.objAsString(args[5]).getByteList();
|
938
|
+
ps = c.prepareStatement(sql);
|
939
|
+
if(args[0].isTrue()) { // binary
|
940
|
+
ps.setBinaryStream(1,new ByteArrayInputStream(outp.bytes, outp.begin, outp.realSize), outp.realSize);
|
941
|
+
} else { // clob
|
942
|
+
String ss = outp.toString();
|
943
|
+
ps.setCharacterStream(1,new StringReader(ss), ss.length());
|
944
|
+
}
|
945
|
+
ps.executeUpdate();
|
946
|
+
} finally {
|
947
|
+
try {
|
948
|
+
ps.close();
|
949
|
+
} catch(Exception e) {}
|
950
|
+
}
|
951
|
+
return runtime.getNil();
|
952
|
+
}
|
953
|
+
}
|