activerecord-jdbc-adapter 1.2.0 → 1.2.1
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/.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
|