ActiveRecord-JDBC 0.4 → 0.5

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.
Files changed (35) hide show
  1. data/History.txt +12 -0
  2. data/Manifest.txt +13 -0
  3. data/Rakefile +18 -10
  4. data/lib/active_record/connection_adapters/derby_adapter.rb +1 -0
  5. data/lib/active_record/connection_adapters/h2_adapter.rb +1 -0
  6. data/lib/active_record/connection_adapters/hsqldb_adapter.rb +1 -0
  7. data/lib/active_record/connection_adapters/jdbc_adapter.rb +124 -56
  8. data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -0
  9. data/lib/active_record/connection_adapters/mysql_adapter.rb +1 -0
  10. data/lib/active_record/connection_adapters/oracle_adapter.rb +1 -0
  11. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1 -0
  12. data/lib/jdbc_adapter/jdbc_db2.rb +21 -0
  13. data/lib/jdbc_adapter/jdbc_derby.rb +22 -101
  14. data/lib/jdbc_adapter/jdbc_firebird.rb +5 -1
  15. data/lib/jdbc_adapter/jdbc_hsqldb.rb +23 -1
  16. data/lib/jdbc_adapter/jdbc_mimer.rb +4 -2
  17. data/lib/jdbc_adapter/jdbc_mssql.rb +149 -67
  18. data/lib/jdbc_adapter/jdbc_mysql.rb +22 -2
  19. data/lib/jdbc_adapter/jdbc_oracle.rb +80 -15
  20. data/lib/jdbc_adapter/jdbc_postgre.rb +132 -4
  21. data/lib/jdbc_adapter_internal.jar +0 -0
  22. data/src/java/JDBCDerbySpec.java +323 -0
  23. data/src/java/JDBCMySQLSpec.java +83 -0
  24. data/src/java/JdbcAdapterInternalService.java +802 -0
  25. data/test/activerecord/connection_adapters/type_conversion_test.rb +1 -0
  26. data/test/db/derby.rb +2 -5
  27. data/test/db/h2.rb +2 -5
  28. data/test/db/hsqldb.rb +2 -6
  29. data/test/db/jdbc.rb +9 -0
  30. data/test/db/mysql.rb +4 -15
  31. data/test/db/postgres.rb +2 -3
  32. data/test/generic_jdbc_connection_test.rb +9 -0
  33. data/test/jdbc_adapter/jdbc_db2_test.rb +21 -0
  34. data/test/simple.rb +6 -8
  35. metadata +15 -2
@@ -1,10 +1,48 @@
1
- module JdbcSpec
1
+ module ::JdbcSpec
2
+ module ActiveRecordExtensions
3
+ def postgresql_connection(config)
4
+ config[:port] ||= 5432
5
+ config[:url] ||= "jdbc:postgresql://#{config[:host]}:#{config[:port]}/#{config[:database]}"
6
+ config[:driver] ||= "org.postgresql.Driver"
7
+ jdbc_connection(config)
8
+ end
9
+ end
10
+
2
11
  module PostgreSQL
12
+ def self.column_selector
13
+ [/postgre/i, lambda {|cfg,col| col.extend(::JdbcSpec::PostgreSQL::Column)}]
14
+ end
15
+
16
+ def self.adapter_selector
17
+ [/postgre/i, lambda {|cfg,adapt| adapt.extend(::JdbcSpec::PostgreSQL)}]
18
+ end
19
+
3
20
  module Column
21
+ def type_cast(value)
22
+ case type
23
+ when :boolean then cast_to_boolean(value)
24
+ else super
25
+ end
26
+ end
27
+
4
28
  def simplified_type(field_type)
5
29
  return :integer if field_type =~ /^serial/i
30
+ return :string if field_type =~ /\[\]$/i || field_type =~ /^interval/i
31
+ return :string if field_type =~ /^(?:point|lseg|box|"?path"?|polygon|circle)/i
32
+ return :datetime if field_type =~ /^timestamp/i
33
+ return :float if field_type =~ /^real|^money/i
34
+ return :binary if field_type =~ /^bytea/i
35
+ return :boolean if field_type =~ /^bool/i
6
36
  super
7
37
  end
38
+
39
+ def cast_to_boolean(value)
40
+ if value == true || value == false
41
+ value
42
+ else
43
+ %w(true t 1).include?(value.to_s.downcase)
44
+ end
45
+ end
8
46
 
9
47
  def cast_to_date_or_time(value)
10
48
  return value if value.is_a? Date
@@ -126,21 +164,99 @@ module JdbcSpec
126
164
  id_value || last_insert_id(table, sequence_name || default_sequence_name(table, pk))
127
165
  end
128
166
 
167
+ def columns(table_name, name=nil)
168
+ @connection.columns_internal(table_name, name, "public")
169
+ end
170
+
129
171
  def last_insert_id(table, sequence_name)
130
172
  Integer(select_value("SELECT currval('#{sequence_name}')"))
131
173
  end
132
174
 
175
+ # the point here is really just to empty the database, not recreate it
176
+ # so we delete all tables
177
+ def recreate_database(name)
178
+ tables.each{|t| drop_table(t)}
179
+ end
180
+
181
+ def structure_dump
182
+ abcs = ActiveRecord::Base.configurations
183
+
184
+ database = nil
185
+ if abcs[RAILS_ENV]["url"] =~ /\/([^\/]*)$/
186
+ database = $1
187
+ else
188
+ raise "Could not figure out what database this url is for #{abcs[RAILS_ENV]["url"]}"
189
+ end
190
+
191
+ ENV['PGHOST'] = abcs[RAILS_ENV]["host"] if abcs[RAILS_ENV]["host"]
192
+ ENV['PGPORT'] = abcs[RAILS_ENV]["port"].to_s if abcs[RAILS_ENV]["port"]
193
+ ENV['PGPASSWORD'] = abcs[RAILS_ENV]["password"].to_s if abcs[RAILS_ENV]["password"]
194
+ search_path = abcs[RAILS_ENV]["schema_search_path"]
195
+ search_path = "--schema=#{search_path}" if search_path
196
+
197
+ @connection.connection.close
198
+ begin
199
+ file = "db/#{RAILS_ENV}_structure.sql"
200
+ `pg_dump -i -U "#{abcs[RAILS_ENV]["username"]}" -s -x -O -f #{file} #{search_path} #{database}`
201
+ raise "Error dumping database" if $?.exitstatus == 1
202
+
203
+ # need to patch away any references to SQL_ASCII as it breaks the JDBC driver
204
+ lines = File.readlines(file)
205
+ File.open(file, "w") do |io|
206
+ lines.each do |line|
207
+ line.gsub!(/SQL_ASCII/, 'UNICODE')
208
+ io.write(line)
209
+ end
210
+ end
211
+ ensure
212
+ reconnect!
213
+ end
214
+ end
215
+
133
216
  def _execute(sql, name = nil)
134
- log_no_bench(sql, name) do
135
217
  case sql.strip
136
- when /^(select|show)/i:
218
+ when /\A\(?\s*(select|show)/i:
137
219
  @connection.execute_query(sql)
138
220
  else
139
221
  @connection.execute_update(sql)
140
222
  end
141
- end
142
223
  end
143
224
 
225
+ # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
226
+ #
227
+ # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
228
+ # requires that the ORDER BY include the distinct column.
229
+ #
230
+ # distinct("posts.id", "posts.created_at desc")
231
+ def distinct(columns, order_by)
232
+ return "DISTINCT #{columns}" if order_by.blank?
233
+
234
+ # construct a clean list of column names from the ORDER BY clause, removing
235
+ # any asc/desc modifiers
236
+ order_columns = order_by.split(',').collect { |s| s.split.first }
237
+ order_columns.delete_if &:blank?
238
+ order_columns = order_columns.zip((0...order_columns.size).to_a).map { |s,i| "#{s} AS alias_#{i}" }
239
+
240
+ # return a DISTINCT ON() clause that's distinct on the columns we want but includes
241
+ # all the required columns for the ORDER BY to work properly
242
+ sql = "DISTINCT ON (#{columns}) #{columns}, "
243
+ sql << order_columns * ', '
244
+ end
245
+
246
+ # ORDER BY clause for the passed order option.
247
+ #
248
+ # PostgreSQL does not allow arbitrary ordering when using DISTINCT ON, so we work around this
249
+ # by wrapping the sql as a sub-select and ordering in that query.
250
+ def add_order_by_for_association_limiting!(sql, options)
251
+ return sql if options[:order].blank?
252
+
253
+ order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?)
254
+ order.map! { |s| 'DESC' if s =~ /\bdesc$/i }
255
+ order = order.zip((0...order.size).to_a).map { |s,i| "id_list.alias_#{i} #{s}" }.join(', ')
256
+
257
+ sql.replace "SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}"
258
+ end
259
+
144
260
  def quote(value, column = nil)
145
261
  if value.kind_of?(String) && column && column.type == :binary
146
262
  "'#{escape_bytea(value)}'"
@@ -151,10 +267,22 @@ module JdbcSpec
151
267
  end
152
268
  end
153
269
 
270
+ def escape_bytea(s)
271
+ if s
272
+ result = ''
273
+ s.each_byte { |c| result << sprintf('\\\\%03o', c) }
274
+ result
275
+ end
276
+ end
277
+
154
278
  def quote_column_name(name)
155
279
  %("#{name}")
156
280
  end
157
281
 
282
+ def quoted_date(value)
283
+ value.strftime("%Y-%m-%d %H:%M:%S.#{sprintf("%06d", value.usec)}")
284
+ end
285
+
158
286
  def rename_table(name, new_name)
159
287
  execute "ALTER TABLE #{name} RENAME TO #{new_name}"
160
288
  end
@@ -0,0 +1,323 @@
1
+ /***** BEGIN LICENSE BLOCK *****
2
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
3
+ *
4
+ * The contents of this file are subject to the Common Public
5
+ * License Version 1.0 (the "License"); you may not use this file
6
+ * except in compliance with the License. You may obtain a copy of
7
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
8
+ *
9
+ * Software distributed under the License is distributed on an "AS
10
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11
+ * implied. See the License for the specific language governing
12
+ * rights and limitations under the License.
13
+ *
14
+ * Copyright (C) 2007 Ola Bini <ola.bini@gmail.com>
15
+ *
16
+ * Alternatively, the contents of this file may be used under the terms of
17
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
18
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
19
+ * in which case the provisions of the GPL or the LGPL are applicable instead
20
+ * of those above. If you wish to allow use of your version of this file only
21
+ * under the terms of either the GPL or the LGPL, and not to allow others to
22
+ * use your version of this file under the terms of the CPL, indicate your
23
+ * decision by deleting the provisions above and replace them with the notice
24
+ * and other provisions required by the GPL or the LGPL. If you do not delete
25
+ * the provisions above, a recipient may use your version of this file under
26
+ * the terms of any one of the CPL, the GPL or the LGPL.
27
+ ***** END LICENSE BLOCK *****/
28
+
29
+ import org.jruby.Ruby;
30
+ import org.jruby.RubyModule;
31
+ import org.jruby.RubyString;
32
+ import org.jruby.RubyFloat;
33
+ import org.jruby.RubyFixnum;
34
+ import org.jruby.RubyBignum;
35
+ import org.jruby.RubyBoolean;
36
+ import org.jruby.RubyBigDecimal;
37
+ import org.jruby.RubyRange;
38
+ import org.jruby.RubyNumeric;
39
+
40
+ import org.jruby.runtime.Arity;
41
+ import org.jruby.runtime.CallbackFactory;
42
+ import org.jruby.runtime.MethodIndex;
43
+ import org.jruby.runtime.ThreadContext;
44
+ import org.jruby.runtime.builtin.IRubyObject;
45
+
46
+ import org.jruby.util.ByteList;
47
+
48
+ import java.sql.SQLException;
49
+
50
+ public class JDBCDerbySpec {
51
+ public static void load(Ruby runtime, RubyModule jdbcSpec) {
52
+ RubyModule derby = jdbcSpec.defineModuleUnder("Derby");
53
+ CallbackFactory cf = runtime.callbackFactory(JDBCDerbySpec.class);
54
+ derby.defineFastMethod("quote_string",cf.getFastSingletonMethod("quote_string",IRubyObject.class));
55
+ derby.defineFastMethod("quote",cf.getFastOptSingletonMethod("quote"));
56
+ derby.defineFastMethod("_execute",cf.getFastOptSingletonMethod("_execute"));
57
+ derby.defineFastMethod("add_limit_offset!",cf.getFastSingletonMethod("add_limit_offset", IRubyObject.class, IRubyObject.class));
58
+ derby.defineFastMethod("select_all",cf.getFastOptSingletonMethod("select_all"));
59
+ derby.defineFastMethod("select_one",cf.getFastOptSingletonMethod("select_one"));
60
+ RubyModule col = derby.defineModuleUnder("Column");
61
+ col.defineFastMethod("type_cast",cf.getFastSingletonMethod("type_cast", IRubyObject.class));
62
+ }
63
+
64
+ /*
65
+ * JdbcSpec::Derby::Column.type_cast(value)
66
+ */
67
+ public static IRubyObject type_cast(IRubyObject recv, IRubyObject value) {
68
+ Ruby runtime = recv.getRuntime();
69
+
70
+ if(value.isNil() || ((value instanceof RubyString) && value.toString().trim().equalsIgnoreCase("null"))) {
71
+ return runtime.getNil();
72
+ }
73
+
74
+ String type = recv.getInstanceVariable("@type").toString();
75
+
76
+ switch(type.charAt(0)) {
77
+ case 's': //string
78
+ return value;
79
+ case 't': //text, timestamp, time
80
+ if(type.equals("text")) {
81
+ return value;
82
+ } else {
83
+ return recv.callMethod(runtime.getCurrentContext(), "cast_to_time", value);
84
+ }
85
+ case 'i': //integer
86
+ case 'p': //primary key
87
+ if(value.respondsTo("to_i")) {
88
+ return value.callMethod(runtime.getCurrentContext(), "to_i");
89
+ } else {
90
+ return runtime.newFixnum( value.isTrue() ? 1 : 0 );
91
+ }
92
+ case 'd': //decimal, datetime, date
93
+ if(type.equals("datetime")) {
94
+ return recv.callMethod(runtime.getCurrentContext(), "cast_to_date_or_time", value);
95
+ } else if(type.equals("date")) {
96
+ return recv.getMetaClass().callMethod(runtime.getCurrentContext(), "string_to_date", value);
97
+ } else {
98
+ return recv.getMetaClass().callMethod(runtime.getCurrentContext(), "value_to_decimal", value);
99
+ }
100
+ case 'f': //float
101
+ return value.callMethod(runtime.getCurrentContext(),"to_f");
102
+ case 'b': //binary, boolean
103
+ if(type.equals("binary")) {
104
+ return recv.callMethod(runtime.getCurrentContext(), "value_to_binary", value);
105
+ } else {
106
+ return recv.getMetaClass().callMethod(runtime.getCurrentContext(), "value_to_boolean", value);
107
+ }
108
+ }
109
+ return value;
110
+ }
111
+
112
+ public static IRubyObject quote(IRubyObject recv, IRubyObject[] args) {
113
+ Ruby runtime = recv.getRuntime();
114
+ Arity.checkArgumentCount(runtime, args, 1, 2);
115
+ IRubyObject value = args[0];
116
+ if(args.length > 1) {
117
+ IRubyObject col = args[1];
118
+ IRubyObject type = col.callMethod(runtime.getCurrentContext(),"type");
119
+ if(value instanceof RubyString) {
120
+ if(type == runtime.newSymbol("string")) {
121
+ return quote_string_with_surround(runtime, "'", (RubyString)value, "'");
122
+ } else if(type == runtime.newSymbol("text")) {
123
+ return quote_string_with_surround(runtime, "CAST('", (RubyString)value, "' AS CLOB)");
124
+ } else if(type == runtime.newSymbol("binary")) {
125
+ return hexquote_string_with_surround(runtime, "CAST('", (RubyString)value, "' AS BLOB)");
126
+ } else {
127
+ // column type :integer or other numeric or date version
128
+ if(only_digits((RubyString)value)) {
129
+ return value;
130
+ } else {
131
+ return super_quote(recv, runtime, value, col);
132
+ }
133
+ }
134
+ } else if((value instanceof RubyFloat) || (value instanceof RubyFixnum) || (value instanceof RubyBignum)) {
135
+ if(type == runtime.newSymbol("string")) {
136
+ return quote_string_with_surround(runtime, "'", RubyString.objAsString(value), "'");
137
+ }
138
+ }
139
+ }
140
+ return super_quote(recv, runtime, value, runtime.getNil());
141
+ }
142
+
143
+ private final static ByteList NULL = new ByteList("NULL".getBytes());
144
+
145
+ public static IRubyObject super_quote(IRubyObject recv, Ruby runtime, IRubyObject value, IRubyObject col) {
146
+ if(value.respondsTo("quoted_id")) {
147
+ return value.callMethod(runtime.getCurrentContext(), "quoted_id");
148
+ }
149
+
150
+ IRubyObject type = (col.isNil()) ? col : col.callMethod(runtime.getCurrentContext(),"type");
151
+ if(value instanceof RubyString ||
152
+ value.isKindOf((RubyModule)(((RubyModule)((RubyModule)runtime.getModule("ActiveSupport")).getConstant("Multibyte")).getConstantAt("Chars")))) {
153
+ RubyString svalue = RubyString.objAsString(value);
154
+ if(type == runtime.newSymbol("binary") && col.getType().respondsTo("string_to_binary")) {
155
+ return quote_string_with_surround(runtime, "'", (RubyString)(col.getType().callMethod(runtime.getCurrentContext(), "string_to_binary", svalue)), "'");
156
+ } else if(type == runtime.newSymbol("integer") || type == runtime.newSymbol("float")) {
157
+ return RubyString.objAsString(((type == runtime.newSymbol("integer")) ?
158
+ svalue.callMethod(runtime.getCurrentContext(), "to_i") :
159
+ svalue.callMethod(runtime.getCurrentContext(), "to_f")));
160
+ } else {
161
+ return quote_string_with_surround(runtime, "'", svalue, "'");
162
+ }
163
+ } else if(value.isNil()) {
164
+ return runtime.newStringShared(NULL);
165
+ } else if(value instanceof RubyBoolean) {
166
+ return (value.isTrue() ?
167
+ (type == runtime.newSymbol(":integer")) ? runtime.newString("1") : recv.callMethod(runtime.getCurrentContext(),"quoted_true") :
168
+ (type == runtime.newSymbol(":integer")) ? runtime.newString("0") : recv.callMethod(runtime.getCurrentContext(),"quoted_false"));
169
+ } else if((value instanceof RubyFloat) || (value instanceof RubyFixnum) || (value instanceof RubyBignum)) {
170
+ return RubyString.objAsString(value);
171
+ } else if(value instanceof RubyBigDecimal) {
172
+ return value.callMethod(runtime.getCurrentContext(), "to_s", runtime.newString("F"));
173
+ } else if(value.isKindOf(runtime.getModule("Date"))) {
174
+ return quote_string_with_surround(runtime, "'", RubyString.objAsString(value), "'");
175
+ } else if(value.isKindOf(runtime.getModule("Time")) || value.isKindOf(runtime.getModule("DateTime"))) {
176
+ return quote_string_with_surround(runtime, "'", (RubyString)(recv.callMethod(runtime.getCurrentContext(), "quoted_date", value)), "'");
177
+ } else {
178
+ return quote_string_with_surround(runtime, "'", (RubyString)(value.callMethod(runtime.getCurrentContext(), "to_yaml")), "'");
179
+ }
180
+ }
181
+
182
+ private final static ByteList TWO_SINGLE = new ByteList(new byte[]{'\'','\''});
183
+
184
+ public static IRubyObject quote_string_with_surround(Ruby runtime, String before, RubyString string, String after) {
185
+ ByteList input = string.getByteList();
186
+ ByteList output = new ByteList(before.getBytes());
187
+ for(int i = input.begin; i< input.begin + input.realSize; i++) {
188
+ switch(input.bytes[i]) {
189
+ case '\'':
190
+ output.append(input.bytes[i]);
191
+ //FALLTHROUGH
192
+ default:
193
+ output.append(input.bytes[i]);
194
+ }
195
+
196
+ }
197
+
198
+ output.append(after.getBytes());
199
+
200
+ return runtime.newStringShared(output);
201
+ }
202
+
203
+ private final static byte[] HEX = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
204
+
205
+ public static IRubyObject hexquote_string_with_surround(Ruby runtime, String before, RubyString string, String after) {
206
+ ByteList input = string.getByteList();
207
+ ByteList output = new ByteList(before.getBytes());
208
+ for(int i = input.begin; i< input.begin + input.realSize; i++) {
209
+ byte b1 = input.bytes[i];
210
+ byte higher = HEX[(((char)b1)>>4)%16];
211
+ byte lower = HEX[((char)b1)%16];
212
+ if(b1 == '\'') {
213
+ output.append(higher);
214
+ output.append(lower);
215
+ }
216
+ output.append(higher);
217
+ output.append(lower);
218
+ }
219
+
220
+ output.append(after.getBytes());
221
+ return runtime.newStringShared(output);
222
+ }
223
+
224
+ private static boolean only_digits(RubyString inp) {
225
+ ByteList input = inp.getByteList();
226
+ for(int i = input.begin; i< input.begin + input.realSize; i++) {
227
+ if(input.bytes[i] < '0' || input.bytes[i] > '9') {
228
+ return false;
229
+ }
230
+ }
231
+ return true;
232
+ }
233
+
234
+ public static IRubyObject quote_string(IRubyObject recv, IRubyObject string) {
235
+ boolean replacementFound = false;
236
+ ByteList bl = ((RubyString) string).getByteList();
237
+
238
+ for(int i = bl.begin; i < bl.begin + bl.realSize; i++) {
239
+ switch (bl.bytes[i]) {
240
+ case '\'': break;
241
+ default: continue;
242
+ }
243
+
244
+ // On first replacement allocate a different bytelist so we don't manip original
245
+ if(!replacementFound) {
246
+ i-= bl.begin;
247
+ bl = new ByteList(bl);
248
+ replacementFound = true;
249
+ }
250
+
251
+ bl.replace(i, 1, TWO_SINGLE);
252
+ i+=1;
253
+ }
254
+ if(replacementFound) {
255
+ return recv.getRuntime().newStringShared(bl);
256
+ } else {
257
+ return string;
258
+ }
259
+ }
260
+
261
+ public static IRubyObject select_all(IRubyObject recv, IRubyObject[] args) {
262
+ return recv.callMethod(recv.getRuntime().getCurrentContext(),"execute",args);
263
+ }
264
+
265
+ public static IRubyObject select_one(IRubyObject recv, IRubyObject[] args) {
266
+ IRubyObject limit = recv.getInstanceVariable("@limit");
267
+ if(limit == null || limit.isNil()) {
268
+ recv.setInstanceVariable("@limit", recv.getRuntime().newFixnum(1));
269
+ }
270
+ try {
271
+ return recv.callMethod(recv.getRuntime().getCurrentContext(),"execute",args).callMethod(recv.getRuntime().getCurrentContext(), "first");
272
+ } finally {
273
+ recv.setInstanceVariable("@limit", recv.getRuntime().getNil());
274
+ }
275
+ }
276
+
277
+ public static IRubyObject add_limit_offset(IRubyObject recv, IRubyObject sql, IRubyObject options) {
278
+ recv.setInstanceVariable("@limit", options.callMethod(recv.getRuntime().getCurrentContext(), MethodIndex.AREF, "[]", recv.getRuntime().newSymbol("limit")));
279
+ return recv.setInstanceVariable("@offset", options.callMethod(recv.getRuntime().getCurrentContext(), MethodIndex.AREF, "[]", recv.getRuntime().newSymbol("offset")));
280
+ }
281
+
282
+ public static IRubyObject _execute(IRubyObject recv, IRubyObject[] args) throws SQLException, java.io.IOException {
283
+ Ruby runtime = recv.getRuntime();
284
+ try {
285
+ IRubyObject conn = recv.getInstanceVariable("@connection");
286
+ String sql = args[0].toString().trim().toLowerCase();
287
+ if(sql.charAt(0) == '(') {
288
+ sql = sql.substring(1).trim();
289
+ }
290
+ if(sql.startsWith("insert")) {
291
+ return JdbcAdapterInternalService.execute_insert(conn, args[0]);
292
+ } else if(sql.startsWith("select") || sql.startsWith("show")) {
293
+ IRubyObject offset = recv.getInstanceVariable("@offset");
294
+ if(offset == null || offset.isNil()) {
295
+ offset = RubyFixnum.zero(runtime);
296
+ }
297
+ IRubyObject limit = recv.getInstanceVariable("@limit");
298
+ IRubyObject range;
299
+ IRubyObject max;
300
+ if(limit == null || limit.isNil() || RubyNumeric.fix2int(limit) == -1) {
301
+ range = RubyRange.newRange(runtime, offset, runtime.newFixnum(-1), false);
302
+ max = RubyFixnum.zero(runtime);
303
+ } else {
304
+ IRubyObject v1 = offset.callMethod(runtime.getCurrentContext(), MethodIndex.OP_PLUS, "+", limit);
305
+ range = RubyRange.newRange(runtime, offset, v1, true);
306
+ max = v1.callMethod(runtime.getCurrentContext(), MethodIndex.OP_PLUS, "+", RubyFixnum.one(runtime));
307
+ }
308
+ IRubyObject ret = JdbcAdapterInternalService.execute_query(conn, new IRubyObject[]{args[0], max}).
309
+ callMethod(runtime.getCurrentContext(), MethodIndex.AREF, "[]", range);
310
+ if(ret.isNil()) {
311
+ return runtime.newArray();
312
+ } else {
313
+ return ret;
314
+ }
315
+ } else {
316
+ return JdbcAdapterInternalService.execute_update(conn, args[0]);
317
+ }
318
+ } finally {
319
+ recv.setInstanceVariable("@limit",runtime.getNil());
320
+ recv.setInstanceVariable("@offset",runtime.getNil());
321
+ }
322
+ }
323
+ }