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.
@@ -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
+ }