activerecord-jdbc-adapter 0.6 → 0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -337,6 +337,7 @@ module ::JdbcSpec
337
337
 
338
338
  # For DDL it appears you can quote "" column names, but in queries (like insert it errors out?)
339
339
  def quote_column_name(name) #:nodoc:
340
+ name = name.to_s
340
341
  if /^references$/i =~ name
341
342
  %Q{"#{name.upcase}"}
342
343
  elsif /[A-Z]/ =~ name && /[a-z]/ =~ name
@@ -134,7 +134,7 @@ module ::JdbcSpec
134
134
  end
135
135
 
136
136
  def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
137
- log_no_bench(sql,name) do
137
+ log(sql,name) do
138
138
  @connection.execute_update(sql)
139
139
  end
140
140
  table = sql.split(" ", 4)[2]
@@ -7,13 +7,14 @@ module ::JdbcSpec
7
7
  warn "AR-JDBC MySQL on JRuby does not support sockets"
8
8
  end
9
9
  config[:port] ||= 3306
10
- config[:url] ||= "jdbc:mysql://#{config[:host]}:#{config[:port]}/#{config[:database]}?zeroDateTimeBehavior=convertToNull&jdbcCompliantTruncation=false&useUnicode=true&characterEncoding=utf8"
10
+ config[:url] ||= "jdbc:mysql://#{config[:host]}:#{config[:port]}/#{config[:database]}?#{MySQL::URL_OPTIONS}"
11
11
  config[:driver] = "com.mysql.jdbc.Driver"
12
12
  jdbc_connection(config)
13
13
  end
14
14
  end
15
15
 
16
16
  module MySQL
17
+ URL_OPTIONS = "zeroDateTimeBehavior=convertToNull&jdbcCompliantTruncation=false&useUnicode=true&characterEncoding=utf8"
17
18
  def self.column_selector
18
19
  [/mysql/i, lambda {|cfg,col| col.extend(::JdbcSpec::MySQL::Column)}]
19
20
  end
@@ -80,6 +81,10 @@ module ::JdbcSpec
80
81
  def quote_column_name(name) #:nodoc:
81
82
  "`#{name}`"
82
83
  end
84
+
85
+ def quote_table_name(name) #:nodoc:
86
+ quote_column_name(name).gsub('.', '`.`')
87
+ end
83
88
 
84
89
  def quoted_true
85
90
  "1"
@@ -89,6 +94,25 @@ module ::JdbcSpec
89
94
  "0"
90
95
  end
91
96
 
97
+ def begin_db_transaction #:nodoc:
98
+ @connection.begin
99
+ rescue Exception
100
+ # Transactions aren't supported
101
+ end
102
+
103
+ def commit_db_transaction #:nodoc:
104
+ @connection.commit
105
+ rescue Exception
106
+ # Transactions aren't supported
107
+ end
108
+
109
+ def rollback_db_transaction #:nodoc:
110
+ @connection.rollback
111
+ rescue Exception
112
+ # Transactions aren't supported
113
+ end
114
+
115
+
92
116
  # SCHEMA STATEMENTS ========================================
93
117
 
94
118
  def structure_dump #:nodoc:
@@ -100,7 +124,7 @@ module ::JdbcSpec
100
124
 
101
125
  select_all(sql).inject("") do |structure, table|
102
126
  table.delete('Table_type')
103
- structure += select_one("SHOW CREATE TABLE #{table.to_a.first.last}")["Create Table"] + ";\n\n"
127
+ structure += select_one("SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}")["Create Table"] + ";\n\n"
104
128
  end
105
129
  end
106
130
 
@@ -126,28 +150,32 @@ module ::JdbcSpec
126
150
  end
127
151
 
128
152
  def rename_table(name, new_name)
129
- execute "RENAME TABLE #{name} TO #{new_name}"
153
+ execute "RENAME TABLE #{quote_table_name(name)} TO #{quote_table_name(new_name)}"
130
154
  end
131
155
 
132
156
  def change_column_default(table_name, column_name, default) #:nodoc:
133
- current_type = select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["Type"]
157
+ current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
134
158
 
135
- execute("ALTER TABLE #{table_name} CHANGE #{column_name} #{column_name} #{current_type} DEFAULT #{quote(default)}")
159
+ execute("ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{current_type} DEFAULT #{quote(default)}")
136
160
  end
137
161
 
138
162
  def change_column(table_name, column_name, type, options = {}) #:nodoc:
139
- unless options.include?(:default) && !(options[:null] == false && options[:default].nil?)
140
- options[:default] = select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["Default"]
163
+ unless options_include_default?(options)
164
+ if column = columns(table_name).find { |c| c.name == column_name.to_s }
165
+ options[:default] = column.default
166
+ else
167
+ raise "No such column: #{table_name}.#{column_name}"
168
+ end
141
169
  end
142
-
143
- change_column_sql = "ALTER TABLE #{table_name} CHANGE #{column_name} #{column_name} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
170
+
171
+ change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
144
172
  add_column_options!(change_column_sql, options)
145
173
  execute(change_column_sql)
146
174
  end
147
-
175
+
148
176
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
149
- current_type = select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["Type"]
150
- execute "ALTER TABLE #{table_name} CHANGE #{column_name} #{new_column_name} #{current_type}"
177
+ current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
178
+ execute "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_table_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
151
179
  end
152
180
 
153
181
  def add_limit_offset!(sql, options) #:nodoc:
@@ -160,9 +188,23 @@ module ::JdbcSpec
160
188
  end
161
189
  end
162
190
 
191
+ def show_variable(var)
192
+ res = execute("show variables like '#{var}'")
193
+ row = res.detect {|row| row["Variable_name"] == var }
194
+ row && row["Value"]
195
+ end
196
+
197
+ def charset
198
+ show_variable("character_set_database")
199
+ end
200
+
201
+ def collation
202
+ show_variable("collation_database")
203
+ end
204
+
163
205
  private
164
206
  def supports_views?
165
207
  false
166
208
  end
167
209
  end
168
- end
210
+ end
@@ -1,5 +1,5 @@
1
1
  module JdbcAdapter
2
2
  module Version
3
- VERSION = "0.6"
3
+ VERSION = "0.7"
4
4
  end
5
5
  end
@@ -0,0 +1,1113 @@
1
+ /***** BEGIN LICENSE BLOCK *****
2
+ * Copyright (c) 2006-2007 Nick Sieger <nick@nicksieger.com>
3
+ * Copyright (c) 2006-2007 Ola Bini <ola.bini@gmail.com>
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining
6
+ * a copy of this software and associated documentation files (the
7
+ * "Software"), to deal in the Software without restriction, including
8
+ * without limitation the rights to use, copy, modify, merge, publish,
9
+ * distribute, sublicense, and/or sell copies of the Software, and to
10
+ * permit persons to whom the Software is furnished to do so, subject to
11
+ * the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be
14
+ * included in all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+ ***** END LICENSE BLOCK *****/
24
+
25
+ package jdbc_adapter;
26
+
27
+ import java.io.IOException;
28
+ import java.io.Reader;
29
+ import java.io.InputStream;
30
+ import java.io.ByteArrayInputStream;
31
+ import java.io.StringReader;
32
+
33
+ import java.sql.Connection;
34
+ import java.sql.DatabaseMetaData;
35
+ import java.sql.PreparedStatement;
36
+ import java.sql.ResultSetMetaData;
37
+ import java.sql.ResultSet;
38
+ import java.sql.SQLException;
39
+ import java.sql.Statement;
40
+ import java.sql.PreparedStatement;
41
+ import java.sql.Timestamp;
42
+ import java.sql.Types;
43
+
44
+ import java.text.DateFormat;
45
+ import java.text.SimpleDateFormat;
46
+
47
+ import java.util.ArrayList;
48
+ import java.util.Calendar;
49
+ import java.util.Date;
50
+ import java.util.List;
51
+
52
+ import org.jruby.Ruby;
53
+ import org.jruby.RubyArray;
54
+ import org.jruby.RubyClass;
55
+ import org.jruby.RubyHash;
56
+ import org.jruby.RubyModule;
57
+ import org.jruby.RubyNumeric;
58
+ import org.jruby.RubyObjectAdapter;
59
+ import org.jruby.RubyString;
60
+ import org.jruby.RubySymbol;
61
+ import org.jruby.RubyTime;
62
+ import org.jruby.
63
+ exceptions.RaiseException;
64
+ import org.jruby.javasupport.Java;
65
+ import org.jruby.javasupport.JavaEmbedUtils;
66
+ import org.jruby.javasupport.JavaObject;
67
+ import org.jruby.runtime.Arity;
68
+ import org.jruby.runtime.Block;
69
+ import org.jruby.runtime.CallbackFactory;
70
+ import org.jruby.runtime.ThreadContext;
71
+ import org.jruby.runtime.builtin.IRubyObject;
72
+ import org.jruby.runtime.load.BasicLibraryService;
73
+ import org.jruby.util.ByteList;
74
+
75
+ public class JdbcAdapterInternalService implements BasicLibraryService {
76
+ private static RubyObjectAdapter rubyApi;
77
+
78
+ public boolean basicLoad(final Ruby runtime) throws IOException {
79
+ RubyClass cJdbcConn = ((RubyModule)(runtime.getModule("ActiveRecord").getConstant("ConnectionAdapters"))).
80
+ defineClassUnder("JdbcConnection",runtime.getObject(),runtime.getObject().getAllocator());
81
+
82
+ CallbackFactory cf = runtime.callbackFactory(JdbcAdapterInternalService.class);
83
+ cJdbcConn.defineMethod("unmarshal_result",cf.getSingletonMethod("unmarshal_result", IRubyObject.class));
84
+ cJdbcConn.defineMethod("with_connection_retry_guard",cf.getSingletonMethod("with_connection_retry_guard"));
85
+ cJdbcConn.defineFastMethod("connection",cf.getFastSingletonMethod("connection"));
86
+ cJdbcConn.defineFastMethod("reconnect!",cf.getFastSingletonMethod("reconnect"));
87
+ cJdbcConn.defineFastMethod("disconnect!",cf.getFastSingletonMethod("disconnect"));
88
+ cJdbcConn.defineFastMethod("execute_update",cf.getFastSingletonMethod("execute_update", IRubyObject.class));
89
+ cJdbcConn.defineFastMethod("execute_query",cf.getFastOptSingletonMethod("execute_query"));
90
+ cJdbcConn.defineFastMethod("execute_insert",cf.getFastSingletonMethod("execute_insert", IRubyObject.class));
91
+ cJdbcConn.defineFastMethod("execute_id_insert",cf.getFastSingletonMethod("execute_id_insert", IRubyObject.class, IRubyObject.class));
92
+ cJdbcConn.defineFastMethod("primary_keys",cf.getFastSingletonMethod("primary_keys", IRubyObject.class));
93
+ cJdbcConn.defineFastMethod("set_native_database_types",cf.getFastSingletonMethod("set_native_database_types"));
94
+ cJdbcConn.defineFastMethod("native_database_types",cf.getFastSingletonMethod("native_database_types"));
95
+ cJdbcConn.defineFastMethod("begin",cf.getFastSingletonMethod("begin"));
96
+ cJdbcConn.defineFastMethod("commit",cf.getFastSingletonMethod("commit"));
97
+ cJdbcConn.defineFastMethod("rollback",cf.getFastSingletonMethod("rollback"));
98
+ cJdbcConn.defineFastMethod("database_name",cf.getFastSingletonMethod("database_name"));
99
+ cJdbcConn.defineFastMethod("columns",cf.getFastOptSingletonMethod("columns_internal"));
100
+ cJdbcConn.defineFastMethod("columns_internal",cf.getFastOptSingletonMethod("columns_internal"));
101
+ cJdbcConn.defineFastMethod("tables",cf.getFastOptSingletonMethod("tables"));
102
+
103
+ cJdbcConn.defineFastMethod("insert_bind",cf.getFastOptSingletonMethod("insert_bind"));
104
+ cJdbcConn.defineFastMethod("update_bind",cf.getFastOptSingletonMethod("update_bind"));
105
+
106
+ cJdbcConn.defineFastMethod("write_large_object",cf.getFastOptSingletonMethod("write_large_object"));
107
+
108
+ cJdbcConn.getMetaClass().defineFastMethod("insert?",cf.getFastSingletonMethod("insert_p", IRubyObject.class));
109
+ cJdbcConn.getMetaClass().defineFastMethod("select?",cf.getFastSingletonMethod("select_p", IRubyObject.class));
110
+
111
+ RubyModule jdbcSpec = runtime.getOrCreateModule("JdbcSpec");
112
+
113
+ rubyApi = JavaEmbedUtils.newObjectAdapter();
114
+ JdbcMySQLSpec.load(runtime, jdbcSpec);
115
+ JdbcDerbySpec.load(runtime, jdbcSpec, rubyApi);
116
+ return true;
117
+ }
118
+
119
+ private static int whitespace(int p, final int pend, ByteList bl) {
120
+ while(p < pend) {
121
+ switch(bl.bytes[p]) {
122
+ case ' ':
123
+ case '\n':
124
+ case '\r':
125
+ case '\t':
126
+ p++;
127
+ break;
128
+ default:
129
+ return p;
130
+ }
131
+ }
132
+ return p;
133
+ }
134
+
135
+ public static IRubyObject insert_p(IRubyObject recv, IRubyObject _sql) {
136
+ ByteList bl = rubyApi.convertToRubyString(_sql).getByteList();
137
+
138
+ int p = bl.begin;
139
+ int pend = p + bl.realSize;
140
+
141
+ p = whitespace(p, pend, bl);
142
+
143
+ if(pend - p >= 6) {
144
+ switch(bl.bytes[p++]) {
145
+ case 'i':
146
+ case 'I':
147
+ switch(bl.bytes[p++]) {
148
+ case 'n':
149
+ case 'N':
150
+ switch(bl.bytes[p++]) {
151
+ case 's':
152
+ case 'S':
153
+ switch(bl.bytes[p++]) {
154
+ case 'e':
155
+ case 'E':
156
+ switch(bl.bytes[p++]) {
157
+ case 'r':
158
+ case 'R':
159
+ switch(bl.bytes[p++]) {
160
+ case 't':
161
+ case 'T':
162
+ return recv.getRuntime().getTrue();
163
+ }
164
+ }
165
+ }
166
+ }
167
+ }
168
+ }
169
+ }
170
+ return recv.getRuntime().getFalse();
171
+ }
172
+
173
+ public static IRubyObject select_p(IRubyObject recv, IRubyObject _sql) {
174
+ ByteList bl = rubyApi.convertToRubyString(_sql).getByteList();
175
+
176
+ int p = bl.begin;
177
+ int pend = p + bl.realSize;
178
+
179
+ p = whitespace(p, pend, bl);
180
+
181
+ if(pend - p >= 6) {
182
+ if(bl.bytes[p] == '(') {
183
+ p++;
184
+ p = whitespace(p, pend, bl);
185
+ }
186
+ if(pend - p >= 6) {
187
+ switch(bl.bytes[p++]) {
188
+ case 's':
189
+ case 'S':
190
+ switch(bl.bytes[p++]) {
191
+ case 'e':
192
+ case 'E':
193
+ switch(bl.bytes[p++]) {
194
+ case 'l':
195
+ case 'L':
196
+ switch(bl.bytes[p++]) {
197
+ case 'e':
198
+ case 'E':
199
+ switch(bl.bytes[p++]) {
200
+ case 'c':
201
+ case 'C':
202
+ switch(bl.bytes[p++]) {
203
+ case 't':
204
+ case 'T':
205
+ return recv.getRuntime().getTrue();
206
+ }
207
+ }
208
+ }
209
+ }
210
+ case 'h':
211
+ case 'H':
212
+ switch(bl.bytes[p++]) {
213
+ case 'o':
214
+ case 'O':
215
+ switch(bl.bytes[p++]) {
216
+ case 'w':
217
+ case 'W':
218
+ return recv.getRuntime().getTrue();
219
+ }
220
+ }
221
+ }
222
+ }
223
+ }
224
+ }
225
+ return recv.getRuntime().getFalse();
226
+ }
227
+
228
+ public static IRubyObject connection(IRubyObject recv) {
229
+ Connection c = getConnection(recv);
230
+ if (c == null) {
231
+ reconnect(recv);
232
+ }
233
+ return rubyApi.getInstanceVariable(recv, "@connection");
234
+ }
235
+
236
+ public static IRubyObject disconnect(IRubyObject recv) {
237
+ setConnection(recv, null);
238
+ return recv;
239
+ }
240
+
241
+ public static IRubyObject reconnect(IRubyObject recv) {
242
+ setConnection(recv, getConnectionFactory(recv).newConnection());
243
+ return recv;
244
+ }
245
+
246
+ public static IRubyObject with_connection_retry_guard(final IRubyObject recv, final Block block) {
247
+ return withConnectionAndRetry(recv, new SQLBlock() {
248
+ public IRubyObject call(Connection c) throws SQLException {
249
+ return block.call(recv.getRuntime().getCurrentContext(), new IRubyObject[] {
250
+ wrappedConnection(recv, c)
251
+ });
252
+ }
253
+ });
254
+ }
255
+
256
+ private static IRubyObject withConnectionAndRetry(IRubyObject recv, SQLBlock block) {
257
+ int tries = 1;
258
+ int i = 0;
259
+ Throwable toWrap = null;
260
+ while (i < tries) {
261
+ Connection c = getConnection(recv);
262
+ try {
263
+ return block.call(c);
264
+ } catch (Exception e) {
265
+ toWrap = e;
266
+ while (toWrap.getCause() != null && toWrap.getCause() != toWrap) {
267
+ toWrap = toWrap.getCause();
268
+ }
269
+ if (i == 0) {
270
+ tries = (int) rubyApi.convertToRubyInteger(config_value(recv, "retry_count")).getLongValue();
271
+ if (tries <= 0) {
272
+ tries = 1;
273
+ }
274
+ }
275
+ i++;
276
+ if (isConnectionBroken(recv, c)) {
277
+ reconnect(recv);
278
+ } else {
279
+ throw wrap(recv, toWrap);
280
+ }
281
+ }
282
+ }
283
+ throw wrap(recv, toWrap);
284
+ }
285
+
286
+ private static SQLBlock tableLookupBlock(final Ruby runtime,
287
+ final String catalog, final String schemapat,
288
+ final String tablepat, final String[] types) {
289
+ return new SQLBlock() {
290
+ public IRubyObject call(Connection c) throws SQLException {
291
+ ResultSet rs = null;
292
+ try {
293
+ DatabaseMetaData metadata = c.getMetaData();
294
+ String clzName = metadata.getClass().getName().toLowerCase();
295
+ boolean isOracle = clzName.indexOf("oracle") != -1 || clzName.indexOf("oci") != -1;
296
+
297
+ String realschema = schemapat;
298
+ if (realschema == null && isOracle) {
299
+ ResultSet schemas = metadata.getSchemas();
300
+ String username = metadata.getUserName();
301
+ while (schemas.next()) {
302
+ if (schemas.getString(1).equalsIgnoreCase(username)) {
303
+ realschema = schemas.getString(1);
304
+ break;
305
+ }
306
+ }
307
+ schemas.close();
308
+ }
309
+ rs = metadata.getTables(catalog, realschema, tablepat, types);
310
+ List arr = new ArrayList();
311
+ while (rs.next()) {
312
+ String name = rs.getString(3).toLowerCase();
313
+ // Handle stupid Oracle 10g RecycleBin feature
314
+ if (!isOracle || !name.startsWith("bin$")) {
315
+ arr.add(runtime.newString(name));
316
+ }
317
+ }
318
+ return runtime.newArray(arr);
319
+ } finally {
320
+ try { rs.close(); } catch (Exception e) { }
321
+ }
322
+ }
323
+ };
324
+ }
325
+
326
+ public static IRubyObject tables(final IRubyObject recv, IRubyObject[] args) {
327
+ final Ruby runtime = recv.getRuntime();
328
+ final String catalog = getCatalog(args);
329
+ final String schemapat = getSchemaPattern(args);
330
+ final String tablepat = getTablePattern(args);
331
+ final String[] types = getTypes(args);
332
+ return withConnectionAndRetry(recv, tableLookupBlock(runtime, catalog,
333
+ schemapat, tablepat, types));
334
+ }
335
+
336
+ private static String getCatalog(IRubyObject[] args) {
337
+ if (args != null && args.length > 0) {
338
+ return convertToStringOrNull(args[0]);
339
+ }
340
+ return null;
341
+ }
342
+
343
+ private static String getSchemaPattern(IRubyObject[] args) {
344
+ if (args != null && args.length > 1) {
345
+ return convertToStringOrNull(args[1]);
346
+ }
347
+ return null;
348
+ }
349
+
350
+ private static String getTablePattern(IRubyObject[] args) {
351
+ if (args != null && args.length > 2) {
352
+ return convertToStringOrNull(args[2]);
353
+ }
354
+ return null;
355
+ }
356
+
357
+ private static String[] getTypes(IRubyObject[] args) {
358
+ String[] types = new String[]{"TABLE"};
359
+ if (args != null && args.length > 3) {
360
+ IRubyObject typearr = args[3];
361
+ if (typearr instanceof RubyArray) {
362
+ IRubyObject[] arr = rubyApi.convertToJavaArray(typearr);
363
+ types = new String[arr.length];
364
+ for (int i = 0; i < types.length; i++) {
365
+ types[i] = arr[i].toString();
366
+ }
367
+ } else {
368
+ types = new String[]{types.toString()};
369
+ }
370
+ }
371
+ return types;
372
+ }
373
+
374
+ public static IRubyObject native_database_types(IRubyObject recv) {
375
+ return rubyApi.getInstanceVariable(recv, "@tps");
376
+ }
377
+
378
+ public static IRubyObject set_native_database_types(IRubyObject recv) throws SQLException, IOException {
379
+ Ruby runtime = recv.getRuntime();
380
+ IRubyObject types = unmarshal_result_downcase(recv, getConnection(recv).getMetaData().getTypeInfo());
381
+ IRubyObject typeConverter = ((RubyModule) (runtime.getModule("ActiveRecord").getConstant("ConnectionAdapters"))).getConstant("JdbcTypeConverter");
382
+ IRubyObject value = rubyApi.callMethod(rubyApi.callMethod(typeConverter, "new", types), "choose_best_types");
383
+ rubyApi.setInstanceVariable(recv, "@native_types", value);
384
+ return runtime.getNil();
385
+ }
386
+
387
+ public static IRubyObject database_name(IRubyObject recv) throws SQLException {
388
+ String name = getConnection(recv).getCatalog();
389
+ if(null == name) {
390
+ name = getConnection(recv).getMetaData().getUserName();
391
+ if(null == name) {
392
+ name = "db1";
393
+ }
394
+ }
395
+ return recv.getRuntime().newString(name);
396
+ }
397
+
398
+ public static IRubyObject begin(IRubyObject recv) throws SQLException {
399
+ getConnection(recv).setAutoCommit(false);
400
+ return recv.getRuntime().getNil();
401
+ }
402
+
403
+ public static IRubyObject commit(IRubyObject recv) throws SQLException {
404
+ try {
405
+ getConnection(recv).commit();
406
+ return recv.getRuntime().getNil();
407
+ } finally {
408
+ getConnection(recv).setAutoCommit(true);
409
+ }
410
+ }
411
+
412
+ public static IRubyObject rollback(IRubyObject recv) throws SQLException {
413
+ try {
414
+ getConnection(recv).rollback();
415
+ return recv.getRuntime().getNil();
416
+ } finally {
417
+ getConnection(recv).setAutoCommit(true);
418
+ }
419
+ }
420
+
421
+ public static IRubyObject columns_internal(final IRubyObject recv, final IRubyObject[] args) throws SQLException, IOException {
422
+ return withConnectionAndRetry(recv, new SQLBlock() {
423
+ public IRubyObject call(Connection c) throws SQLException {
424
+ ResultSet results = null;
425
+ try {
426
+ String table_name = rubyApi.convertToRubyString(args[0]).getUnicodeValue();
427
+ DatabaseMetaData metadata = c.getMetaData();
428
+ String clzName = metadata.getClass().getName().toLowerCase();
429
+ boolean isDerby = clzName.indexOf("derby") != -1;
430
+ boolean isOracle = clzName.indexOf("oracle") != -1 || clzName.indexOf("oci") != -1;
431
+ String schemaName = null;
432
+
433
+ if(args.length>2) {
434
+ schemaName = args[2].toString();
435
+ }
436
+
437
+ if(metadata.storesUpperCaseIdentifiers()) {
438
+ table_name = table_name.toUpperCase();
439
+ } else if(metadata.storesLowerCaseIdentifiers()) {
440
+ table_name = table_name.toLowerCase();
441
+ }
442
+
443
+ if(schemaName == null && (isDerby || isOracle)) {
444
+ ResultSet schemas = metadata.getSchemas();
445
+ String username = metadata.getUserName();
446
+ while(schemas.next()) {
447
+ if(schemas.getString(1).equalsIgnoreCase(username)) {
448
+ schemaName = schemas.getString(1);
449
+ break;
450
+ }
451
+ }
452
+ schemas.close();
453
+ }
454
+
455
+ RubyArray matchingTables = (RubyArray) tableLookupBlock(recv.getRuntime(),
456
+ c.getCatalog(), schemaName, table_name, getTypes(null)).call(c);
457
+ if (matchingTables.isEmpty()) {
458
+ throw new SQLException("Table " + table_name + " does not exist");
459
+ }
460
+
461
+ results = metadata.getColumns(c.getCatalog(),schemaName,table_name,null);
462
+ return unmarshal_columns(recv, metadata, results);
463
+ } finally {
464
+ try { if (results != null) results.close(); } catch (SQLException sqx) {}
465
+ }
466
+ }
467
+ });
468
+ }
469
+
470
+ private static final java.util.regex.Pattern HAS_SMALL = java.util.regex.Pattern.compile("[a-z]");
471
+ private static IRubyObject unmarshal_columns(IRubyObject recv, DatabaseMetaData metadata, ResultSet rs) throws SQLException {
472
+ try {
473
+ List columns = new ArrayList();
474
+ String clzName = metadata.getClass().getName().toLowerCase();
475
+ boolean isDerby = clzName.indexOf("derby") != -1;
476
+ boolean isOracle = clzName.indexOf("oracle") != -1 || clzName.indexOf("oci") != -1;
477
+ Ruby runtime = recv.getRuntime();
478
+
479
+ IRubyObject adapter = rubyApi.callMethod(recv, "adapter");
480
+ RubyHash tps = (RubyHash) rubyApi.callMethod(adapter, "native_database_types");
481
+
482
+ IRubyObject jdbcCol = ((RubyModule)(runtime.getModule("ActiveRecord").getConstant("ConnectionAdapters"))).getConstant("JdbcColumn");
483
+
484
+ while(rs.next()) {
485
+ String column_name = rs.getString(4);
486
+ if(metadata.storesUpperCaseIdentifiers() && !HAS_SMALL.matcher(column_name).find()) {
487
+ column_name = column_name.toLowerCase();
488
+ }
489
+
490
+ String prec = rs.getString(7);
491
+ String scal = rs.getString(9);
492
+ int precision = -1;
493
+ int scale = -1;
494
+ if(prec != null) {
495
+ precision = Integer.parseInt(prec);
496
+ if(scal != null) {
497
+ scale = Integer.parseInt(scal);
498
+ }
499
+ }
500
+ String type = rs.getString(6);
501
+ if(prec != null && precision > 0) {
502
+ type += "(" + precision;
503
+ if(scal != null && scale > 0) {
504
+ type += "," + scale;
505
+ }
506
+ type += ")";
507
+ }
508
+ String def = rs.getString(13);
509
+ IRubyObject _def;
510
+ if(def == null || (isOracle && def.toLowerCase().trim().equals("null"))) {
511
+ _def = runtime.getNil();
512
+ } else {
513
+ if(isOracle) {
514
+ def = def.trim();
515
+ }
516
+ if((isDerby || isOracle) && def.length() > 0 && def.charAt(0) == '\'') {
517
+ def = def.substring(1, def.length()-1);
518
+ }
519
+ _def = runtime.newString(def);
520
+ }
521
+ IRubyObject config = rubyApi.getInstanceVariable(recv, "@config");
522
+ IRubyObject c = rubyApi.callMethod(jdbcCol, "new",
523
+ new IRubyObject[]{
524
+ config, runtime.newString(column_name),
525
+ _def, runtime.newString(type),
526
+ runtime.newBoolean(!rs.getString(18).trim().equals("NO"))
527
+ });
528
+ columns.add(c);
529
+
530
+ IRubyObject tp = (IRubyObject)tps.fastARef(rubyApi.callMethod(c,"type"));
531
+ if(tp != null && !tp.isNil() && rubyApi.callMethod(tp, "[]", runtime.newSymbol("limit")).isNil()) {
532
+ rubyApi.callMethod(c, "limit=", runtime.getNil());
533
+ if(!rubyApi.callMethod(c, "type").equals(runtime.newSymbol("decimal"))) {
534
+ rubyApi.callMethod(c, "precision=", runtime.getNil());
535
+ }
536
+ }
537
+ }
538
+ return runtime.newArray(columns);
539
+ } finally {
540
+ try {
541
+ rs.close();
542
+ } catch(Exception e) {}
543
+ }
544
+ }
545
+
546
+ public static IRubyObject primary_keys(final IRubyObject recv, final IRubyObject _table_name) throws SQLException {
547
+ return withConnectionAndRetry(recv, new SQLBlock() {
548
+ public IRubyObject call(Connection c) throws SQLException {
549
+ DatabaseMetaData metadata = c.getMetaData();
550
+ String table_name = _table_name.toString();
551
+ if (metadata.storesUpperCaseIdentifiers()) {
552
+ table_name = table_name.toUpperCase();
553
+ } else if (metadata.storesLowerCaseIdentifiers()) {
554
+ table_name = table_name.toLowerCase();
555
+ }
556
+ ResultSet result_set = metadata.getPrimaryKeys(null, null, table_name);
557
+ List keyNames = new ArrayList();
558
+ Ruby runtime = recv.getRuntime();
559
+ while (result_set.next()) {
560
+ String s1 = result_set.getString(4);
561
+ if (metadata.storesUpperCaseIdentifiers() && !HAS_SMALL.matcher(s1).find()) {
562
+ s1 = s1.toLowerCase();
563
+ }
564
+ keyNames.add(runtime.newString(s1));
565
+ }
566
+
567
+ try {
568
+ result_set.close();
569
+ } catch (Exception e) {
570
+ }
571
+
572
+ return runtime.newArray(keyNames);
573
+ }
574
+ });
575
+ }
576
+
577
+ public static IRubyObject execute_id_insert(IRubyObject recv, final IRubyObject sql, final IRubyObject id) throws SQLException {
578
+ return withConnectionAndRetry(recv, new SQLBlock() {
579
+ public IRubyObject call(Connection c) throws SQLException {
580
+ PreparedStatement ps = c.prepareStatement(rubyApi.convertToRubyString(sql).getUnicodeValue());
581
+ try {
582
+ ps.setLong(1, RubyNumeric.fix2long(id));
583
+ ps.executeUpdate();
584
+ } finally {
585
+ ps.close();
586
+ }
587
+ return id;
588
+ }
589
+ });
590
+ }
591
+
592
+ public static IRubyObject execute_update(final IRubyObject recv, final IRubyObject sql) throws SQLException {
593
+ return withConnectionAndRetry(recv, new SQLBlock() {
594
+ public IRubyObject call(Connection c) throws SQLException {
595
+ Statement stmt = null;
596
+ try {
597
+ stmt = c.createStatement();
598
+ return recv.getRuntime().newFixnum(stmt.executeUpdate(rubyApi.convertToRubyString(sql).getUnicodeValue()));
599
+ } finally {
600
+ if (null != stmt) {
601
+ try {
602
+ stmt.close();
603
+ } catch (Exception e) {
604
+ }
605
+ }
606
+ }
607
+ }
608
+ });
609
+ }
610
+
611
+ public static IRubyObject execute_query(final IRubyObject recv, IRubyObject[] args) throws SQLException, IOException {
612
+ final IRubyObject sql = args[0];
613
+ final int maxrows;
614
+
615
+ if (args.length > 1) {
616
+ maxrows = RubyNumeric.fix2int(args[1]);
617
+ } else {
618
+ maxrows = 0;
619
+ }
620
+
621
+ return withConnectionAndRetry(recv, new SQLBlock() {
622
+ public IRubyObject call(Connection c) throws SQLException {
623
+ Statement stmt = null;
624
+ try {
625
+ stmt = c.createStatement();
626
+ stmt.setMaxRows(maxrows);
627
+ return unmarshal_result(recv, stmt.executeQuery(rubyApi.convertToRubyString(sql).getUnicodeValue()));
628
+ } finally {
629
+ if (null != stmt) {
630
+ try {
631
+ stmt.close();
632
+ } catch (Exception e) {
633
+ }
634
+ }
635
+ }
636
+ }
637
+ });
638
+ }
639
+
640
+ public static IRubyObject execute_insert(final IRubyObject recv, final IRubyObject sql) throws SQLException {
641
+ return withConnectionAndRetry(recv, new SQLBlock() {
642
+ public IRubyObject call(Connection c) throws SQLException {
643
+ Statement stmt = null;
644
+ try {
645
+ stmt = c.createStatement();
646
+ stmt.executeUpdate(rubyApi.convertToRubyString(sql).getUnicodeValue(), Statement.RETURN_GENERATED_KEYS);
647
+ return unmarshal_id_result(recv.getRuntime(), stmt.getGeneratedKeys());
648
+ } finally {
649
+ if (null != stmt) {
650
+ try {
651
+ stmt.close();
652
+ } catch (Exception e) {
653
+ }
654
+ }
655
+ }
656
+ }
657
+ });
658
+ }
659
+
660
+ public static IRubyObject unmarshal_result_downcase(IRubyObject recv, ResultSet rs) throws SQLException, IOException {
661
+ List results = new ArrayList();
662
+ Ruby runtime = recv.getRuntime();
663
+ try {
664
+ ResultSetMetaData metadata = rs.getMetaData();
665
+ int col_count = metadata.getColumnCount();
666
+ IRubyObject[] col_names = new IRubyObject[col_count];
667
+ int[] col_types = new int[col_count];
668
+ int[] col_scale = new int[col_count];
669
+
670
+ for(int i=0;i<col_count;i++) {
671
+ col_names[i] = runtime.newString(metadata.getColumnName(i+1).toLowerCase());
672
+ col_types[i] = metadata.getColumnType(i+1);
673
+ col_scale[i] = metadata.getScale(i+1);
674
+ }
675
+
676
+ while(rs.next()) {
677
+ RubyHash row = RubyHash.newHash(runtime);
678
+ for(int i=0;i<col_count;i++) {
679
+ rubyApi.callMethod(row, "[]=", new IRubyObject[] {
680
+ col_names[i], jdbc_to_ruby(runtime, i+1, col_types[i], col_scale[i], rs)
681
+ });
682
+ }
683
+ results.add(row);
684
+ }
685
+ } finally {
686
+ try {
687
+ rs.close();
688
+ } catch(Exception e) {}
689
+ }
690
+
691
+ return runtime.newArray(results);
692
+ }
693
+
694
+ public static IRubyObject unmarshal_result(IRubyObject recv, ResultSet rs) throws SQLException {
695
+ Ruby runtime = recv.getRuntime();
696
+ List results = new ArrayList();
697
+ try {
698
+ ResultSetMetaData metadata = rs.getMetaData();
699
+ boolean storesUpper = rs.getStatement().getConnection().getMetaData().storesUpperCaseIdentifiers();
700
+ int col_count = metadata.getColumnCount();
701
+ IRubyObject[] col_names = new IRubyObject[col_count];
702
+ int[] col_types = new int[col_count];
703
+ int[] col_scale = new int[col_count];
704
+
705
+ for(int i=0;i<col_count;i++) {
706
+ String s1 = metadata.getColumnName(i+1);
707
+ if(storesUpper && !HAS_SMALL.matcher(s1).find()) {
708
+ s1 = s1.toLowerCase();
709
+ }
710
+ col_names[i] = runtime.newString(s1);
711
+ col_types[i] = metadata.getColumnType(i+1);
712
+ col_scale[i] = metadata.getScale(i+1);
713
+ }
714
+
715
+ while(rs.next()) {
716
+ RubyHash row = RubyHash.newHash(runtime);
717
+ for(int i=0;i<col_count;i++) {
718
+ rubyApi.callMethod(row, "[]=", new IRubyObject[] {
719
+ col_names[i], jdbc_to_ruby(runtime, i+1, col_types[i], col_scale[i], rs)
720
+ });
721
+ }
722
+ results.add(row);
723
+ }
724
+ } finally {
725
+ try {
726
+ rs.close();
727
+ } catch(Exception e) {}
728
+ }
729
+ return runtime.newArray(results);
730
+ }
731
+
732
+ public static IRubyObject unmarshal_result(IRubyObject recv, IRubyObject resultset, Block row_filter) throws SQLException, IOException {
733
+ Ruby runtime = recv.getRuntime();
734
+ ResultSet rs = intoResultSet(resultset);
735
+ List results = new ArrayList();
736
+ try {
737
+ ResultSetMetaData metadata = rs.getMetaData();
738
+ int col_count = metadata.getColumnCount();
739
+ IRubyObject[] col_names = new IRubyObject[col_count];
740
+ int[] col_types = new int[col_count];
741
+ int[] col_scale = new int[col_count];
742
+
743
+ for (int i=0;i<col_count;i++) {
744
+ col_names[i] = runtime.newString(metadata.getColumnName(i+1));
745
+ col_types[i] = metadata.getColumnType(i+1);
746
+ col_scale[i] = metadata.getScale(i+1);
747
+ }
748
+
749
+ if (row_filter.isGiven()) {
750
+ while (rs.next()) {
751
+ if (row_filter.yield(runtime.getCurrentContext(),resultset).isTrue()) {
752
+ RubyHash row = RubyHash.newHash(runtime);
753
+ for (int i=0;i<col_count;i++) {
754
+ rubyApi.callMethod(row, "[]=", new IRubyObject[] {
755
+ col_names[i], jdbc_to_ruby(runtime, i+1, col_types[i], col_scale[i], rs)
756
+ });
757
+ }
758
+ results.add(row);
759
+ }
760
+ }
761
+ } else {
762
+ while (rs.next()) {
763
+ RubyHash row = RubyHash.newHash(runtime);
764
+ for (int i=0;i<col_count;i++) {
765
+ rubyApi.callMethod(row, "[]=", new IRubyObject[] {
766
+ col_names[i], jdbc_to_ruby(runtime, i+1, col_types[i], col_scale[i], rs)
767
+ });
768
+ }
769
+ results.add(row);
770
+ }
771
+ }
772
+
773
+ } finally {
774
+ try {
775
+ rs.close();
776
+ } catch(Exception e) {}
777
+ }
778
+
779
+ return runtime.newArray(results);
780
+ }
781
+
782
+ private static IRubyObject jdbc_to_ruby(Ruby runtime, int row, int type, int scale, ResultSet rs) throws SQLException {
783
+ try {
784
+ int n;
785
+ switch (type) {
786
+ case Types.BINARY:
787
+ case Types.BLOB:
788
+ case Types.LONGVARBINARY:
789
+ case Types.VARBINARY:
790
+ InputStream is = rs.getBinaryStream(row);
791
+ if (is == null || rs.wasNull()) {
792
+ return runtime.getNil();
793
+ }
794
+ ByteList str = new ByteList(2048);
795
+ byte[] buf = new byte[2048];
796
+
797
+ while ((n = is.read(buf)) != -1) {
798
+ str.append(buf, 0, n);
799
+ }
800
+ is.close();
801
+
802
+ return runtime.newString(str);
803
+ case Types.LONGVARCHAR:
804
+ case Types.CLOB:
805
+ Reader rss = rs.getCharacterStream(row);
806
+ if (rss == null || rs.wasNull()) {
807
+ return runtime.getNil();
808
+ }
809
+ StringBuffer str2 = new StringBuffer(2048);
810
+ char[] cuf = new char[2048];
811
+ while ((n = rss.read(cuf)) != -1) {
812
+ str2.append(cuf, 0, n);
813
+ }
814
+ rss.close();
815
+ return RubyString.newUnicodeString(runtime, str2.toString());
816
+ case Types.TIMESTAMP:
817
+ Timestamp time = rs.getTimestamp(row);
818
+ if (time == null || rs.wasNull()) {
819
+ return runtime.getNil();
820
+ }
821
+ String sttr = time.toString();
822
+ if (sttr.endsWith(" 00:00:00.0")) {
823
+ sttr = sttr.substring(0, sttr.length() - (" 00:00:00.0".length()));
824
+ }
825
+ return RubyString.newUnicodeString(runtime, sttr);
826
+ default:
827
+ String vs = rs.getString(row);
828
+ if (vs == null || rs.wasNull()) {
829
+ return runtime.getNil();
830
+ }
831
+
832
+ return RubyString.newUnicodeString(runtime, vs);
833
+ }
834
+ } catch (IOException ioe) {
835
+ throw (SQLException) new SQLException(ioe.getMessage()).initCause(ioe);
836
+ }
837
+ }
838
+
839
+ public static IRubyObject unmarshal_id_result(Ruby runtime, ResultSet rs) throws SQLException {
840
+ try {
841
+ if(rs.next()) {
842
+ if(rs.getMetaData().getColumnCount() > 0) {
843
+ return runtime.newFixnum(rs.getLong(1));
844
+ }
845
+ }
846
+ return runtime.getNil();
847
+ } finally {
848
+ try {
849
+ rs.close();
850
+ } catch(Exception e) {}
851
+ }
852
+ }
853
+
854
+ private static String convertToStringOrNull(IRubyObject obj) {
855
+ if (obj.isNil()) {
856
+ return null;
857
+ }
858
+ return obj.toString();
859
+ }
860
+
861
+ private static int getTypeValueFor(Ruby runtime, IRubyObject type) throws SQLException {
862
+ if(!(type instanceof RubySymbol)) {
863
+ type = rubyApi.callMethod(type, "type");
864
+ }
865
+ if(type == runtime.newSymbol("string")) {
866
+ return Types.VARCHAR;
867
+ } else if(type == runtime.newSymbol("text")) {
868
+ return Types.CLOB;
869
+ } else if(type == runtime.newSymbol("integer")) {
870
+ return Types.INTEGER;
871
+ } else if(type == runtime.newSymbol("decimal")) {
872
+ return Types.DECIMAL;
873
+ } else if(type == runtime.newSymbol("float")) {
874
+ return Types.FLOAT;
875
+ } else if(type == runtime.newSymbol("datetime")) {
876
+ return Types.TIMESTAMP;
877
+ } else if(type == runtime.newSymbol("timestamp")) {
878
+ return Types.TIMESTAMP;
879
+ } else if(type == runtime.newSymbol("time")) {
880
+ return Types.TIME;
881
+ } else if(type == runtime.newSymbol("date")) {
882
+ return Types.DATE;
883
+ } else if(type == runtime.newSymbol("binary")) {
884
+ return Types.BLOB;
885
+ } else if(type == runtime.newSymbol("boolean")) {
886
+ return Types.BOOLEAN;
887
+ } else {
888
+ return -1;
889
+ }
890
+ }
891
+
892
+ private final static DateFormat FORMAT = new SimpleDateFormat("%y-%M-%d %H:%m:%s");
893
+
894
+ private static void setValue(PreparedStatement ps, int index, Ruby runtime, IRubyObject value, IRubyObject type) throws SQLException {
895
+ final int tp = getTypeValueFor(runtime, type);
896
+ if(value.isNil()) {
897
+ ps.setNull(index, tp);
898
+ return;
899
+ }
900
+
901
+ switch(tp) {
902
+ case Types.VARCHAR:
903
+ case Types.CLOB:
904
+ ps.setString(index, RubyString.objAsString(value).toString());
905
+ break;
906
+ case Types.INTEGER:
907
+ ps.setLong(index, RubyNumeric.fix2long(value));
908
+ break;
909
+ case Types.FLOAT:
910
+ ps.setDouble(index, ((RubyNumeric)value).getDoubleValue());
911
+ break;
912
+ case Types.TIMESTAMP:
913
+ case Types.TIME:
914
+ case Types.DATE:
915
+ if(!(value instanceof RubyTime)) {
916
+ try {
917
+ Date dd = FORMAT.parse(RubyString.objAsString(value).toString());
918
+ ps.setTimestamp(index, new java.sql.Timestamp(dd.getTime()), Calendar.getInstance());
919
+ } catch(Exception e) {
920
+ ps.setString(index, RubyString.objAsString(value).toString());
921
+ }
922
+ } else {
923
+ RubyTime rubyTime = (RubyTime) value;
924
+ java.util.Date date = rubyTime.getJavaDate();
925
+ long millis = date.getTime();
926
+ long micros = rubyTime.microseconds() - millis / 1000;
927
+ java.sql.Timestamp ts = new java.sql.Timestamp(millis);
928
+ java.util.Calendar cal = Calendar.getInstance();
929
+ cal.setTime(date);
930
+ ts.setNanos((int)(micros * 1000));
931
+ ps.setTimestamp(index, ts, cal);
932
+ }
933
+ break;
934
+ case Types.BOOLEAN:
935
+ ps.setBoolean(index, value.isTrue());
936
+ break;
937
+ default: throw new RuntimeException("type " + type + " not supported in _bind yet");
938
+ }
939
+ }
940
+
941
+ private static void setValuesOnPS(PreparedStatement ps, Ruby runtime, IRubyObject values, IRubyObject types) throws SQLException {
942
+ RubyArray vals = (RubyArray)values;
943
+ RubyArray tps = (RubyArray)types;
944
+
945
+ for(int i=0, j=vals.getLength(); i<j; i++) {
946
+ setValue(ps, i+1, runtime, vals.eltInternal(i), tps.eltInternal(i));
947
+ }
948
+ }
949
+
950
+ /*
951
+ * sql, values, types, name = nil, pk = nil, id_value = nil, sequence_name = nil
952
+ */
953
+ public static IRubyObject insert_bind(IRubyObject recv, final IRubyObject[] args) throws SQLException {
954
+ final Ruby runtime = recv.getRuntime();
955
+ Arity.checkArgumentCount(runtime, args, 3, 7);
956
+ return withConnectionAndRetry(recv, new SQLBlock() {
957
+ public IRubyObject call(Connection c) throws SQLException {
958
+ PreparedStatement ps = null;
959
+ try {
960
+ ps = c.prepareStatement(rubyApi.convertToRubyString(args[0]).toString(), Statement.RETURN_GENERATED_KEYS);
961
+ setValuesOnPS(ps, runtime, args[1], args[2]);
962
+ ps.executeUpdate();
963
+ return unmarshal_id_result(runtime, ps.getGeneratedKeys());
964
+ } finally {
965
+ try {
966
+ ps.close();
967
+ } catch (Exception e) {
968
+ }
969
+ }
970
+ }
971
+ });
972
+ }
973
+
974
+ /*
975
+ * sql, values, types, name = nil
976
+ */
977
+ public static IRubyObject update_bind(IRubyObject recv, final IRubyObject[] args) throws SQLException {
978
+ final Ruby runtime = recv.getRuntime();
979
+ Arity.checkArgumentCount(runtime, args, 3, 4);
980
+ return withConnectionAndRetry(recv, new SQLBlock() {
981
+ public IRubyObject call(Connection c) throws SQLException {
982
+ PreparedStatement ps = null;
983
+ try {
984
+ ps = c.prepareStatement(rubyApi.convertToRubyString(args[0]).toString());
985
+ setValuesOnPS(ps, runtime, args[1], args[2]);
986
+ ps.executeUpdate();
987
+ } finally {
988
+ try {
989
+ ps.close();
990
+ } catch (Exception e) {
991
+ }
992
+ }
993
+ return runtime.getNil();
994
+ }
995
+ });
996
+ }
997
+
998
+ /*
999
+ * (is binary?, colname, tablename, primary key, id, value)
1000
+ */
1001
+ public static IRubyObject write_large_object(IRubyObject recv, final IRubyObject[] args)
1002
+ throws SQLException, IOException {
1003
+ final Ruby runtime = recv.getRuntime();
1004
+ Arity.checkArgumentCount(runtime, args, 6, 6);
1005
+ return withConnectionAndRetry(recv, new SQLBlock() {
1006
+ public IRubyObject call(Connection c) throws SQLException {
1007
+ String sql = "UPDATE " + rubyApi.convertToRubyString(args[2])
1008
+ + " SET " + rubyApi.convertToRubyString(args[1])
1009
+ + " = ? WHERE " + rubyApi.convertToRubyString(args[3])
1010
+ + "=" + rubyApi.convertToRubyString(args[4]);
1011
+ PreparedStatement ps = null;
1012
+ try {
1013
+ ps = c.prepareStatement(sql);
1014
+ if (args[0].isTrue()) { // binary
1015
+ ByteList outp = rubyApi.convertToRubyString(args[5]).getByteList();
1016
+ ps.setBinaryStream(1, new ByteArrayInputStream(outp.bytes,
1017
+ outp.begin, outp.realSize), outp.realSize);
1018
+ } else { // clob
1019
+ String ss = rubyApi.convertToRubyString(args[5]).getUnicodeValue();
1020
+ ps.setCharacterStream(1, new StringReader(ss), ss.length());
1021
+ }
1022
+ ps.executeUpdate();
1023
+ } finally {
1024
+ try {
1025
+ ps.close();
1026
+ } catch (Exception e) {
1027
+ }
1028
+ }
1029
+ return runtime.getNil();
1030
+ }
1031
+ });
1032
+ }
1033
+
1034
+ private static Connection getConnection(IRubyObject recv) {
1035
+ Connection conn = (Connection) recv.dataGetStruct();
1036
+ return conn;
1037
+ }
1038
+
1039
+ private static RuntimeException wrap(IRubyObject recv, Throwable exception) {
1040
+ RubyClass err = recv.getRuntime().getModule("ActiveRecord").getClass("ActiveRecordError");
1041
+ return (RuntimeException) new RaiseException(recv.getRuntime(), err, exception.getMessage(), false).initCause(exception);
1042
+ }
1043
+
1044
+ private static ResultSet intoResultSet(IRubyObject inp) {
1045
+ JavaObject jo;
1046
+ if (inp instanceof JavaObject) {
1047
+ jo = (JavaObject) inp;
1048
+ } else {
1049
+ jo = (JavaObject) rubyApi.getInstanceVariable(inp, "@java_object");
1050
+ }
1051
+ return (ResultSet) jo.getValue();
1052
+ }
1053
+
1054
+ private static boolean isConnectionBroken(IRubyObject recv, Connection c) {
1055
+ try {
1056
+ IRubyObject alive = config_value(recv, "connection_alive_sql");
1057
+ if (select_p(recv, alive).isTrue()) {
1058
+ String connectionSQL = rubyApi.convertToRubyString(alive).toString();
1059
+ Statement s = c.createStatement();
1060
+ try {
1061
+ s.execute(connectionSQL);
1062
+ } finally {
1063
+ try { s.close(); } catch (SQLException ignored) {}
1064
+ }
1065
+ return true;
1066
+ } else {
1067
+ return !c.isClosed();
1068
+ }
1069
+ } catch (SQLException sx) {
1070
+ return true;
1071
+ }
1072
+ }
1073
+
1074
+ private static IRubyObject setConnection(IRubyObject recv, Connection c) {
1075
+ Connection prev = getConnection(recv);
1076
+ if (prev != null) {
1077
+ try {
1078
+ prev.close();
1079
+ } catch(Exception e) {}
1080
+ }
1081
+ IRubyObject rubyconn = recv.getRuntime().getNil();
1082
+ if (c != null) {
1083
+ rubyconn = wrappedConnection(recv,c);
1084
+ }
1085
+ rubyApi.setInstanceVariable(recv, "@connection", rubyconn);
1086
+ recv.dataWrapStruct(c);
1087
+ return recv;
1088
+ }
1089
+
1090
+ private static IRubyObject wrappedConnection(IRubyObject recv, Connection c) {
1091
+ return Java.java_to_ruby(recv, JavaObject.wrap(recv.getRuntime(), c), Block.NULL_BLOCK);
1092
+ }
1093
+
1094
+ private static JdbcConnectionFactory getConnectionFactory(IRubyObject recv) throws RaiseException {
1095
+ IRubyObject connection_factory = rubyApi.getInstanceVariable(recv, "@connection_factory");
1096
+ JdbcConnectionFactory factory = null;
1097
+ try {
1098
+ factory = (JdbcConnectionFactory) ((JavaObject) rubyApi.getInstanceVariable(connection_factory, "@java_object")).getValue();
1099
+ } catch (Exception e) {
1100
+ factory = null;
1101
+ }
1102
+ if (factory == null) {
1103
+ throw recv.getRuntime().newRuntimeError("@connection_factory not set properly");
1104
+ }
1105
+ return factory;
1106
+ }
1107
+
1108
+ private static IRubyObject config_value(IRubyObject recv, String key) {
1109
+ Ruby runtime = recv.getRuntime();
1110
+ IRubyObject config_hash = rubyApi.getInstanceVariable(recv, "@config");
1111
+ return rubyApi.callMethod(config_hash, "[]", runtime.newSymbol(key));
1112
+ }
1113
+ }