ActiveRecord-JDBC 0.4 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
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
+ }