activerecord-jdbc-adapter 50.8-java → 51.1-java
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.
- checksums.yaml +5 -5
- data/.gitignore +1 -2
- data/.travis.yml +26 -51
- data/Gemfile +3 -1
- data/README.md +9 -11
- data/Rakefile +15 -78
- data/activerecord-jdbc-adapter.gemspec +2 -2
- data/lib/active_record/connection_adapters/mssql_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -0
- data/lib/arjdbc/abstract/core.rb +4 -12
- data/lib/arjdbc/abstract/database_statements.rb +4 -10
- data/lib/arjdbc/abstract/statement_cache.rb +4 -4
- data/lib/arjdbc/abstract/transaction_support.rb +2 -9
- data/lib/arjdbc/jdbc.rb +4 -0
- data/lib/arjdbc/jdbc/column.rb +11 -5
- data/lib/arjdbc/jdbc/connection_methods.rb +9 -2
- data/lib/arjdbc/jdbc/error.rb +1 -1
- data/lib/arjdbc/jdbc/jdbc.rake +4 -0
- data/lib/arjdbc/mssql.rb +7 -0
- data/lib/arjdbc/mssql/adapter.rb +804 -0
- data/lib/arjdbc/mssql/column.rb +200 -0
- data/lib/arjdbc/mssql/connection_methods.rb +79 -0
- data/lib/arjdbc/mssql/explain_support.rb +99 -0
- data/lib/arjdbc/mssql/limit_helpers.rb +231 -0
- data/lib/arjdbc/mssql/lock_methods.rb +77 -0
- data/lib/arjdbc/mssql/types.rb +343 -0
- data/lib/arjdbc/mssql/utils.rb +82 -0
- data/lib/arjdbc/mysql/adapter.rb +14 -11
- data/lib/arjdbc/mysql/connection_methods.rb +9 -18
- data/lib/arjdbc/postgresql/adapter.rb +108 -59
- data/lib/arjdbc/postgresql/column.rb +3 -6
- data/lib/arjdbc/postgresql/connection_methods.rb +3 -12
- data/lib/arjdbc/postgresql/oid_types.rb +14 -93
- data/lib/arjdbc/sqlite3/adapter.rb +171 -140
- data/lib/arjdbc/sqlite3/connection_methods.rb +1 -2
- data/lib/arjdbc/tasks/database_tasks.rb +36 -16
- data/lib/arjdbc/tasks/databases.rake +75 -32
- data/lib/arjdbc/tasks/databases3.rake +215 -0
- data/lib/arjdbc/tasks/databases4.rake +39 -0
- data/lib/arjdbc/version.rb +1 -1
- data/rakelib/01-tomcat.rake +2 -2
- data/rakelib/02-test.rake +3 -0
- data/rakelib/compile.rake +70 -0
- data/rakelib/db.rake +7 -21
- data/rakelib/rails.rake +4 -5
- data/src/java/arjdbc/ArJdbcModule.java +15 -5
- data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +2 -2
- data/src/java/arjdbc/jdbc/ConnectionFactory.java +87 -0
- data/src/java/arjdbc/jdbc/DataSourceConnectionFactory.java +1 -0
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +41 -120
- data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +14 -310
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +2 -2
- data/src/java/arjdbc/postgresql/ByteaUtils.java +1 -0
- data/src/java/arjdbc/postgresql/PgResultSetMetaDataWrapper.java +23 -0
- data/src/java/arjdbc/postgresql/PostgreSQLResult.java +13 -21
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +46 -41
- data/src/java/arjdbc/util/DateTimeUtils.java +5 -141
- data/src/java/arjdbc/util/QuotingUtils.java +7 -6
- data/src/java/arjdbc/util/StringHelper.java +20 -6
- metadata +25 -16
- data/src/java/arjdbc/jdbc/RubyConnectionFactory.java +0 -61
- data/src/java/arjdbc/postgresql/PgDateTimeUtils.java +0 -52
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
ArJdbc::ConnectionMethods.module_eval do
|
3
3
|
def postgresql_connection(config)
|
4
|
-
config = config.deep_dup
|
5
4
|
# NOTE: this isn't "really" necessary but Rails (in tests) assumes being able to :
|
6
5
|
# ActiveRecord::Base.postgresql_connection ActiveRecord::Base.configurations['arunit'].merge(:insert_returning => false)
|
7
6
|
# ... while using symbols by default but than configurations returning string keys ;(
|
@@ -17,8 +16,7 @@ ArJdbc::ConnectionMethods.module_eval do
|
|
17
16
|
::Jdbc::Postgres.load_driver(:require) if defined?(::Jdbc::Postgres.load_driver)
|
18
17
|
rescue LoadError # assuming driver.jar is on the class-path
|
19
18
|
end
|
20
|
-
driver =
|
21
|
-
defined?(::Jdbc::Postgres.driver_name) ? ::Jdbc::Postgres.driver_name : 'org.postgresql.Driver')
|
19
|
+
driver = config[:driver] ||= 'org.postgresql.Driver'
|
22
20
|
|
23
21
|
host = config[:host] ||= ( config[:hostaddr] || ENV['PGHOST'] || 'localhost' )
|
24
22
|
port = config[:port] ||= ( ENV['PGPORT'] || 5432 )
|
@@ -53,15 +51,8 @@ ArJdbc::ConnectionMethods.module_eval do
|
|
53
51
|
properties['tcpKeepAlive'] ||= config[:keepalives] if config.key?(:keepalives)
|
54
52
|
properties['kerberosServerName'] ||= config[:krbsrvname] if config[:krbsrvname]
|
55
53
|
|
56
|
-
|
57
|
-
|
58
|
-
if prepared_statements
|
59
|
-
# this makes the pgjdbc driver handle hot compatibility internally
|
60
|
-
properties['autosave'] ||= 'conservative'
|
61
|
-
else
|
62
|
-
# If prepared statements are off, lets make sure they are really *off*
|
63
|
-
properties['prepareThreshold'] = 0
|
64
|
-
end
|
54
|
+
# If prepared statements are off, lets make sure they are really *off*
|
55
|
+
properties['prepareThreshold'] ||= 0 unless config[:prepared_statements]
|
65
56
|
|
66
57
|
jdbc_connection(config)
|
67
58
|
end
|
@@ -9,61 +9,6 @@ module ArJdbc
|
|
9
9
|
# @private
|
10
10
|
OID = ::ActiveRecord::ConnectionAdapters::PostgreSQL::OID
|
11
11
|
|
12
|
-
# this version makes sure to register the types by name as well
|
13
|
-
# we still need to version with OID since it's used from SchemaStatements as well
|
14
|
-
class ArjdbcTypeMapInitializer < OID::TypeMapInitializer
|
15
|
-
private
|
16
|
-
|
17
|
-
def name_with_ns(row)
|
18
|
-
if row['in_ns']
|
19
|
-
row['typname']
|
20
|
-
else
|
21
|
-
%Q("#{row['nspname']}"."#{row['typname']}")
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def register_enum_type(row)
|
26
|
-
super
|
27
|
-
register name_with_ns(row), OID::Enum.new
|
28
|
-
end
|
29
|
-
|
30
|
-
def register_array_type(row)
|
31
|
-
super
|
32
|
-
register_with_subtype(name_with_ns(row), row['typelem'].to_i) do |subtype|
|
33
|
-
OID::Array.new(subtype, row['typdelim'])
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def register_range_type(row)
|
38
|
-
super
|
39
|
-
name = name_with_ns(row)
|
40
|
-
register_with_subtype(name, row['rngsubtype'].to_i) do |subtype|
|
41
|
-
OID::Range.new(subtype, name.to_sym)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def register_domain_type(row)
|
46
|
-
if base_type = @store.lookup(row['typbasetype'].to_i)
|
47
|
-
register row['oid'], base_type
|
48
|
-
register name_with_ns(row), base_type
|
49
|
-
else
|
50
|
-
warn "unknown base type (OID: #{row['typbasetype']}) for domain #{row['typname']}."
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def register_composite_type(row)
|
55
|
-
if subtype = @store.lookup(row['typelem'].to_i)
|
56
|
-
register row['oid'], OID::Vector.new(row['typdelim'], subtype)
|
57
|
-
register name_with_ns(row), OID::Vector.new(row['typdelim'], subtype)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def assert_valid_registration(oid, oid_type)
|
62
|
-
ret = super
|
63
|
-
ret == 0 ? oid : ret
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
12
|
# @private
|
68
13
|
module OIDTypes
|
69
14
|
|
@@ -96,7 +41,7 @@ module ArJdbc
|
|
96
41
|
|
97
42
|
def get_oid_type(oid, fmod, column_name, sql_type = '') # :nodoc:
|
98
43
|
if !type_map.key?(oid)
|
99
|
-
load_additional_types(type_map, oid)
|
44
|
+
load_additional_types(type_map, [oid])
|
100
45
|
end
|
101
46
|
|
102
47
|
type_map.fetch(oid, fmod, sql_type) {
|
@@ -124,7 +69,7 @@ module ArJdbc
|
|
124
69
|
register_class_with_limit m, 'int2', Type::Integer
|
125
70
|
register_class_with_limit m, 'int4', Type::Integer
|
126
71
|
register_class_with_limit m, 'int8', Type::Integer
|
127
|
-
m.
|
72
|
+
m.register_type 'oid', OID::Oid.new
|
128
73
|
m.register_type 'float4', Type::Float.new
|
129
74
|
m.alias_type 'float8', 'float4'
|
130
75
|
m.register_type 'text', Type::Text.new
|
@@ -159,15 +104,10 @@ module ArJdbc
|
|
159
104
|
m.register_type 'polygon', OID::SpecializedString.new(:polygon)
|
160
105
|
m.register_type 'circle', OID::SpecializedString.new(:circle)
|
161
106
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
# m.register_type 'interval' do |_, _, sql_type|
|
167
|
-
# precision = extract_precision(sql_type)
|
168
|
-
# OID::SpecializedString.new(:interval, precision: precision)
|
169
|
-
# end
|
170
|
-
m.register_type 'interval', OID::SpecializedString.new(:interval)
|
107
|
+
m.register_type 'interval' do |_, _, sql_type|
|
108
|
+
precision = extract_precision(sql_type)
|
109
|
+
OID::SpecializedString.new(:interval, precision: precision)
|
110
|
+
end
|
171
111
|
|
172
112
|
register_class_with_precision m, 'time', Type::Time
|
173
113
|
register_class_with_precision m, 'timestamp', OID::DateTime
|
@@ -193,45 +133,26 @@ module ArJdbc
|
|
193
133
|
end
|
194
134
|
|
195
135
|
load_additional_types(m)
|
196
|
-
|
197
|
-
# pgjdbc returns these if the column is auto-incrmenting
|
198
|
-
m.alias_type 'serial', 'int4'
|
199
|
-
m.alias_type 'bigserial', 'int8'
|
200
136
|
end
|
201
137
|
|
202
|
-
def load_additional_types(type_map,
|
203
|
-
initializer =
|
138
|
+
def load_additional_types(type_map, oids = nil) # :nodoc:
|
139
|
+
initializer = OID::TypeMapInitializer.new(type_map)
|
204
140
|
|
205
141
|
if supports_ranges?
|
206
142
|
query = <<-SQL
|
207
|
-
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
|
208
|
-
ns.nspname, ns.nspname = ANY(current_schemas(true)) in_ns
|
143
|
+
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
|
209
144
|
FROM pg_type as t
|
210
145
|
LEFT JOIN pg_range as r ON oid = rngtypid
|
211
|
-
JOIN pg_namespace AS ns ON t.typnamespace = ns.oid
|
212
146
|
SQL
|
213
147
|
else
|
214
148
|
query = <<-SQL
|
215
|
-
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
|
216
|
-
ns.nspname, ns.nspname = ANY(current_schemas(true)) in_ns
|
149
|
+
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
|
217
150
|
FROM pg_type as t
|
218
|
-
JOIN pg_namespace AS ns ON t.typnamespace = ns.oid
|
219
151
|
SQL
|
220
152
|
end
|
221
153
|
|
222
|
-
if
|
223
|
-
|
224
|
-
# numeric OID
|
225
|
-
query += "WHERE t.oid::integer = %s" % oid
|
226
|
-
|
227
|
-
elsif m = oid.match(/"?(\w+)"?\."?(\w+)"?/)
|
228
|
-
# namespace and type name
|
229
|
-
query += "WHERE ns.nspname = '%s' AND t.typname = '%s'" % [m[1], m[2]]
|
230
|
-
|
231
|
-
else
|
232
|
-
# only type name
|
233
|
-
query += "WHERE t.typname = '%s' AND ns.nspname = ANY(current_schemas(true))" % oid
|
234
|
-
end
|
154
|
+
if oids
|
155
|
+
query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
|
235
156
|
else
|
236
157
|
query += initializer.query_conditions_for_initial_load(type_map)
|
237
158
|
end
|
@@ -255,8 +176,8 @@ module ArJdbc
|
|
255
176
|
ActiveRecord::Type.register(:json, OID::Json, adapter: :postgresql)
|
256
177
|
ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql)
|
257
178
|
ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql)
|
258
|
-
ActiveRecord::Type.register(:point, OID::
|
259
|
-
ActiveRecord::Type.register(:legacy_point, OID::
|
179
|
+
ActiveRecord::Type.register(:point, OID::Point, adapter: :postgresql)
|
180
|
+
ActiveRecord::Type.register(:legacy_point, OID::LegacyPoint, adapter: :postgresql)
|
260
181
|
ActiveRecord::Type.register(:uuid, OID::Uuid, adapter: :postgresql)
|
261
182
|
ActiveRecord::Type.register(:vector, OID::Vector, adapter: :postgresql)
|
262
183
|
ActiveRecord::Type.register(:xml, OID::Xml, adapter: :postgresql)
|
@@ -4,11 +4,14 @@ require "arjdbc/abstract/core"
|
|
4
4
|
require "arjdbc/abstract/database_statements"
|
5
5
|
require 'arjdbc/abstract/statement_cache'
|
6
6
|
require "arjdbc/abstract/transaction_support"
|
7
|
+
require "active_record/connection_adapters/abstract_adapter"
|
7
8
|
require "active_record/connection_adapters/statement_pool"
|
8
|
-
require "active_record/connection_adapters/abstract/database_statements"
|
9
9
|
require "active_record/connection_adapters/sqlite3/explain_pretty_printer"
|
10
10
|
require "active_record/connection_adapters/sqlite3/quoting"
|
11
11
|
require "active_record/connection_adapters/sqlite3/schema_creation"
|
12
|
+
require "active_record/connection_adapters/sqlite3/schema_definitions"
|
13
|
+
require "active_record/connection_adapters/sqlite3/schema_dumper"
|
14
|
+
require "active_record/connection_adapters/sqlite3/schema_statements"
|
12
15
|
|
13
16
|
module ArJdbc
|
14
17
|
# All the code in this module is a copy of ConnectionAdapters::SQLite3Adapter from active_record 5.
|
@@ -28,7 +31,10 @@ module ArJdbc
|
|
28
31
|
|
29
32
|
ADAPTER_NAME = 'SQLite'.freeze
|
30
33
|
|
31
|
-
|
34
|
+
# DIFFERENCE: FQN
|
35
|
+
include ::ActiveRecord::ConnectionAdapters::SQLite3::Quoting
|
36
|
+
include ::ActiveRecord::ConnectionAdapters::SQLite3::ColumnDumper
|
37
|
+
include ::ActiveRecord::ConnectionAdapters::SQLite3::SchemaStatements
|
32
38
|
|
33
39
|
NATIVE_DATABASE_TYPES = {
|
34
40
|
primary_key: "INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL",
|
@@ -52,19 +58,28 @@ module ArJdbc
|
|
52
58
|
end
|
53
59
|
end
|
54
60
|
|
61
|
+
def update_table_definition(table_name, base) # :nodoc:
|
62
|
+
# DIFFERENCE: FQN
|
63
|
+
::ActiveRecord::ConnectionAdapters::SQLite3::Table.new(table_name, base)
|
64
|
+
end
|
65
|
+
|
55
66
|
def schema_creation # :nodoc:
|
56
|
-
|
67
|
+
# DIFFERENCE: FQN
|
68
|
+
::ActiveRecord::ConnectionAdapters::SQLite3::SchemaCreation.new self
|
57
69
|
end
|
58
70
|
|
59
71
|
def arel_visitor # :nodoc:
|
60
72
|
Arel::Visitors::SQLite.new(self)
|
61
73
|
end
|
62
74
|
|
63
|
-
|
75
|
+
# DIFFERENCE: we remove connection_options because we are not using it.
|
76
|
+
def initialize(connection, logger, config)
|
64
77
|
super(connection, logger, config)
|
65
78
|
|
66
79
|
@active = nil
|
67
80
|
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
|
81
|
+
|
82
|
+
configure_connection
|
68
83
|
end
|
69
84
|
|
70
85
|
def supports_ddl_transactions?
|
@@ -85,17 +100,12 @@ module ArJdbc
|
|
85
100
|
true
|
86
101
|
end
|
87
102
|
|
88
|
-
|
89
|
-
def supports_migrations? #:nodoc:
|
90
|
-
true
|
91
|
-
end
|
92
|
-
|
93
|
-
def supports_primary_key? #:nodoc:
|
103
|
+
def requires_reloading?
|
94
104
|
true
|
95
105
|
end
|
96
106
|
|
97
|
-
def
|
98
|
-
|
107
|
+
def supports_foreign_keys_in_create?
|
108
|
+
sqlite_version >= "3.6.19"
|
99
109
|
end
|
100
110
|
|
101
111
|
def supports_views?
|
@@ -131,10 +141,6 @@ module ArJdbc
|
|
131
141
|
true
|
132
142
|
end
|
133
143
|
|
134
|
-
def valid_type?(type)
|
135
|
-
true
|
136
|
-
end
|
137
|
-
|
138
144
|
# Returns 62. SQLite supports index names up to 64
|
139
145
|
# characters. The rest is used by Rails internally to perform
|
140
146
|
# temporary rename operations
|
@@ -155,43 +161,59 @@ module ArJdbc
|
|
155
161
|
true
|
156
162
|
end
|
157
163
|
|
164
|
+
# REFERENTIAL INTEGRITY ====================================
|
165
|
+
|
166
|
+
def disable_referential_integrity # :nodoc:
|
167
|
+
old = query_value("PRAGMA foreign_keys")
|
168
|
+
|
169
|
+
begin
|
170
|
+
execute("PRAGMA foreign_keys = OFF")
|
171
|
+
yield
|
172
|
+
ensure
|
173
|
+
execute("PRAGMA foreign_keys = #{old}")
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
158
177
|
#--
|
159
178
|
# DATABASE STATEMENTS ======================================
|
160
179
|
#++
|
161
180
|
|
162
181
|
def explain(arel, binds = [])
|
163
182
|
sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
|
183
|
+
# DIFFERENCE: FQN
|
164
184
|
::ActiveRecord::ConnectionAdapters::SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
|
165
185
|
end
|
166
186
|
|
167
187
|
def exec_query(sql, name = nil, binds = [], prepare: false)
|
168
|
-
type_casted_binds = binds
|
169
|
-
|
170
|
-
log(sql, name, binds) do
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
188
|
+
type_casted_binds = type_casted_binds(binds)
|
189
|
+
|
190
|
+
log(sql, name, binds, type_casted_binds) do
|
191
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
192
|
+
# Don't cache statements if they are not prepared
|
193
|
+
unless prepare
|
194
|
+
stmt = @connection.prepare(sql)
|
195
|
+
begin
|
196
|
+
cols = stmt.columns
|
197
|
+
unless without_prepared_statement?(binds)
|
198
|
+
stmt.bind_params(type_casted_binds)
|
199
|
+
end
|
200
|
+
records = stmt.to_a
|
201
|
+
ensure
|
202
|
+
stmt.close
|
178
203
|
end
|
204
|
+
else
|
205
|
+
cache = @statements[sql] ||= {
|
206
|
+
stmt: @connection.prepare(sql)
|
207
|
+
}
|
208
|
+
stmt = cache[:stmt]
|
209
|
+
cols = cache[:cols] ||= stmt.columns
|
210
|
+
stmt.reset!
|
211
|
+
stmt.bind_params(type_casted_binds)
|
179
212
|
records = stmt.to_a
|
180
|
-
ensure
|
181
|
-
stmt.close
|
182
213
|
end
|
183
|
-
stmt = records
|
184
|
-
else
|
185
|
-
cache = @statements[sql] ||= {
|
186
|
-
:stmt => @connection.prepare(sql)
|
187
|
-
}
|
188
|
-
stmt = cache[:stmt]
|
189
|
-
cols = cache[:cols] ||= stmt.columns
|
190
|
-
stmt.reset!
|
191
|
-
stmt.bind_params(type_casted_binds)
|
192
|
-
end
|
193
214
|
|
194
|
-
|
215
|
+
ActiveRecord::Result.new(cols, records)
|
216
|
+
end
|
195
217
|
end
|
196
218
|
end
|
197
219
|
|
@@ -206,7 +228,11 @@ module ArJdbc
|
|
206
228
|
end
|
207
229
|
|
208
230
|
def execute(sql, name = nil) #:nodoc:
|
209
|
-
log(sql, name)
|
231
|
+
log(sql, name) do
|
232
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
233
|
+
@connection.execute(sql)
|
234
|
+
end
|
235
|
+
end
|
210
236
|
end
|
211
237
|
|
212
238
|
def begin_db_transaction #:nodoc:
|
@@ -223,80 +249,30 @@ module ArJdbc
|
|
223
249
|
|
224
250
|
# SCHEMA STATEMENTS ========================================
|
225
251
|
|
226
|
-
def
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
235
|
-
Passing arguments to #tables is deprecated without replacement.
|
236
|
-
MSG
|
252
|
+
def new_column_from_field(table_name, field) # :nondoc:
|
253
|
+
case field["dflt_value"]
|
254
|
+
when /^null$/i
|
255
|
+
field["dflt_value"] = nil
|
256
|
+
when /^'(.*)'$/m
|
257
|
+
field["dflt_value"] = $1.gsub("''", "'")
|
258
|
+
when /^"(.*)"$/m
|
259
|
+
field["dflt_value"] = $1.gsub('""', '"')
|
237
260
|
end
|
238
261
|
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
select_values("SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'", "SCHEMA")
|
244
|
-
end
|
245
|
-
|
246
|
-
def table_exists?(table_name)
|
247
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
248
|
-
#table_exists? currently checks both tables and views.
|
249
|
-
This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
|
250
|
-
Use #data_source_exists? instead.
|
251
|
-
MSG
|
252
|
-
|
253
|
-
data_source_exists?(table_name)
|
254
|
-
end
|
255
|
-
|
256
|
-
def data_source_exists?(table_name)
|
257
|
-
return false unless table_name.present?
|
258
|
-
|
259
|
-
sql = "SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'"
|
260
|
-
sql << " AND name = #{quote(table_name)}"
|
261
|
-
|
262
|
-
select_values(sql, "SCHEMA").any?
|
263
|
-
end
|
264
|
-
|
265
|
-
def views # :nodoc:
|
266
|
-
select_values("SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'", "SCHEMA")
|
267
|
-
end
|
268
|
-
|
269
|
-
def view_exists?(view_name) # :nodoc:
|
270
|
-
return false unless view_name.present?
|
271
|
-
|
272
|
-
sql = "SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'"
|
273
|
-
sql << " AND name = #{quote(view_name)}"
|
274
|
-
|
275
|
-
select_values(sql, "SCHEMA").any?
|
276
|
-
end
|
277
|
-
|
278
|
-
# Returns an array of +Column+ objects for the table specified by +table_name+.
|
279
|
-
def columns(table_name) # :nodoc:
|
280
|
-
table_name = table_name.to_s
|
281
|
-
table_structure(table_name).map do |field|
|
282
|
-
case field["dflt_value"]
|
283
|
-
when /^null$/i
|
284
|
-
field["dflt_value"] = nil
|
285
|
-
when /^'(.*)'$/m
|
286
|
-
field["dflt_value"] = $1.gsub("''", "'")
|
287
|
-
when /^"(.*)"$/m
|
288
|
-
field["dflt_value"] = $1.gsub('""', '"')
|
289
|
-
end
|
290
|
-
|
291
|
-
collation = field["collation"]
|
292
|
-
sql_type = field["type"]
|
293
|
-
type_metadata = fetch_type_metadata(sql_type)
|
294
|
-
new_column(field["name"], field["dflt_value"], type_metadata, field["notnull"].to_i == 0, table_name, nil, collation)
|
295
|
-
end
|
262
|
+
collation = field["collation"]
|
263
|
+
sql_type = field["type"]
|
264
|
+
type_metadata = fetch_type_metadata(sql_type)
|
265
|
+
new_column(field["name"], field["dflt_value"], type_metadata, field["notnull"].to_i == 0, table_name, nil, collation)
|
296
266
|
end
|
297
267
|
|
298
268
|
# Returns an array of indexes for the given table.
|
299
269
|
def indexes(table_name, name = nil) #:nodoc:
|
270
|
+
if name
|
271
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
272
|
+
Passing name to #indexes is deprecated without replacement.
|
273
|
+
MSG
|
274
|
+
end
|
275
|
+
|
300
276
|
exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", "SCHEMA").map do |row|
|
301
277
|
sql = <<-SQL
|
302
278
|
SELECT sql
|
@@ -306,17 +282,17 @@ module ArJdbc
|
|
306
282
|
SELECT sql
|
307
283
|
FROM sqlite_temp_master
|
308
284
|
WHERE name=#{quote(row['name'])} AND type='index'
|
309
|
-
|
285
|
+
SQL
|
310
286
|
index_sql = exec_query(sql).first["sql"]
|
311
287
|
match = /\sWHERE\s+(.+)$/i.match(index_sql)
|
312
288
|
where = match[1] if match
|
313
289
|
IndexDefinition.new(
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
290
|
+
table_name,
|
291
|
+
row["name"],
|
292
|
+
row["unique"] != 0,
|
293
|
+
exec_query("PRAGMA index_info('#{row['name']}')", "SCHEMA").map { |col|
|
294
|
+
col["name"]
|
295
|
+
}, nil, nil, where)
|
320
296
|
end
|
321
297
|
end
|
322
298
|
|
@@ -346,7 +322,7 @@ module ArJdbc
|
|
346
322
|
end
|
347
323
|
|
348
324
|
def add_column(table_name, column_name, type, options = {}) #:nodoc:
|
349
|
-
if valid_alter_table_type?(type)
|
325
|
+
if valid_alter_table_type?(type) && !options[:primary_key]
|
350
326
|
super(table_name, column_name, type, options)
|
351
327
|
else
|
352
328
|
alter_table(table_name) do |definition|
|
@@ -380,11 +356,10 @@ module ArJdbc
|
|
380
356
|
|
381
357
|
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
382
358
|
alter_table(table_name) do |definition|
|
383
|
-
include_default = options_include_default?(options)
|
384
359
|
definition[column_name].instance_eval do
|
385
360
|
self.type = type
|
386
361
|
self.limit = options[:limit] if options.include?(:limit)
|
387
|
-
self.default = options[:default] if
|
362
|
+
self.default = options[:default] if options.include?(:default)
|
388
363
|
self.null = options[:null] if options.include?(:null)
|
389
364
|
self.precision = options[:precision] if options.include?(:precision)
|
390
365
|
self.scale = options[:scale] if options.include?(:scale)
|
@@ -399,14 +374,34 @@ module ArJdbc
|
|
399
374
|
rename_column_indexes(table_name, column.name, new_column_name)
|
400
375
|
end
|
401
376
|
|
402
|
-
|
377
|
+
def add_reference(table_name, ref_name, **options) # :nodoc:
|
378
|
+
super(table_name, ref_name, type: :integer, **options)
|
379
|
+
end
|
380
|
+
alias :add_belongs_to :add_reference
|
381
|
+
|
382
|
+
def foreign_keys(table_name)
|
383
|
+
fk_info = exec_query("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA")
|
384
|
+
fk_info.map do |row|
|
385
|
+
options = {
|
386
|
+
column: row["from"],
|
387
|
+
primary_key: row["to"],
|
388
|
+
on_delete: extract_foreign_key_action(row["on_delete"]),
|
389
|
+
on_update: extract_foreign_key_action(row["on_update"])
|
390
|
+
}
|
391
|
+
# DIFFERENCE: FQN
|
392
|
+
::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new(table_name, row["table"], options)
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
private
|
403
397
|
|
404
398
|
def table_structure(table_name)
|
405
399
|
structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
|
406
400
|
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
|
407
401
|
table_structure_with_collation(table_name, structure)
|
408
402
|
end
|
409
|
-
|
403
|
+
alias column_definitions table_structure
|
404
|
+
|
410
405
|
def alter_table(table_name, options = {}) #:nodoc:
|
411
406
|
altered_table_name = "a#{table_name}"
|
412
407
|
caller = lambda { |definition| yield definition if block_given? }
|
@@ -418,28 +413,31 @@ module ArJdbc
|
|
418
413
|
end
|
419
414
|
end
|
420
415
|
|
421
|
-
def move_table(from, to, options = {}, &block)
|
416
|
+
def move_table(from, to, options = {}, &block)
|
422
417
|
copy_table(from, to, options, &block)
|
423
418
|
drop_table(from)
|
424
419
|
end
|
425
420
|
|
426
|
-
def copy_table(from, to, options = {})
|
421
|
+
def copy_table(from, to, options = {})
|
427
422
|
from_primary_key = primary_key(from)
|
428
423
|
options[:id] = false
|
429
424
|
create_table(to, options) do |definition|
|
430
425
|
@definition = definition
|
431
|
-
|
426
|
+
if from_primary_key.is_a?(Array)
|
427
|
+
@definition.primary_keys from_primary_key
|
428
|
+
end
|
432
429
|
columns(from).each do |column|
|
433
430
|
column_name = options[:rename] ?
|
434
431
|
(options[:rename][column.name] ||
|
435
432
|
options[:rename][column.name.to_sym] ||
|
436
433
|
column.name) : column.name
|
437
|
-
next if column_name == from_primary_key
|
438
434
|
|
439
435
|
@definition.column(column_name, column.type,
|
440
436
|
limit: column.limit, default: column.default,
|
441
437
|
precision: column.precision, scale: column.scale,
|
442
|
-
null: column.null, collation: column.collation
|
438
|
+
null: column.null, collation: column.collation,
|
439
|
+
primary_key: column_name == from_primary_key
|
440
|
+
)
|
443
441
|
end
|
444
442
|
yield @definition if block_given?
|
445
443
|
end
|
@@ -449,9 +447,12 @@ module ArJdbc
|
|
449
447
|
options[:rename] || {})
|
450
448
|
end
|
451
449
|
|
452
|
-
def copy_table_indexes(from, to, rename = {})
|
450
|
+
def copy_table_indexes(from, to, rename = {})
|
453
451
|
indexes(from).each do |index|
|
454
452
|
name = index.name
|
453
|
+
# indexes sqlite creates for internal use start with `sqlite_` and
|
454
|
+
# don't need to be copied
|
455
|
+
next if name.starts_with?("sqlite_")
|
455
456
|
if to == "a#{from}"
|
456
457
|
name = "t#{name}"
|
457
458
|
elsif from == "a#{to}"
|
@@ -467,12 +468,13 @@ module ArJdbc
|
|
467
468
|
# index name can't be the same
|
468
469
|
opts = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
|
469
470
|
opts[:unique] = true if index.unique
|
471
|
+
opts[:where] = index.where if index.where
|
470
472
|
add_index(to, columns, opts)
|
471
473
|
end
|
472
474
|
end
|
473
475
|
end
|
474
476
|
|
475
|
-
def copy_table_contents(from, to, columns, rename = {})
|
477
|
+
def copy_table_contents(from, to, columns, rename = {})
|
476
478
|
column_mappings = Hash[columns.map { |name| [name, name] }]
|
477
479
|
rename.each { |a| column_mappings[a.last] = a.first }
|
478
480
|
from_columns = columns(from).collect(&:name)
|
@@ -496,43 +498,51 @@ module ArJdbc
|
|
496
498
|
# Older versions of SQLite return:
|
497
499
|
# column *column_name* is not unique
|
498
500
|
when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
|
499
|
-
|
501
|
+
# DIFFERENCE: FQN
|
502
|
+
::ActiveRecord::RecordNotUnique.new(message)
|
503
|
+
when /.* may not be NULL/, /NOT NULL constraint failed: .*/
|
504
|
+
# DIFFERENCE: FQN
|
505
|
+
::ActiveRecord::NotNullViolation.new(message)
|
506
|
+
when /FOREIGN KEY constraint failed/i
|
507
|
+
# DIFFERENCE: FQN
|
508
|
+
::ActiveRecord::InvalidForeignKey.new(message)
|
500
509
|
else
|
501
510
|
super
|
502
511
|
end
|
503
512
|
end
|
504
513
|
|
505
|
-
private
|
506
514
|
COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
|
507
515
|
|
508
516
|
def table_structure_with_collation(table_name, basic_structure)
|
509
517
|
collation_hash = {}
|
510
|
-
sql
|
511
|
-
|
512
|
-
|
513
|
-
|
518
|
+
sql = <<-SQL
|
519
|
+
SELECT sql FROM
|
520
|
+
(SELECT * FROM sqlite_master UNION ALL
|
521
|
+
SELECT * FROM sqlite_temp_master)
|
522
|
+
WHERE type = 'table' AND name = #{quote(table_name)}
|
523
|
+
SQL
|
514
524
|
|
515
525
|
# Result will have following sample string
|
516
526
|
# CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
517
527
|
# "password_digest" varchar COLLATE "NOCASE");
|
518
|
-
result = exec_query(sql,
|
528
|
+
result = exec_query(sql, "SCHEMA").first
|
519
529
|
|
520
530
|
if result
|
521
531
|
# Splitting with left parentheses and picking up last will return all
|
522
532
|
# columns separated with comma(,).
|
523
|
-
columns_string = result["sql"].split(
|
533
|
+
columns_string = result["sql"].split("(").last
|
524
534
|
|
525
|
-
columns_string.split(
|
535
|
+
columns_string.split(",").each do |column_string|
|
526
536
|
# This regex will match the column name and collation type and will save
|
527
537
|
# the value in $1 and $2 respectively.
|
528
|
-
collation_hash[$1] = $2 if
|
538
|
+
collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
|
529
539
|
end
|
530
540
|
|
531
541
|
basic_structure.map! do |column|
|
532
|
-
column_name = column[
|
542
|
+
column_name = column["name"]
|
533
543
|
|
534
544
|
if collation_hash.has_key? column_name
|
535
|
-
column[
|
545
|
+
column["collation"] = collation_hash[column_name]
|
536
546
|
end
|
537
547
|
|
538
548
|
column
|
@@ -541,6 +551,23 @@ module ArJdbc
|
|
541
551
|
basic_structure.to_hash
|
542
552
|
end
|
543
553
|
end
|
554
|
+
|
555
|
+
def create_table_definition(*args)
|
556
|
+
# DIFFERENCE: FQN
|
557
|
+
::ActiveRecord::ConnectionAdapters::SQLite3::TableDefinition.new(*args)
|
558
|
+
end
|
559
|
+
|
560
|
+
def extract_foreign_key_action(specifier)
|
561
|
+
case specifier
|
562
|
+
when "CASCADE"; :cascade
|
563
|
+
when "SET NULL"; :nullify
|
564
|
+
when "RESTRICT"; :restrict
|
565
|
+
end
|
566
|
+
end
|
567
|
+
|
568
|
+
def configure_connection
|
569
|
+
execute("PRAGMA foreign_keys = ON", "SCHEMA")
|
570
|
+
end
|
544
571
|
end
|
545
572
|
end
|
546
573
|
|
@@ -651,10 +678,14 @@ module ActiveRecord::ConnectionAdapters
|
|
651
678
|
include ArJdbc::Abstract::StatementCache
|
652
679
|
include ArJdbc::Abstract::TransactionSupport
|
653
680
|
|
681
|
+
def supports_transaction_isolation?
|
682
|
+
false
|
683
|
+
end
|
684
|
+
|
654
685
|
def begin_isolated_db_transaction(isolation)
|
655
686
|
raise ActiveRecord::TransactionIsolationError, 'adapter does not support setting transaction isolation'
|
656
687
|
end
|
657
|
-
|
688
|
+
|
658
689
|
# SQLite driver doesn't support all types of insert statements with executeUpdate so
|
659
690
|
# make it act like a regular query and the ids will be returned from #last_inserted_id
|
660
691
|
# example: INSERT INTO "aircraft" DEFAULT VALUES
|