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.
- data/History.txt +12 -0
- data/Manifest.txt +13 -0
- data/Rakefile +18 -10
- data/lib/active_record/connection_adapters/derby_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/h2_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/hsqldb_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/jdbc_adapter.rb +124 -56
- data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/oracle_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +1 -0
- data/lib/jdbc_adapter/jdbc_db2.rb +21 -0
- data/lib/jdbc_adapter/jdbc_derby.rb +22 -101
- data/lib/jdbc_adapter/jdbc_firebird.rb +5 -1
- data/lib/jdbc_adapter/jdbc_hsqldb.rb +23 -1
- data/lib/jdbc_adapter/jdbc_mimer.rb +4 -2
- data/lib/jdbc_adapter/jdbc_mssql.rb +149 -67
- data/lib/jdbc_adapter/jdbc_mysql.rb +22 -2
- data/lib/jdbc_adapter/jdbc_oracle.rb +80 -15
- data/lib/jdbc_adapter/jdbc_postgre.rb +132 -4
- data/lib/jdbc_adapter_internal.jar +0 -0
- data/src/java/JDBCDerbySpec.java +323 -0
- data/src/java/JDBCMySQLSpec.java +83 -0
- data/src/java/JdbcAdapterInternalService.java +802 -0
- data/test/activerecord/connection_adapters/type_conversion_test.rb +1 -0
- data/test/db/derby.rb +2 -5
- data/test/db/h2.rb +2 -5
- data/test/db/hsqldb.rb +2 -6
- data/test/db/jdbc.rb +9 -0
- data/test/db/mysql.rb +4 -15
- data/test/db/postgres.rb +2 -3
- data/test/generic_jdbc_connection_test.rb +9 -0
- data/test/jdbc_adapter/jdbc_db2_test.rb +21 -0
- data/test/simple.rb +6 -8
- 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
|
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
|
Binary file
|
@@ -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
|
+
}
|