activerecord-jdbc-adapter 0.6 → 0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
- }