Skip to content

Commit 781b792

Browse files
committed
Add execute_batch2 for sqlite3 and closed? for all adapters.
Our execute_batch2 is same name as cext method but it works with an array instead of a semicolon-delimeted string.
1 parent 304ea13 commit 781b792

File tree

4 files changed

+75
-80
lines changed

4 files changed

+75
-80
lines changed

lib/arjdbc/abstract/database_statements.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def exec_insert(sql, name = nil, binds = NO_BINDS, pk = nil, sequence_name = nil
3030

3131
# It appears that at this point (AR 5.0) "prepare" should only ever be true
3232
# if prepared statements are enabled
33-
def exec_query(sql, name = nil, binds = NO_BINDS, prepare: false)
33+
def exec_query(sql, name = nil, binds = NO_BINDS, prepare: false, async: false)
3434
if preventing_writes? && write_query?(sql)
3535
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
3636
end

lib/arjdbc/sqlite3/adapter.rb

Lines changed: 31 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
require "active_record/connection_adapters/statement_pool"
1111
require "active_record/connection_adapters/sqlite3/explain_pretty_printer"
1212
require "active_record/connection_adapters/sqlite3/quoting"
13+
require "active_record/connection_adapters/sqlite3/database_statements"
1314
require "active_record/connection_adapters/sqlite3/schema_creation"
1415
require "active_record/connection_adapters/sqlite3/schema_definitions"
1516
require "active_record/connection_adapters/sqlite3/schema_dumper"
@@ -64,6 +65,7 @@ module SQLite3
6465
# DIFFERENCE: FQN
6566
include ::ActiveRecord::ConnectionAdapters::SQLite3::Quoting
6667
include ::ActiveRecord::ConnectionAdapters::SQLite3::SchemaStatements
68+
include ::ActiveRecord::ConnectionAdapters::SQLite3::DatabaseStatements
6769

6870
NATIVE_DATABASE_TYPES = {
6971
primary_key: "integer PRIMARY KEY AUTOINCREMENT NOT NULL",
@@ -81,19 +83,28 @@ module SQLite3
8183
}
8284

8385
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
84-
alias reset clear
85-
8686
private
8787
def dealloc(stmt)
8888
stmt.close unless stmt.closed?
8989
end
9090
end
9191

9292
def initialize(connection, logger, connection_options, config)
93+
@memory_database = config[:database] == ":memory:"
9394
super(connection, logger, config)
9495
configure_connection
9596
end
9697

98+
def self.database_exists?(config)
99+
config = config.symbolize_keys
100+
if config[:database] == ":memory:"
101+
true
102+
else
103+
database_file = defined?(Rails.root) ? File.expand_path(config[:database], Rails.root) : config[:database]
104+
File.exist?(database_file)
105+
end
106+
end
107+
97108
def supports_ddl_transactions?
98109
true
99110
end
@@ -158,18 +169,10 @@ def active?
158169
!@raw_connection.closed?
159170
end
160171

161-
def reconnect!(restore_transactions: false)
162-
@lock.synchronize do
163-
if active?
164-
@connection.rollback rescue nil
165-
else
166-
connect
167-
end
168-
169-
super
170-
end
172+
def reconnect!
173+
super
174+
connect if @connection.closed?
171175
end
172-
alias :reset! :reconnect!
173176

174177
# Disconnects from the database if already connected. Otherwise, this
175178
# method does nothing.
@@ -178,7 +181,6 @@ def disconnect!
178181
@connection.close rescue nil
179182
end
180183

181-
182184
def supports_index_sort_order?
183185
true
184186
end
@@ -216,48 +218,8 @@ def disable_referential_integrity # :nodoc:
216218
end
217219
end
218220

219-
#--
220-
# DATABASE STATEMENTS ======================================
221-
#++
222-
223-
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
224-
:pragma
225-
) # :nodoc:
226-
private_constant :READ_QUERY
227-
228-
def write_query?(sql) # :nodoc:
229-
!READ_QUERY.match?(sql)
230-
end
231-
232-
def explain(arel, binds = [])
233-
sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
234-
# DIFFERENCE: FQN
235-
::ActiveRecord::ConnectionAdapters::SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
236-
end
237-
238-
# DIFFERENCE: implemented in ArJdbc::Abstract::DatabaseStatements
239-
#def exec_query(sql, name = nil, binds = [], prepare: false)
240-
241-
# DIFFERENCE: implemented in ArJdbc::Abstract::DatabaseStatements
242-
#def exec_delete(sql, name = "SQL", binds = [])
243-
244-
def last_inserted_id(result)
245-
@connection.last_insert_row_id
246-
end
247-
248-
# DIFFERENCE: implemented in ArJdbc::Abstract::DatabaseStatements
249-
#def execute(sql, name = nil) #:nodoc:
250-
251-
def begin_db_transaction #:nodoc:
252-
log("begin transaction", 'TRANSACTION') { @connection.transaction }
253-
end
254-
255-
def commit_db_transaction #:nodoc:
256-
log("commit transaction", 'TRANSACTION') { @connection.commit }
257-
end
258-
259-
def exec_rollback_db_transaction #:nodoc:
260-
log("rollback transaction", 'TRANSACTION') { @connection.rollback }
221+
def all_foreign_keys_valid? # :nodoc:
222+
execute("PRAGMA foreign_key_check").blank?
261223
end
262224

263225
# SCHEMA STATEMENTS ========================================
@@ -275,6 +237,7 @@ def remove_index(table_name, column_name = nil, **options) # :nodoc:
275237
exec_query "DROP INDEX #{quote_column_name(index_name)}"
276238
end
277239

240+
278241
# Renames a table.
279242
#
280243
# Example:
@@ -366,18 +329,6 @@ def foreign_keys(table_name)
366329
end
367330
end
368331

369-
def insert_fixtures_set(fixture_set, tables_to_delete = [])
370-
disable_referential_integrity do
371-
transaction(requires_new: true) do
372-
tables_to_delete.each { |table| delete "DELETE FROM #{quote_table_name(table)}", "Fixture Delete" }
373-
374-
fixture_set.each do |table_name, rows|
375-
rows.each { |row| insert_fixture(row, table_name) }
376-
end
377-
end
378-
end
379-
end
380-
381332
def build_insert_sql(insert) # :nodoc:
382333
sql = +"INSERT #{insert.into} #{insert.values_list}"
383334

@@ -392,18 +343,14 @@ def build_insert_sql(insert) # :nodoc:
392343
sql
393344
end
394345

395-
def shared_cache?
396-
config[:properties] && config[:properties][:shared_cache] == true
346+
def shared_cache? # :nodoc:
347+
@config.fetch(:flags, 0).anybits?(::SQLite3::Constants::Open::SHAREDCACHE)
397348
end
398349

399350
def get_database_version # :nodoc:
400351
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
401352
end
402353

403-
def build_truncate_statement(table_name)
404-
"DELETE FROM #{quote_table_name(table_name)}"
405-
end
406-
407354
def check_version
408355
if database_version < "3.8.0"
409356
raise "Your version of SQLite (#{database_version}) is too old. Active Record supports SQLite >= 3.8."
@@ -553,6 +500,7 @@ def copy_table_indexes(from, to, rename = {})
553500
options = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
554501
options[:unique] = true if index.unique
555502
options[:where] = index.where if index.where
503+
options[:order] = index.orders if index.orders
556504
add_index(to, columns, **options)
557505
end
558506
end
@@ -803,11 +751,15 @@ def self.jdbc_connection_class
803751

804752
# because the JDBC driver doesn't like multiple SQL statements in one JDBC statement
805753
def combine_multi_statements(total_sql)
806-
if total_sql.length == 1
807-
total_sql.first
808-
else
809-
total_sql
810-
end
754+
total_sql
755+
end
756+
757+
# combine
758+
def write_query?(sql) # :nodoc:
759+
return sql.any? { |stmt| super(stmt) } if sql.kind_of? Array
760+
!READ_QUERY.match?(sql)
761+
rescue ArgumentError # Invalid encoding
762+
!READ_QUERY.match?(sql.b)
811763
end
812764

813765
def initialize_type_map(m = type_map)

src/java/arjdbc/jdbc/RubyJdbcConnection.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,21 @@ public IRubyObject open_p(final ThreadContext context) {
720720
}
721721
}
722722

723+
@JRubyMethod(name = "closed?")
724+
public IRubyObject closed_p(ThreadContext context) {
725+
try {
726+
final Connection connection = getConnectionInternal(false);
727+
728+
if (connection == null) return context.fals;
729+
730+
// NOTE: isClosed method generally cannot be called to determine
731+
// whether a connection to a database is valid or invalid ...
732+
return context.runtime.newBoolean(connection.isClosed());
733+
} catch (SQLException e) {
734+
return handleException(context, e);
735+
}
736+
}
737+
723738
@JRubyMethod(name = "close")
724739
public IRubyObject close(final ThreadContext context) {
725740
final Connection connection = getConnection(false);

src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,34 @@ public IRubyObject readonly_p(final ThreadContext context) throws SQLException {
460460
return context.runtime.newBoolean(connection.isReadOnly());
461461
}
462462

463+
// note: sqlite3 cext uses this same method but we do not combine all our statements
464+
// into a single ; delimited string but leave it as an array of statements. This is
465+
// because the JDBC way of handling batches is to use addBatch().
466+
@JRubyMethod(name = "execute_batch2")
467+
public IRubyObject execute_batch2(ThreadContext context, IRubyObject statementsArg) {
468+
// Assume we will only call this with an array.
469+
final RubyArray statements = (RubyArray) statementsArg;
470+
return withConnection(context, connection -> {
471+
Statement statement = null;
472+
try {
473+
statement = createStatement(context, connection);
474+
475+
int length = statements.getLength();
476+
for (int i = 0; i < length; i++) {
477+
statement.addBatch(sqlString(statements.eltOk(i)));
478+
}
479+
statement.executeBatch();
480+
return context.nil;
481+
} catch (final SQLException e) {
482+
// Generate list semicolon list of statements which should match AR error formatting more.
483+
debugErrorSQL(context, sqlString(statements.join(context, context.runtime.newString(";\n"))));
484+
throw e;
485+
} finally {
486+
close(statement);
487+
}
488+
});
489+
}
490+
463491
@Override
464492
protected void setDecimalParameter(final ThreadContext context,
465493
final Connection connection, final PreparedStatement statement,

0 commit comments

Comments
 (0)