activerecord-jdbc-adapter 0.6 → 0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,4 +3,5 @@ require 'rubygems'
3
3
  require 'models/auto_id'
4
4
  require 'models/entry'
5
5
  require 'simple'
6
+ require 'has_many_through'
6
7
  require 'test/unit'
@@ -11,3 +11,7 @@ require 'db/mysql'
11
11
  class MysqlSimpleTest < Test::Unit::TestCase
12
12
  include SimpleTestMethods
13
13
  end
14
+
15
+ class MysqlHasManyThroughTest < Test::Unit::TestCase
16
+ include HasManyThroughMethods
17
+ end
@@ -1,3 +1,5 @@
1
+ ActiveRecord::Schema.verbose = false
2
+
1
3
  module MigrationSetup
2
4
  def setup
3
5
  CreateEntries.up
@@ -110,9 +112,42 @@ module SimpleTestMethods
110
112
 
111
113
  def test_invalid
112
114
  e = Entry.new(:title => @title, :content => @content, :rating => ' ')
113
- p e.valid?
115
+ assert e.valid?
116
+ end
117
+
118
+ def test_reconnect
119
+ assert_equal 1, Entry.count
120
+ @connection.reconnect!
121
+ assert_equal 1, Entry.count
122
+ end
123
+
124
+ def test_connection_valid
125
+ assert_raises(ActiveRecord::ActiveRecordError) do
126
+ @connection.raw_connection.with_connection_retry_guard do |c|
127
+ begin
128
+ stmt = c.createStatement
129
+ stmt.execute "bogus sql"
130
+ ensure
131
+ stmt.close rescue nil
132
+ end
133
+ end
134
+ end
114
135
  end
115
136
 
137
+ def test_disconnect
138
+ assert_equal 1, Entry.count
139
+ ActiveRecord::Base.clear_active_connections!
140
+ assert !ActiveRecord::Base.connected?
141
+ assert_equal 1, Entry.count
142
+ assert ActiveRecord::Base.connected?
143
+ end
144
+
145
+ class Animal < ActiveRecord::Base; end
146
+ def test_fetching_columns_for_nonexistent_table_should_raise
147
+ assert_raises(ActiveRecord::ActiveRecordError) do
148
+ Animal.columns
149
+ end
150
+ end
116
151
  end
117
152
 
118
153
  module MultibyteTestMethods
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
3
3
  specification_version: 1
4
4
  name: activerecord-jdbc-adapter
5
5
  version: !ruby/object:Gem::Version
6
- version: "0.6"
7
- date: 2007-12-13 00:00:00 -08:00
6
+ version: "0.7"
7
+ date: 2007-12-15 00:00:00 -06:00
8
8
  summary: JDBC adapter for ActiveRecord, for use within JRuby on Rails.
9
9
  require_paths:
10
10
  - lib
@@ -55,7 +55,7 @@ files:
55
55
  - lib/jdbc_adapter/missing_functionality_helper.rb
56
56
  - lib/jdbc_adapter/version.rb
57
57
  - lib/jdbc_adapter.rb
58
- - lib/jdbc_adapter_internal.jar
58
+ - lib/jdbc_adapter/jdbc_adapter_internal.jar
59
59
  - test/activerecord/connection_adapters/type_conversion_test.rb
60
60
  - test/activerecord/connections/native_jdbc_mysql/connection.rb
61
61
  - test/db/derby.rb
@@ -70,6 +70,7 @@ files:
70
70
  - test/derby_simple_test.rb
71
71
  - test/generic_jdbc_connection_test.rb
72
72
  - test/h2_simple_test.rb
73
+ - test/has_many_through.rb
73
74
  - test/hsqldb_simple_test.rb
74
75
  - test/jdbc_adapter/jdbc_db2_test.rb
75
76
  - test/jdbc_common.rb
@@ -90,9 +91,11 @@ files:
90
91
  - test/postgres_simple_test.rb
91
92
  - test/simple.rb
92
93
  - lib/tasks/jdbc_databases.rake
93
- - src/java/JdbcAdapterInternalService.java
94
- - src/java/JDBCDerbySpec.java
95
- - src/java/JDBCMySQLSpec.java
94
+ - src/java/jdbc_adapter/JdbcAdapterInternalService.java
95
+ - src/java/jdbc_adapter/JdbcConnectionFactory.java
96
+ - src/java/jdbc_adapter/JdbcDerbySpec.java
97
+ - src/java/jdbc_adapter/JdbcMySQLSpec.java
98
+ - src/java/jdbc_adapter/SQLBlock.java
96
99
  test_files: []
97
100
 
98
101
  rdoc_options:
@@ -1,953 +0,0 @@
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
- }