activerecord-jdbc-adapter 1.2.0 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +5 -0
- data/Gemfile.lock +1 -1
- data/History.txt +10 -0
- data/Rakefile +5 -5
- data/lib/arel/visitors/firebird.rb +10 -3
- data/lib/arjdbc/derby/adapter.rb +0 -1
- data/lib/arjdbc/h2/adapter.rb +17 -0
- data/lib/arjdbc/hsqldb/adapter.rb +1 -2
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/jdbc/type_converter.rb +2 -2
- data/lib/arjdbc/mysql/adapter.rb +34 -43
- data/lib/arjdbc/postgresql/adapter.rb +35 -1
- data/lib/arjdbc/postgresql/connection_methods.rb +1 -0
- data/lib/arjdbc/sqlite3/adapter.rb +2 -2
- data/lib/arjdbc/version.rb +1 -1
- data/rakelib/bundler_ext.rb +11 -0
- data/rakelib/compile.rake +20 -22
- data/rakelib/db.rake +21 -13
- data/rakelib/rails.rake +1 -1
- data/src/java/arjdbc/derby/DerbyModule.java +69 -67
- data/src/java/arjdbc/sqlite3/Sqlite3RubyJdbcConnection.java +62 -0
- data/test/db/mysql.rb +3 -3
- data/test/db/postgres.rb +3 -3
- data/test/h2_change_column_test.rb +68 -0
- data/test/helper.rb +69 -0
- data/test/mysql_simple_test.rb +16 -4
- data/test/postgres_native_type_mapping_test.rb +6 -1
- data/test/postgres_simple_test.rb +12 -6
- data/test/postgres_type_conversion_test.rb +34 -0
- data/test/simple.rb +78 -6
- data/test/sqlite3_simple_test.rb +25 -3
- metadata +8 -2
data/rakelib/db.rake
CHANGED
@@ -1,19 +1,27 @@
|
|
1
1
|
namespace :db do
|
2
|
-
task :load_arjdbc do
|
3
|
-
$LOAD_PATH.unshift 'test'
|
4
|
-
$LOAD_PATH.unshift 'lib'
|
5
|
-
require 'abstract_db_create'
|
6
|
-
end
|
7
|
-
|
8
2
|
desc "Creates the test database for MySQL."
|
9
|
-
task :mysql
|
3
|
+
task :mysql do
|
10
4
|
load 'test/db/mysql.rb' rescue nil
|
11
|
-
|
12
|
-
|
5
|
+
IO.popen("mysql -u root", "w") do |io|
|
6
|
+
io.puts <<-SQL
|
7
|
+
DROP DATABASE IF EXISTS `#{MYSQL_CONFIG[:database]}`;
|
8
|
+
CREATE DATABASE `#{MYSQL_CONFIG[:database]}` DEFAULT CHARACTER SET `utf8`;
|
9
|
+
GRANT ALL PRIVILEGES ON `#{MYSQL_CONFIG[:database]}`.* TO #{MYSQL_CONFIG[:username]}@localhost;
|
10
|
+
SET PASSWORD FOR #{MYSQL_CONFIG[:username]}@localhost = PASSWORD('#{MYSQL_CONFIG[:password]}');
|
11
|
+
SQL
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Creates the test database for PostgreSQL."
|
16
|
+
task :postgres do
|
17
|
+
load 'test/db/postgres.rb' rescue nil
|
18
|
+
IO.popen("psql", "w") do |io|
|
19
|
+
io.puts <<-SQL
|
20
|
+
DROP DATABASE IF EXISTS #{POSTGRES_CONFIG[:database]};
|
21
|
+
DROP USER IF EXISTS #{POSTGRES_CONFIG[:username]};
|
22
|
+
CREATE USER #{POSTGRES_CONFIG[:username]} CREATEDB SUPERUSER LOGIN PASSWORD '#{POSTGRES_CONFIG[:password]}';
|
23
|
+
CREATE DATABASE #{POSTGRES_CONFIG[:database]} OWNER #{POSTGRES_CONFIG[:username]};
|
24
|
+
SQL
|
13
25
|
end
|
14
|
-
extend AbstractDbCreate
|
15
|
-
do_setup('arjdbc', nil)
|
16
|
-
Rake::Task['db:drop'].invoke rescue nil
|
17
|
-
Rake::Task['db:create'].invoke
|
18
26
|
end
|
19
27
|
end
|
data/rakelib/rails.rake
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
/***** BEGIN LICENSE BLOCK *****
|
2
|
-
* Copyright (c) 2006-
|
2
|
+
* Copyright (c) 2006-2011 Nick Sieger <nick@nicksieger.com>
|
3
3
|
* Copyright (c) 2006-2007 Ola Bini <ola.bini@gmail.com>
|
4
4
|
*
|
5
5
|
* Permission is hereby granted, free of charge, to any person obtaining
|
@@ -29,13 +29,8 @@ import java.sql.SQLException;
|
|
29
29
|
import arjdbc.jdbc.RubyJdbcConnection;
|
30
30
|
|
31
31
|
import org.jruby.Ruby;
|
32
|
-
import org.jruby.RubyBigDecimal;
|
33
|
-
import org.jruby.RubyBignum;
|
34
32
|
import org.jruby.RubyBoolean;
|
35
|
-
import org.jruby.RubyFixnum;
|
36
|
-
import org.jruby.RubyFloat;
|
37
33
|
import org.jruby.RubyModule;
|
38
|
-
import org.jruby.RubyNumeric;
|
39
34
|
import org.jruby.RubyObjectAdapter;
|
40
35
|
import org.jruby.RubyRange;
|
41
36
|
import org.jruby.RubyString;
|
@@ -66,39 +61,39 @@ public class DerbyModule {
|
|
66
61
|
String type = rubyApi.getInstanceVariable(recv, "@type").toString();
|
67
62
|
|
68
63
|
switch (type.charAt(0)) {
|
69
|
-
|
64
|
+
case 's': //string
|
65
|
+
return value;
|
66
|
+
case 't': //text, timestamp, time
|
67
|
+
if (type.equals("text")) {
|
70
68
|
return value;
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
return rubyApi.callMethod(
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
} else {
|
100
|
-
return rubyApi.callMethod(recv.getMetaClass(), "value_to_boolean", value);
|
101
|
-
}
|
69
|
+
} else if (type.equals("timestamp")) {
|
70
|
+
return rubyApi.callMethod(recv.getMetaClass(), "string_to_time", value);
|
71
|
+
} else { //time
|
72
|
+
return rubyApi.callMethod(recv.getMetaClass(), "string_to_dummy_time", value);
|
73
|
+
}
|
74
|
+
case 'i': //integer
|
75
|
+
case 'p': //primary key
|
76
|
+
if (value.respondsTo("to_i")) {
|
77
|
+
return rubyApi.callMethod(value, "to_i");
|
78
|
+
} else {
|
79
|
+
return runtime.newFixnum(value.isTrue() ? 1 : 0);
|
80
|
+
}
|
81
|
+
case 'd': //decimal, datetime, date
|
82
|
+
if (type.equals("datetime")) {
|
83
|
+
return rubyApi.callMethod(recv.getMetaClass(), "string_to_time", value);
|
84
|
+
} else if (type.equals("date")) {
|
85
|
+
return rubyApi.callMethod(recv.getMetaClass(), "string_to_date", value);
|
86
|
+
} else {
|
87
|
+
return rubyApi.callMethod(recv.getMetaClass(), "value_to_decimal", value);
|
88
|
+
}
|
89
|
+
case 'f': //float
|
90
|
+
return rubyApi.callMethod(value, "to_f");
|
91
|
+
case 'b': //binary, boolean
|
92
|
+
if (type.equals("binary")) {
|
93
|
+
return rubyApi.callMethod(recv.getMetaClass(), "binary_to_string", value);
|
94
|
+
} else {
|
95
|
+
return rubyApi.callMethod(recv.getMetaClass(), "value_to_boolean", value);
|
96
|
+
}
|
102
97
|
}
|
103
98
|
return value;
|
104
99
|
}
|
@@ -115,6 +110,8 @@ public class DerbyModule {
|
|
115
110
|
if (type.equals("text") || type.equals("string")) {
|
116
111
|
value = make_ruby_string_for_text_column(context, recv, runtime, value);
|
117
112
|
}
|
113
|
+
String metaClass = value.getMetaClass().getName();
|
114
|
+
|
118
115
|
if (value instanceof RubyString) {
|
119
116
|
if (type.equals("string")) {
|
120
117
|
return quote_string_with_surround(runtime, "'", (RubyString)value, "'");
|
@@ -130,7 +127,7 @@ public class DerbyModule {
|
|
130
127
|
return super_quote(context, recv, runtime, value, col);
|
131
128
|
}
|
132
129
|
}
|
133
|
-
} else if ((
|
130
|
+
} else if (metaClass.equals("Float") || metaClass.equals("Fixnum") || metaClass.equals("Bignum")) {
|
134
131
|
if (type.equals("string")) {
|
135
132
|
return quote_string_with_surround(runtime, "'", RubyString.objAsString(context, value), "'");
|
136
133
|
}
|
@@ -139,49 +136,54 @@ public class DerbyModule {
|
|
139
136
|
return super_quote(context, recv, runtime, value, runtime.getNil());
|
140
137
|
}
|
141
138
|
|
142
|
-
/*
|
139
|
+
/*
|
143
140
|
* Derby is not permissive like MySql. Try and send an Integer to a CLOB or VARCHAR column and Derby will vomit.
|
144
141
|
* This method turns non stringy things into strings.
|
145
142
|
*/
|
146
143
|
private static IRubyObject make_ruby_string_for_text_column(ThreadContext context, IRubyObject recv, Ruby runtime, IRubyObject value) {
|
147
|
-
RubyModule multibyteChars = (RubyModule)
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
144
|
+
RubyModule multibyteChars = (RubyModule)
|
145
|
+
((RubyModule) ((RubyModule) runtime.getModule("ActiveSupport")).getConstant("Multibyte")).getConstantAt("Chars");
|
146
|
+
if (value instanceof RubyString || rubyApi.isKindOf(value, multibyteChars) || value.isNil()) {
|
147
|
+
return value;
|
148
|
+
}
|
149
|
+
|
150
|
+
String metaClass = value.getMetaClass().getName();
|
151
|
+
|
152
|
+
if (value instanceof RubyBoolean) {
|
153
153
|
return value.isTrue() ? runtime.newString("1") : runtime.newString("0");
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
154
|
+
} else if (metaClass.equals("Float") || metaClass.equals("Fixnum") || metaClass.equals("Bignum")) {
|
155
|
+
return RubyString.objAsString(context, value);
|
156
|
+
} else if (metaClass.equals("BigDecimal")) {
|
157
|
+
return rubyApi.callMethod(value, "to_s", runtime.newString("F"));
|
158
|
+
} else {
|
159
|
+
if (rubyApi.callMethod(value, "acts_like?", runtime.newString("date")).isTrue() || rubyApi.callMethod(value, "acts_like?", runtime.newString("time")).isTrue()) {
|
160
|
+
return (RubyString)rubyApi.callMethod(recv, "quoted_date", value);
|
161
|
+
} else {
|
162
|
+
return (RubyString)rubyApi.callMethod(value, "to_yaml");
|
163
|
+
}
|
164
|
+
}
|
165
|
+
}
|
166
|
+
|
167
|
+
private final static ByteList NULL = new ByteList("NULL".getBytes());
|
168
168
|
|
169
169
|
private static IRubyObject super_quote(ThreadContext context, IRubyObject recv, Ruby runtime, IRubyObject value, IRubyObject col) {
|
170
170
|
if (value.respondsTo("quoted_id")) {
|
171
171
|
return rubyApi.callMethod(value, "quoted_id");
|
172
172
|
}
|
173
173
|
|
174
|
+
String metaClass = value.getMetaClass().getName();
|
175
|
+
|
174
176
|
IRubyObject type = (col.isNil()) ? col : rubyApi.callMethod(col, "type");
|
175
177
|
RubyModule multibyteChars = (RubyModule)
|
176
|
-
|
178
|
+
((RubyModule) ((RubyModule) runtime.getModule("ActiveSupport")).getConstant("Multibyte")).getConstantAt("Chars");
|
177
179
|
if (value instanceof RubyString || rubyApi.isKindOf(value, multibyteChars)) {
|
178
180
|
RubyString svalue = RubyString.objAsString(context, value);
|
179
181
|
if (type == runtime.newSymbol("binary") && col.getType().respondsTo("string_to_binary")) {
|
180
182
|
return quote_string_with_surround(runtime, "'", (RubyString)(rubyApi.callMethod(col.getType(), "string_to_binary", svalue)), "'");
|
181
183
|
} else if (type == runtime.newSymbol("integer") || type == runtime.newSymbol("float")) {
|
182
184
|
return RubyString.objAsString(context, ((type == runtime.newSymbol("integer")) ?
|
183
|
-
|
184
|
-
|
185
|
+
rubyApi.callMethod(svalue, "to_i") :
|
186
|
+
rubyApi.callMethod(svalue, "to_f")));
|
185
187
|
} else {
|
186
188
|
return quote_string_with_surround(runtime, "'", svalue, "'");
|
187
189
|
}
|
@@ -191,9 +193,9 @@ public class DerbyModule {
|
|
191
193
|
return (value.isTrue() ?
|
192
194
|
(type == runtime.newSymbol(":integer")) ? runtime.newString("1") : rubyApi.callMethod(recv, "quoted_true") :
|
193
195
|
(type == runtime.newSymbol(":integer")) ? runtime.newString("0") : rubyApi.callMethod(recv, "quoted_false"));
|
194
|
-
} else if((
|
196
|
+
} else if (metaClass.equals("Float") || metaClass.equals("Fixnum") || metaClass.equals("Bignum")) {
|
195
197
|
return RubyString.objAsString(context, value);
|
196
|
-
} else if(
|
198
|
+
} else if (metaClass.equals("BigDecimal")) {
|
197
199
|
return rubyApi.callMethod(value, "to_s", runtime.newString("F"));
|
198
200
|
} else if (rubyApi.callMethod(value, "acts_like?", runtime.newString("date")).isTrue() || rubyApi.callMethod(value, "acts_like?", runtime.newString("time")).isTrue()) {
|
199
201
|
return quote_string_with_surround(runtime, "'", (RubyString)(rubyApi.callMethod(recv, "quoted_date", value)), "'");
|
@@ -237,8 +239,8 @@ public class DerbyModule {
|
|
237
239
|
output.append(lower);
|
238
240
|
written += 2;
|
239
241
|
if(written >= 16334) { // max hex length = 16334
|
240
|
-
|
241
|
-
|
242
|
+
output.append("'||X'".getBytes());
|
243
|
+
written = 0;
|
242
244
|
}
|
243
245
|
}
|
244
246
|
|
@@ -26,10 +26,21 @@
|
|
26
26
|
|
27
27
|
package arjdbc.sqlite3;
|
28
28
|
|
29
|
+
import java.io.ByteArrayInputStream;
|
30
|
+
import java.io.IOException;
|
31
|
+
import java.sql.Connection;
|
32
|
+
import java.sql.ResultSet;
|
33
|
+
import java.sql.ResultSetMetaData;
|
34
|
+
import java.sql.SQLException;
|
35
|
+
import java.sql.Statement;
|
36
|
+
import java.sql.Types;
|
37
|
+
|
29
38
|
import arjdbc.jdbc.RubyJdbcConnection;
|
39
|
+
import arjdbc.jdbc.SQLBlock;
|
30
40
|
|
31
41
|
import org.jruby.Ruby;
|
32
42
|
import org.jruby.RubyClass;
|
43
|
+
import org.jruby.anno.JRubyMethod;
|
33
44
|
import org.jruby.runtime.ObjectAllocator;
|
34
45
|
import org.jruby.runtime.ThreadContext;
|
35
46
|
import org.jruby.runtime.builtin.IRubyObject;
|
@@ -57,8 +68,59 @@ public class Sqlite3RubyJdbcConnection extends RubyJdbcConnection {
|
|
57
68
|
}
|
58
69
|
};
|
59
70
|
|
71
|
+
@JRubyMethod(name = "last_insert_row_id")
|
72
|
+
public IRubyObject getLastInsertRowId(final ThreadContext context)
|
73
|
+
throws SQLException {
|
74
|
+
return (IRubyObject) withConnectionAndRetry(context, new SQLBlock() {
|
75
|
+
public Object call(Connection c) throws SQLException {
|
76
|
+
Statement stmt = null;
|
77
|
+
try {
|
78
|
+
stmt = c.createStatement();
|
79
|
+
return unmarshal_id_result(context.getRuntime(),
|
80
|
+
stmt.getGeneratedKeys());
|
81
|
+
} catch (SQLException sqe) {
|
82
|
+
if (context.getRuntime().isDebug()) {
|
83
|
+
System.out.println("Error SQL:" + sqe.getMessage());
|
84
|
+
}
|
85
|
+
throw sqe;
|
86
|
+
} finally {
|
87
|
+
close(stmt);
|
88
|
+
}
|
89
|
+
}
|
90
|
+
});
|
91
|
+
}
|
92
|
+
|
60
93
|
@Override
|
61
94
|
protected IRubyObject tables(ThreadContext context, String catalog, String schemaPattern, String tablePattern, String[] types) {
|
62
95
|
return (IRubyObject) withConnectionAndRetry(context, tableLookupBlock(context.getRuntime(), catalog, schemaPattern, tablePattern, types, true));
|
63
96
|
}
|
97
|
+
|
98
|
+
@Override
|
99
|
+
protected IRubyObject jdbcToRuby(Ruby runtime, int column, int type, ResultSet resultSet)
|
100
|
+
throws SQLException {
|
101
|
+
try {
|
102
|
+
// This is rather gross, and only needed because the resultset metadata for SQLite tries to be overly
|
103
|
+
// clever, and returns a type for the column of the "current" row, so an integer value stored in a
|
104
|
+
// decimal column is returned as Types.INTEGER. Therefore, if the first row of a resultset was an
|
105
|
+
// integer value, all rows of that result set would get truncated.
|
106
|
+
if( resultSet instanceof ResultSetMetaData ) {
|
107
|
+
type = ((ResultSetMetaData)resultSet).getColumnType(column);
|
108
|
+
}
|
109
|
+
switch (type) {
|
110
|
+
case Types.BINARY:
|
111
|
+
case Types.BLOB:
|
112
|
+
case Types.LONGVARBINARY:
|
113
|
+
case Types.VARBINARY:
|
114
|
+
return streamToRuby(runtime, resultSet, new ByteArrayInputStream(resultSet.getBytes(column)));
|
115
|
+
case Types.LONGVARCHAR:
|
116
|
+
return runtime.is1_9() ?
|
117
|
+
readerToRuby(runtime, resultSet, resultSet.getCharacterStream(column)) :
|
118
|
+
streamToRuby(runtime, resultSet, new ByteArrayInputStream(resultSet.getBytes(column)));
|
119
|
+
default:
|
120
|
+
return super.jdbcToRuby(runtime, column, type, resultSet);
|
121
|
+
}
|
122
|
+
} catch (IOException ioe) {
|
123
|
+
throw (SQLException) new SQLException(ioe.getMessage()).initCause(ioe);
|
124
|
+
}
|
125
|
+
}
|
64
126
|
}
|
data/test/db/mysql.rb
CHANGED
data/test/db/postgres.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
POSTGRES_CONFIG = {
|
2
2
|
:adapter => 'postgresql',
|
3
|
-
:database => '
|
3
|
+
:database => 'arjdbc_test',
|
4
4
|
:host => 'localhost',
|
5
|
-
:username => '
|
6
|
-
:password => ''
|
5
|
+
:username => 'arjdbc',
|
6
|
+
:password => 'arjdbc'
|
7
7
|
}
|
8
8
|
|
9
9
|
ActiveRecord::Base.establish_connection(POSTGRES_CONFIG)
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'jdbc_common'
|
2
|
+
require 'db/h2'
|
3
|
+
|
4
|
+
class H2ChangeColumnTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
class Person < ActiveRecord::Base; end
|
7
|
+
|
8
|
+
class CreatePeopleTable < ActiveRecord::Migration
|
9
|
+
def self.up
|
10
|
+
create_table :people do |t|
|
11
|
+
t.integer :phone
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.down
|
16
|
+
drop_table :people
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def setup
|
21
|
+
CreatePeopleTable.up
|
22
|
+
end
|
23
|
+
|
24
|
+
def teardown
|
25
|
+
CreatePeopleTable.down
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_should_change_column_type
|
29
|
+
ActiveRecord::Migration.change_column :people, :phone, :string
|
30
|
+
Person.reset_column_information
|
31
|
+
|
32
|
+
p = Person.create!(:phone => 'ABC')
|
33
|
+
assert_equal 'ABC', p.phone
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_sets_defaults_on_column
|
37
|
+
ActiveRecord::Migration.change_column :people, :phone, :string, :default => '123456'
|
38
|
+
Person.reset_column_information
|
39
|
+
|
40
|
+
p = Person.create!
|
41
|
+
assert_equal '123456', p.phone
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_should_change_column_default_value
|
45
|
+
ActiveRecord::Migration.add_column :people, :email, :string, :default => 'foo@example.com'
|
46
|
+
ActiveRecord::Migration.change_column :people, :email, :string, :default => 'bar@example.com'
|
47
|
+
Person.reset_column_information
|
48
|
+
|
49
|
+
p = Person.create!
|
50
|
+
assert_equal 'bar@example.com', p.email
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_should_set_non_null_restriction
|
54
|
+
ActiveRecord::Migration.change_column :people, :phone, :string, :null => false
|
55
|
+
Person.reset_column_information
|
56
|
+
assert_raises(ActiveRecord::StatementInvalid) { Person.create! }
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_should_set_null_restriction_with_default
|
60
|
+
p = Person.create!
|
61
|
+
ActiveRecord::Migration.change_column :people, :phone, :string, :null => true, :default => '123456'
|
62
|
+
Person.reset_column_information
|
63
|
+
|
64
|
+
assert_nil p.reload.phone
|
65
|
+
assert_equal '123456', Person.create!.phone
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
data/test/helper.rb
CHANGED
@@ -3,3 +3,72 @@ module Kernel
|
|
3
3
|
ENV['PATH'].split(File::PATH_SEPARATOR).detect {|p| File.executable?(File.join(p, name))}
|
4
4
|
end
|
5
5
|
end
|
6
|
+
|
7
|
+
# assert_queries and SQLCounter taken from rails active_record tests
|
8
|
+
require 'test/unit'
|
9
|
+
class Test::Unit::TestCase
|
10
|
+
def assert_queries(num = 1)
|
11
|
+
ActiveRecord::SQLCounter.log = []
|
12
|
+
yield
|
13
|
+
ensure
|
14
|
+
assert_equal num, ActiveRecord::SQLCounter.log.size, "#{ActiveRecord::SQLCounter.log.size} instead of #{num} queries were executed.#{ActiveRecord::SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{ActiveRecord::SQLCounter.log.join("\n")}"}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
require 'active_support/notifications'
|
19
|
+
module ActiveRecord
|
20
|
+
class SQLCounter
|
21
|
+
def self.ignored_sql
|
22
|
+
@@ignored_sql
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.ignored_sql=(value)
|
26
|
+
@@ignored_sql = value
|
27
|
+
end
|
28
|
+
|
29
|
+
self.ignored_sql = [
|
30
|
+
/^PRAGMA (?!(table_info))/,
|
31
|
+
/^SELECT currval/,
|
32
|
+
/^SELECT CAST/,
|
33
|
+
/^SELECT @@IDENTITY/,
|
34
|
+
/^SELECT @@ROWCOUNT/,
|
35
|
+
/^SAVEPOINT/,
|
36
|
+
/^ROLLBACK TO SAVEPOINT/,
|
37
|
+
/^RELEASE SAVEPOINT/,
|
38
|
+
/^SHOW max_identifier_length/,
|
39
|
+
/^BEGIN/,
|
40
|
+
/^COMMIT/
|
41
|
+
]
|
42
|
+
|
43
|
+
# FIXME: this needs to be refactored so specific database can add their own
|
44
|
+
# ignored SQL. This ignored SQL is for Oracle.
|
45
|
+
ignored_sql.concat [/^select .*nextval/i,
|
46
|
+
/^SAVEPOINT/,
|
47
|
+
/^ROLLBACK TO/,
|
48
|
+
/^\s*select .* from all_triggers/im
|
49
|
+
]
|
50
|
+
|
51
|
+
def self.log=(v)
|
52
|
+
@@log = v
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.log
|
56
|
+
@@log
|
57
|
+
end
|
58
|
+
|
59
|
+
self.log = []
|
60
|
+
|
61
|
+
def call(name, start, finish, message_id, values)
|
62
|
+
sql = values[:sql]
|
63
|
+
|
64
|
+
# FIXME: this seems bad. we should probably have a better way to indicate
|
65
|
+
# the query was cached
|
66
|
+
unless 'CACHE' == values[:name]
|
67
|
+
self.class.log << sql unless self.class.ignored_sql.
|
68
|
+
any? { |r| sql =~ r }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
ActiveSupport::Notifications.subscribe('sql.active_record', SQLCounter.new)
|
74
|
+
end
|