activerecord-jdbc-adapter-ficoh 1.3.21-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 +7 -0
- data/.gitignore +35 -0
- data/.travis.yml +462 -0
- data/.yardopts +4 -0
- data/Appraisals +36 -0
- data/CONTRIBUTING.md +49 -0
- data/Gemfile +68 -0
- data/History.md +1191 -0
- data/LICENSE.txt +25 -0
- data/README.md +277 -0
- data/RUNNING_TESTS.md +88 -0
- data/Rakefile +298 -0
- data/Rakefile.jdbc +20 -0
- data/activerecord-jdbc-adapter.gemspec +63 -0
- data/lib/active_record/connection_adapters/as400_adapter.rb +2 -0
- data/lib/active_record/connection_adapters/db2_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/derby_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/firebird_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/informix_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/jdbc_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mariadb_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mssql_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mysql2_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/active_record/connection_adapters/sqlite3_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -0
- data/lib/activerecord-jdbc-adapter.rb +1 -0
- data/lib/arel/visitors/compat.rb +64 -0
- data/lib/arel/visitors/db2.rb +137 -0
- data/lib/arel/visitors/derby.rb +112 -0
- data/lib/arel/visitors/firebird.rb +79 -0
- data/lib/arel/visitors/h2.rb +25 -0
- data/lib/arel/visitors/hsqldb.rb +32 -0
- data/lib/arel/visitors/postgresql_jdbc.rb +6 -0
- data/lib/arel/visitors/sql_server.rb +225 -0
- data/lib/arel/visitors/sql_server/ng42.rb +293 -0
- data/lib/arjdbc.rb +22 -0
- data/lib/arjdbc/db2.rb +4 -0
- data/lib/arjdbc/db2/adapter.rb +802 -0
- data/lib/arjdbc/db2/as400.rb +137 -0
- data/lib/arjdbc/db2/column.rb +177 -0
- data/lib/arjdbc/db2/connection_methods.rb +45 -0
- data/lib/arjdbc/derby.rb +3 -0
- data/lib/arjdbc/derby/active_record_patch.rb +13 -0
- data/lib/arjdbc/derby/adapter.rb +567 -0
- data/lib/arjdbc/derby/connection_methods.rb +16 -0
- data/lib/arjdbc/derby/schema_creation.rb +15 -0
- data/lib/arjdbc/discover.rb +104 -0
- data/lib/arjdbc/firebird.rb +4 -0
- data/lib/arjdbc/firebird/adapter.rb +468 -0
- data/lib/arjdbc/firebird/connection_methods.rb +20 -0
- data/lib/arjdbc/h2.rb +3 -0
- data/lib/arjdbc/h2/adapter.rb +335 -0
- data/lib/arjdbc/h2/connection_methods.rb +22 -0
- data/lib/arjdbc/hsqldb.rb +3 -0
- data/lib/arjdbc/hsqldb/adapter.rb +304 -0
- data/lib/arjdbc/hsqldb/connection_methods.rb +23 -0
- data/lib/arjdbc/hsqldb/explain_support.rb +35 -0
- data/lib/arjdbc/hsqldb/schema_creation.rb +11 -0
- data/lib/arjdbc/informix.rb +5 -0
- data/lib/arjdbc/informix/adapter.rb +160 -0
- data/lib/arjdbc/informix/connection_methods.rb +9 -0
- data/lib/arjdbc/jdbc.rb +62 -0
- data/lib/arjdbc/jdbc/adapter.rb +997 -0
- data/lib/arjdbc/jdbc/adapter_require.rb +46 -0
- data/lib/arjdbc/jdbc/arel_support.rb +149 -0
- data/lib/arjdbc/jdbc/base_ext.rb +34 -0
- data/lib/arjdbc/jdbc/callbacks.rb +52 -0
- data/lib/arjdbc/jdbc/column.rb +83 -0
- data/lib/arjdbc/jdbc/connection.rb +26 -0
- data/lib/arjdbc/jdbc/connection_methods.rb +59 -0
- data/lib/arjdbc/jdbc/driver.rb +44 -0
- data/lib/arjdbc/jdbc/error.rb +75 -0
- data/lib/arjdbc/jdbc/extension.rb +69 -0
- data/lib/arjdbc/jdbc/java.rb +13 -0
- data/lib/arjdbc/jdbc/type_cast.rb +154 -0
- data/lib/arjdbc/jdbc/type_converter.rb +142 -0
- data/lib/arjdbc/mssql.rb +7 -0
- data/lib/arjdbc/mssql/adapter.rb +822 -0
- data/lib/arjdbc/mssql/column.rb +207 -0
- data/lib/arjdbc/mssql/connection_methods.rb +72 -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.rb +3 -0
- data/lib/arjdbc/mysql/adapter.rb +998 -0
- data/lib/arjdbc/mysql/bulk_change_table.rb +150 -0
- data/lib/arjdbc/mysql/column.rb +167 -0
- data/lib/arjdbc/mysql/connection_methods.rb +137 -0
- data/lib/arjdbc/mysql/explain_support.rb +82 -0
- data/lib/arjdbc/mysql/schema_creation.rb +58 -0
- data/lib/arjdbc/oracle.rb +4 -0
- data/lib/arjdbc/oracle/adapter.rb +968 -0
- data/lib/arjdbc/oracle/column.rb +136 -0
- data/lib/arjdbc/oracle/connection_methods.rb +21 -0
- data/lib/arjdbc/postgresql.rb +3 -0
- data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +21 -0
- data/lib/arjdbc/postgresql/adapter.rb +1498 -0
- data/lib/arjdbc/postgresql/base/array_parser.rb +95 -0
- data/lib/arjdbc/postgresql/base/oid.rb +412 -0
- data/lib/arjdbc/postgresql/base/pgconn.rb +8 -0
- data/lib/arjdbc/postgresql/base/schema_definitions.rb +132 -0
- data/lib/arjdbc/postgresql/column.rb +640 -0
- data/lib/arjdbc/postgresql/connection_methods.rb +44 -0
- data/lib/arjdbc/postgresql/explain_support.rb +53 -0
- data/lib/arjdbc/postgresql/oid/bytea.rb +3 -0
- data/lib/arjdbc/postgresql/oid_types.rb +265 -0
- data/lib/arjdbc/postgresql/schema_creation.rb +60 -0
- data/lib/arjdbc/railtie.rb +11 -0
- data/lib/arjdbc/sqlite3.rb +3 -0
- data/lib/arjdbc/sqlite3/adapter.rb +654 -0
- data/lib/arjdbc/sqlite3/connection_methods.rb +36 -0
- data/lib/arjdbc/sqlite3/explain_support.rb +29 -0
- data/lib/arjdbc/sybase.rb +2 -0
- data/lib/arjdbc/sybase/adapter.rb +47 -0
- data/lib/arjdbc/tasks.rb +13 -0
- data/lib/arjdbc/tasks/database_tasks.rb +66 -0
- data/lib/arjdbc/tasks/databases.rake +91 -0
- data/lib/arjdbc/tasks/databases3.rake +239 -0
- data/lib/arjdbc/tasks/databases4.rake +39 -0
- data/lib/arjdbc/tasks/db2_database_tasks.rb +104 -0
- data/lib/arjdbc/tasks/derby_database_tasks.rb +95 -0
- data/lib/arjdbc/tasks/h2_database_tasks.rb +31 -0
- data/lib/arjdbc/tasks/hsqldb_database_tasks.rb +70 -0
- data/lib/arjdbc/tasks/jdbc_database_tasks.rb +169 -0
- data/lib/arjdbc/tasks/mssql_database_tasks.rb +46 -0
- data/lib/arjdbc/tasks/oracle/enhanced_structure_dump.rb +297 -0
- data/lib/arjdbc/tasks/oracle_database_tasks.rb +65 -0
- data/lib/arjdbc/util/quoted_cache.rb +60 -0
- data/lib/arjdbc/util/serialized_attributes.rb +98 -0
- data/lib/arjdbc/util/table_copier.rb +108 -0
- data/lib/arjdbc/version.rb +8 -0
- data/lib/generators/jdbc/USAGE +9 -0
- data/lib/generators/jdbc/jdbc_generator.rb +17 -0
- data/pom.xml +285 -0
- data/rails_generators/jdbc_generator.rb +15 -0
- data/rails_generators/templates/config/initializers/jdbc.rb +10 -0
- data/rails_generators/templates/lib/tasks/jdbc.rake +11 -0
- data/rakelib/01-tomcat.rake +51 -0
- data/rakelib/02-test.rake +151 -0
- data/rakelib/bundler_ext.rb +11 -0
- data/rakelib/db.rake +58 -0
- data/rakelib/rails.rake +77 -0
- data/src/java/arjdbc/ArJdbcModule.java +288 -0
- data/src/java/arjdbc/db2/DB2Module.java +77 -0
- data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +128 -0
- data/src/java/arjdbc/derby/DerbyModule.java +180 -0
- data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +153 -0
- data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +190 -0
- data/src/java/arjdbc/h2/H2Module.java +50 -0
- data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +86 -0
- data/src/java/arjdbc/hsqldb/HSQLDBModule.java +74 -0
- data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +76 -0
- data/src/java/arjdbc/jdbc/AdapterJavaService.java +43 -0
- data/src/java/arjdbc/jdbc/Callable.java +44 -0
- data/src/java/arjdbc/jdbc/ConnectionFactory.java +77 -0
- data/src/java/arjdbc/jdbc/DataSourceConnectionFactory.java +156 -0
- data/src/java/arjdbc/jdbc/DriverConnectionFactory.java +63 -0
- data/src/java/arjdbc/jdbc/DriverWrapper.java +128 -0
- data/src/java/arjdbc/jdbc/JdbcConnectionFactory.java +32 -0
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +4541 -0
- data/src/java/arjdbc/jdbc/SQLBlock.java +54 -0
- data/src/java/arjdbc/jdbc/WithResultSet.java +37 -0
- data/src/java/arjdbc/mssql/MSSQLModule.java +91 -0
- data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +193 -0
- data/src/java/arjdbc/mysql/MySQLModule.java +140 -0
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +456 -0
- data/src/java/arjdbc/oracle/OracleModule.java +81 -0
- data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +477 -0
- data/src/java/arjdbc/postgresql/ByteaUtils.java +171 -0
- data/src/java/arjdbc/postgresql/DriverImplementation.java +78 -0
- data/src/java/arjdbc/postgresql/PGDriverImplementation.java +535 -0
- data/src/java/arjdbc/postgresql/PostgreSQLModule.java +189 -0
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +489 -0
- data/src/java/arjdbc/sqlite3/SQLite3Module.java +93 -0
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +405 -0
- data/src/java/arjdbc/util/CallResultSet.java +826 -0
- data/src/java/arjdbc/util/DateTimeUtils.java +517 -0
- data/src/java/arjdbc/util/NumberUtils.java +50 -0
- data/src/java/arjdbc/util/ObjectSupport.java +65 -0
- data/src/java/arjdbc/util/QuotingUtils.java +139 -0
- data/src/java/arjdbc/util/StringCache.java +60 -0
- data/src/java/arjdbc/util/StringHelper.java +155 -0
- metadata +288 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
ArJdbc::ConnectionMethods.module_eval do
|
2
|
+
def postgresql_connection(config)
|
3
|
+
config[:adapter_spec] ||= ::ArJdbc::PostgreSQL
|
4
|
+
config[:adapter_class] = ActiveRecord::ConnectionAdapters::PostgreSQLAdapter unless config.key?(:adapter_class)
|
5
|
+
|
6
|
+
return jndi_connection(config) if jndi_config?(config)
|
7
|
+
|
8
|
+
driver = config[:driver] ||= 'org.postgresql.Driver'
|
9
|
+
ArJdbc.load_driver(:Postgres) if driver.start_with?('org.postgresql.') && config[:load_driver] != false
|
10
|
+
|
11
|
+
host = config[:host] ||= ( config[:hostaddr] || ENV['PGHOST'] || 'localhost' )
|
12
|
+
port = config[:port] ||= ( ENV['PGPORT'] || 5432 )
|
13
|
+
database = config[:database] || config[:dbname] || ENV['PGDATABASE']
|
14
|
+
|
15
|
+
config[:url] ||= "jdbc:postgresql://#{host}:#{port}/#{database}"
|
16
|
+
config[:url] << config[:pg_params] if config[:pg_params]
|
17
|
+
|
18
|
+
config[:username] ||= ( config[:user] || ENV['PGUSER'] || ENV_JAVA['user.name'] )
|
19
|
+
config[:password] ||= ENV['PGPASSWORD'] unless config.key?(:password)
|
20
|
+
properties = ( config[:properties] ||= {} )
|
21
|
+
# PG :connect_timeout - maximum time to wait for connection to succeed
|
22
|
+
if connect_timeout = ( config[:connect_timeout] || ENV['PGCONNECT_TIMEOUT'] )
|
23
|
+
properties['socketTimeout'] ||= connect_timeout
|
24
|
+
end
|
25
|
+
if login_timeout = config[:login_timeout]
|
26
|
+
properties['loginTimeout'] ||= login_timeout
|
27
|
+
end
|
28
|
+
sslmode = config.key?(:sslmode) ? config[:sslmode] : config[:requiressl]
|
29
|
+
# NOTE: makes not much sense since this needs some JVM options :
|
30
|
+
# sslmode = ENV['PGSSLMODE'] || ENV['PGREQUIRESSL'] if sslmode.nil?
|
31
|
+
unless sslmode.nil? # PG :sslmode - disable|allow|prefer|require
|
32
|
+
# JRuby/JVM needs to be started with :
|
33
|
+
# -Djavax.net.ssl.trustStore=mystore -Djavax.net.ssl.trustStorePassword=...
|
34
|
+
# or a non-validating connection might be used (for testing) :
|
35
|
+
# :sslfactory = 'org.postgresql.ssl.NonValidatingFactory'
|
36
|
+
properties['ssl'] ||= 'true' if sslmode == true || sslmode.to_s == 'require'
|
37
|
+
end
|
38
|
+
properties['tcpKeepAlive'] ||= config[:keepalives] if config.key?(:keepalives)
|
39
|
+
properties['kerberosServerName'] ||= config[:krbsrvname] if config[:krbsrvname]
|
40
|
+
|
41
|
+
jdbc_connection(config)
|
42
|
+
end
|
43
|
+
alias_method :jdbcpostgresql_connection, :postgresql_connection
|
44
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module ArJdbc
|
2
|
+
module PostgreSQL
|
3
|
+
# @private
|
4
|
+
module ExplainSupport
|
5
|
+
def supports_explain?
|
6
|
+
true
|
7
|
+
end
|
8
|
+
|
9
|
+
def explain(arel, binds = [])
|
10
|
+
sql = "EXPLAIN #{to_sql(arel, binds)}"
|
11
|
+
result = exec_query(sql, "EXPLAIN", binds)
|
12
|
+
ExplainPrettyPrinter.new.pp result # we can assume AR >= 3.1
|
13
|
+
end
|
14
|
+
# @private
|
15
|
+
class ExplainPrettyPrinter
|
16
|
+
# Pretty prints the result of a EXPLAIN in a way that resembles the output of the
|
17
|
+
# PostgreSQL shell:
|
18
|
+
#
|
19
|
+
# QUERY PLAN
|
20
|
+
# ------------------------------------------------------------------------------
|
21
|
+
# Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0)
|
22
|
+
# Join Filter: (posts.user_id = users.id)
|
23
|
+
# -> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4)
|
24
|
+
# Index Cond: (id = 1)
|
25
|
+
# -> Seq Scan on posts (cost=0.00..28.88 rows=8 width=4)
|
26
|
+
# Filter: (posts.user_id = 1)
|
27
|
+
# (6 rows)
|
28
|
+
#
|
29
|
+
def pp(result)
|
30
|
+
header = result.columns.first
|
31
|
+
lines = result.rows.map(&:first)
|
32
|
+
|
33
|
+
# We add 2 because there's one char of padding at both sides, note
|
34
|
+
# the extra hyphens in the example above.
|
35
|
+
width = [header, *lines].map(&:length).max + 2
|
36
|
+
|
37
|
+
pp = []
|
38
|
+
|
39
|
+
pp << header.center(width).rstrip
|
40
|
+
pp << '-' * width
|
41
|
+
|
42
|
+
pp += lines.map {|line| " #{line}"}
|
43
|
+
|
44
|
+
nrows = result.rows.length
|
45
|
+
rows_label = nrows == 1 ? 'row' : 'rows'
|
46
|
+
pp << "(#{nrows} #{rows_label})"
|
47
|
+
|
48
|
+
pp.join("\n") + "\n"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,265 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module ArJdbc
|
4
|
+
module PostgreSQL
|
5
|
+
|
6
|
+
if AR42
|
7
|
+
require 'active_record/connection_adapters/postgresql/oid'
|
8
|
+
require 'arjdbc/postgresql/base/pgconn'
|
9
|
+
require 'arjdbc/postgresql/oid/bytea.rb'
|
10
|
+
else
|
11
|
+
require 'arjdbc/postgresql/base/oid'
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'arjdbc/postgresql/base/pgconn'
|
15
|
+
|
16
|
+
# @private
|
17
|
+
OID = ::ActiveRecord::ConnectionAdapters::PostgreSQL::OID
|
18
|
+
|
19
|
+
# @private
|
20
|
+
module OIDTypes
|
21
|
+
|
22
|
+
# @override
|
23
|
+
def enable_extension(name)
|
24
|
+
result = super(name)
|
25
|
+
@extensions = nil
|
26
|
+
reload_type_map
|
27
|
+
result
|
28
|
+
end
|
29
|
+
|
30
|
+
# @override
|
31
|
+
def disable_extension(name)
|
32
|
+
result = super(name)
|
33
|
+
@extensions = nil
|
34
|
+
reload_type_map
|
35
|
+
result
|
36
|
+
end
|
37
|
+
|
38
|
+
# @override
|
39
|
+
def extensions
|
40
|
+
@extensions ||= super
|
41
|
+
end
|
42
|
+
|
43
|
+
# @override
|
44
|
+
def lookup_cast_type(sql_type)
|
45
|
+
oid = execute("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA")
|
46
|
+
super oid.first['oid'].to_i
|
47
|
+
end if AR42
|
48
|
+
|
49
|
+
def get_oid_type(oid, fmod, column_name)
|
50
|
+
type_map.fetch(oid, fmod) {
|
51
|
+
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
|
52
|
+
type_map[oid] = OID::Identity.new
|
53
|
+
}
|
54
|
+
end unless AR42
|
55
|
+
|
56
|
+
def get_oid_type(oid, fmod, column_name, sql_type = '')
|
57
|
+
if !type_map.key?(oid)
|
58
|
+
load_additional_types(type_map, [oid])
|
59
|
+
end
|
60
|
+
|
61
|
+
type_map.fetch(oid, fmod, sql_type) {
|
62
|
+
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
|
63
|
+
Type::Value.new.tap do |cast_type|
|
64
|
+
type_map.register_type(oid, cast_type)
|
65
|
+
end
|
66
|
+
}
|
67
|
+
end if AR42
|
68
|
+
|
69
|
+
@@type_map_cache = {}
|
70
|
+
@@type_map_cache_lock = Mutex.new
|
71
|
+
|
72
|
+
if AR42
|
73
|
+
TypeMap = ActiveRecord::Type::HashLookupTypeMap
|
74
|
+
else
|
75
|
+
TypeMap = OID::TypeMap
|
76
|
+
end
|
77
|
+
|
78
|
+
# @see #type_map
|
79
|
+
# @private
|
80
|
+
TypeMap.class_eval do
|
81
|
+
def dup
|
82
|
+
dup = super # make sure @mapping is not shared
|
83
|
+
dup.instance_variable_set(:@mapping, @mapping.dup)
|
84
|
+
dup
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def type_map
|
89
|
+
# NOTE: our type_map is lazy (on AR < 4.2)
|
90
|
+
# ... since it's only used for `adapter.accessor`
|
91
|
+
@type_map ||= begin
|
92
|
+
if type_map = @@type_map_cache[ type_cache_key ]
|
93
|
+
type_map.dup
|
94
|
+
else
|
95
|
+
type_map = TypeMap.new
|
96
|
+
initialize_type_map(type_map)
|
97
|
+
cache_type_map(type_map)
|
98
|
+
type_map
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def reload_type_map
|
104
|
+
if ( @type_map ||= nil )
|
105
|
+
@type_map.clear
|
106
|
+
initialize_type_map(@type_map)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def cache_type_map(type_map)
|
113
|
+
@@type_map_cache_lock.synchronize do
|
114
|
+
@@type_map_cache[ type_cache_key ] = type_map
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def type_cache_key
|
119
|
+
config.hash + ( 7 * extensions.hash )
|
120
|
+
end
|
121
|
+
|
122
|
+
def add_oid(row, records_by_oid, type_map)
|
123
|
+
return type_map if type_map.key? row['type_elem'].to_i
|
124
|
+
|
125
|
+
if OID.registered_type? typname = row['typname']
|
126
|
+
# this composite type is explicitly registered
|
127
|
+
vector = OID::NAMES[ typname ]
|
128
|
+
else
|
129
|
+
# use the default for composite types
|
130
|
+
unless type_map.key? typelem = row['typelem'].to_i
|
131
|
+
add_oid records_by_oid[ row['typelem'] ], records_by_oid, type_map
|
132
|
+
end
|
133
|
+
|
134
|
+
vector = OID::Vector.new row['typdelim'], type_map[typelem]
|
135
|
+
end
|
136
|
+
|
137
|
+
type_map[ row['oid'].to_i ] = vector
|
138
|
+
type_map
|
139
|
+
end
|
140
|
+
|
141
|
+
def initialize_type_map(type_map)
|
142
|
+
result = execute('SELECT oid, typname, typelem, typdelim, typinput FROM pg_type', 'SCHEMA')
|
143
|
+
leaves, nodes = result.partition { |row| row['typelem'].to_s == '0' }
|
144
|
+
# populate the leaf nodes
|
145
|
+
leaves.find_all { |row| OID.registered_type? row['typname'] }.each do |row|
|
146
|
+
type_map[ row['oid'].to_i ] = OID::NAMES[ row['typname'] ]
|
147
|
+
end
|
148
|
+
|
149
|
+
records_by_oid = result.group_by { |row| row['oid'] }
|
150
|
+
|
151
|
+
arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' }
|
152
|
+
|
153
|
+
# populate composite types
|
154
|
+
nodes.each { |row| add_oid row, records_by_oid, type_map }
|
155
|
+
|
156
|
+
# populate array types
|
157
|
+
arrays.find_all { |row| type_map.key? row['typelem'].to_i }.each do |row|
|
158
|
+
array = OID::Array.new type_map[ row['typelem'].to_i ]
|
159
|
+
type_map[ row['oid'].to_i ] = array
|
160
|
+
end
|
161
|
+
end unless AR42
|
162
|
+
|
163
|
+
def initialize_type_map(m)
|
164
|
+
register_class_with_limit m, 'int2', OID::Integer
|
165
|
+
register_class_with_limit m, 'int4', OID::Integer
|
166
|
+
register_class_with_limit m, 'int8', OID::Integer
|
167
|
+
m.alias_type 'oid', 'int2'
|
168
|
+
m.register_type 'float4', OID::Float.new
|
169
|
+
m.alias_type 'float8', 'float4'
|
170
|
+
m.register_type 'text', Type::Text.new
|
171
|
+
register_class_with_limit m, 'varchar', Type::String
|
172
|
+
m.alias_type 'char', 'varchar'
|
173
|
+
m.alias_type 'name', 'varchar'
|
174
|
+
m.alias_type 'bpchar', 'varchar'
|
175
|
+
m.register_type 'bool', Type::Boolean.new
|
176
|
+
register_class_with_limit m, 'bit', OID::Bit
|
177
|
+
register_class_with_limit m, 'varbit', OID::BitVarying
|
178
|
+
m.alias_type 'timestamptz', 'timestamp'
|
179
|
+
m.register_type 'date', OID::Date.new
|
180
|
+
m.register_type 'time', OID::Time.new
|
181
|
+
|
182
|
+
m.register_type 'money', OID::Money.new
|
183
|
+
m.register_type 'bytea', OID::Bytea.new
|
184
|
+
m.register_type 'point', OID::Point.new
|
185
|
+
m.register_type 'hstore', OID::Hstore.new
|
186
|
+
m.register_type 'json', OID::Json.new
|
187
|
+
m.register_type 'jsonb', OID::Jsonb.new
|
188
|
+
m.register_type 'cidr', OID::Cidr.new
|
189
|
+
m.register_type 'inet', OID::Inet.new
|
190
|
+
m.register_type 'uuid', OID::Uuid.new
|
191
|
+
m.register_type 'xml', OID::Xml.new
|
192
|
+
m.register_type 'tsvector', OID::SpecializedString.new(:tsvector)
|
193
|
+
m.register_type 'macaddr', OID::SpecializedString.new(:macaddr)
|
194
|
+
m.register_type 'citext', OID::SpecializedString.new(:citext)
|
195
|
+
m.register_type 'ltree', OID::SpecializedString.new(:ltree)
|
196
|
+
|
197
|
+
# FIXME: why are we keeping these types as strings?
|
198
|
+
m.alias_type 'interval', 'varchar'
|
199
|
+
m.alias_type 'path', 'varchar'
|
200
|
+
m.alias_type 'line', 'varchar'
|
201
|
+
m.alias_type 'polygon', 'varchar'
|
202
|
+
m.alias_type 'circle', 'varchar'
|
203
|
+
m.alias_type 'lseg', 'varchar'
|
204
|
+
m.alias_type 'box', 'varchar'
|
205
|
+
|
206
|
+
m.register_type 'timestamp' do |_, _, sql_type|
|
207
|
+
precision = extract_precision(sql_type)
|
208
|
+
OID::DateTime.new(precision: precision)
|
209
|
+
end
|
210
|
+
|
211
|
+
m.register_type 'numeric' do |_, fmod, sql_type|
|
212
|
+
precision = extract_precision(sql_type)
|
213
|
+
scale = extract_scale(sql_type)
|
214
|
+
|
215
|
+
# The type for the numeric depends on the width of the field,
|
216
|
+
# so we'll do something special here.
|
217
|
+
#
|
218
|
+
# When dealing with decimal columns:
|
219
|
+
#
|
220
|
+
# places after decimal = fmod - 4 & 0xffff
|
221
|
+
# places before decimal = (fmod - 4) >> 16 & 0xffff
|
222
|
+
if fmod && (fmod - 4 & 0xffff).zero?
|
223
|
+
# FIXME: Remove this class, and the second argument to
|
224
|
+
# lookups on PG
|
225
|
+
Type::DecimalWithoutScale.new(precision: precision)
|
226
|
+
else
|
227
|
+
OID::Decimal.new(precision: precision, scale: scale)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
load_additional_types(m)
|
232
|
+
end if AR42
|
233
|
+
|
234
|
+
def load_additional_types(type_map, oids = nil)
|
235
|
+
if supports_ranges?
|
236
|
+
query = <<-SQL
|
237
|
+
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
|
238
|
+
FROM pg_type as t
|
239
|
+
LEFT JOIN pg_range as r ON oid = rngtypid
|
240
|
+
SQL
|
241
|
+
else
|
242
|
+
query = <<-SQL
|
243
|
+
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
|
244
|
+
FROM pg_type as t
|
245
|
+
SQL
|
246
|
+
end
|
247
|
+
|
248
|
+
initializer = OID::TypeMapInitializer.new(type_map)
|
249
|
+
|
250
|
+
if oids
|
251
|
+
query << ( "WHERE t.oid::integer IN (%s)" % oids.join(", ") )
|
252
|
+
else
|
253
|
+
# query_conditions_for_initial_load only available since AR > 4.2.1
|
254
|
+
if initializer.respond_to?(:query_conditions_for_initial_load)
|
255
|
+
query << initializer.query_conditions_for_initial_load(type_map)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
records = execute(query, 'SCHEMA')
|
260
|
+
initializer.run(records)
|
261
|
+
end if AR42
|
262
|
+
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module ArJdbc
|
2
|
+
module PostgreSQL
|
3
|
+
# @private copied (and adjusted) from native adapter 4.0/4.1/4.2
|
4
|
+
class SchemaCreation < ::ActiveRecord::ConnectionAdapters::AbstractAdapter::SchemaCreation
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def visit_AddColumn(o)
|
9
|
+
sql_type = type_to_sql(o.type.to_sym, o.limit, o.precision, o.scale)
|
10
|
+
sql = "ADD COLUMN #{quote_column_name(o.name)} #{sql_type}"
|
11
|
+
add_column_options!(sql, column_options(o))
|
12
|
+
end unless AR42
|
13
|
+
|
14
|
+
def visit_ColumnDefinition(o)
|
15
|
+
sql = super
|
16
|
+
if o.primary_key? && o.type == :uuid
|
17
|
+
sql << " PRIMARY KEY "
|
18
|
+
add_column_options!(sql, column_options(o))
|
19
|
+
end
|
20
|
+
sql
|
21
|
+
end unless AR42
|
22
|
+
|
23
|
+
def visit_ColumnDefinition(o)
|
24
|
+
sql = super
|
25
|
+
if o.primary_key? && o.type != :primary_key
|
26
|
+
sql << " PRIMARY KEY "
|
27
|
+
add_column_options!(sql, column_options(o))
|
28
|
+
end
|
29
|
+
sql
|
30
|
+
end if AR42
|
31
|
+
|
32
|
+
def add_column_options!(sql, options)
|
33
|
+
if options[:array] || options[:column].try(:array)
|
34
|
+
sql << '[]'
|
35
|
+
end
|
36
|
+
|
37
|
+
column = options.fetch(:column) { return super }
|
38
|
+
if column.type == :uuid && options[:default] =~ /\(\)/
|
39
|
+
sql << " DEFAULT #{options[:default]}"
|
40
|
+
else
|
41
|
+
super
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def type_for_column(column)
|
46
|
+
if column.array
|
47
|
+
@conn.lookup_cast_type("#{column.sql_type}[]")
|
48
|
+
else
|
49
|
+
super
|
50
|
+
end
|
51
|
+
end if AR42
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
def schema_creation
|
56
|
+
SchemaCreation.new self
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end if ::ActiveRecord::ConnectionAdapters::AbstractAdapter.const_defined? :SchemaCreation
|
@@ -0,0 +1,654 @@
|
|
1
|
+
ArJdbc.load_java_part :SQLite3
|
2
|
+
|
3
|
+
require 'arjdbc/sqlite3/explain_support'
|
4
|
+
require 'arjdbc/util/table_copier'
|
5
|
+
|
6
|
+
module ArJdbc
|
7
|
+
module SQLite3
|
8
|
+
include Util::TableCopier
|
9
|
+
|
10
|
+
JdbcConnection = ::ActiveRecord::ConnectionAdapters::SQLite3JdbcConnection
|
11
|
+
|
12
|
+
# @deprecated
|
13
|
+
# @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_connection_class
|
14
|
+
def self.jdbc_connection_class; JdbcConnection end
|
15
|
+
|
16
|
+
# @see ActiveRecord::ConnectionAdapters::JdbcColumn#column_types
|
17
|
+
def self.column_selector
|
18
|
+
[ /sqlite/i, lambda { |config, column| column.extend(ColumnMethods) } ]
|
19
|
+
end
|
20
|
+
|
21
|
+
# @private
|
22
|
+
# @see ActiveRecord::ConnectionAdapters::JdbcColumn
|
23
|
+
module ColumnMethods
|
24
|
+
|
25
|
+
# @override {ActiveRecord::ConnectionAdapters::JdbcColumn#init_column}
|
26
|
+
def init_column(name, default, *args)
|
27
|
+
if default =~ /NULL/
|
28
|
+
@default = nil
|
29
|
+
else
|
30
|
+
super
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# @override {ActiveRecord::ConnectionAdapters::JdbcColumn#default_value}
|
35
|
+
def default_value(value)
|
36
|
+
# JDBC returns column default strings with actual single quotes :
|
37
|
+
return $1 if value =~ /^'(.*)'$/
|
38
|
+
|
39
|
+
value
|
40
|
+
end
|
41
|
+
|
42
|
+
# @override {ActiveRecord::ConnectionAdapters::Column#type_cast}
|
43
|
+
def type_cast(value)
|
44
|
+
return nil if value.nil?
|
45
|
+
case type
|
46
|
+
when :string then value
|
47
|
+
when :primary_key
|
48
|
+
value.respond_to?(:to_i) ? value.to_i : ( value ? 1 : 0 )
|
49
|
+
when :float then value.to_f
|
50
|
+
when :decimal then self.class.value_to_decimal(value)
|
51
|
+
when :boolean then self.class.value_to_boolean(value)
|
52
|
+
else super
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# @override {ActiveRecord::ConnectionAdapters::Column#simplified_type}
|
59
|
+
def simplified_type(field_type)
|
60
|
+
case field_type
|
61
|
+
when /boolean/i then :boolean
|
62
|
+
when /text/i then :text
|
63
|
+
when /varchar/i then :string
|
64
|
+
when /int/i then :integer
|
65
|
+
when /float/i then :float
|
66
|
+
when /real|decimal/i then
|
67
|
+
extract_scale(field_type) == 0 ? :integer : :decimal
|
68
|
+
when /datetime/i then :datetime
|
69
|
+
when /date/i then :date
|
70
|
+
when /time/i then :time
|
71
|
+
when /blob/i then :binary
|
72
|
+
else super
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# @override {ActiveRecord::ConnectionAdapters::Column#extract_limit}
|
77
|
+
def extract_limit(sql_type)
|
78
|
+
return nil if sql_type =~ /^(real)\(\d+/i
|
79
|
+
super
|
80
|
+
end
|
81
|
+
|
82
|
+
def extract_precision(sql_type)
|
83
|
+
case sql_type
|
84
|
+
when /^(real)\((\d+)(,\d+)?\)/i then $2.to_i
|
85
|
+
else super
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def extract_scale(sql_type)
|
90
|
+
case sql_type
|
91
|
+
when /^(real)\((\d+)\)/i then 0
|
92
|
+
when /^(real)\((\d+)(,(\d+))\)/i then $4.to_i
|
93
|
+
else super
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
# @see ActiveRecord::ConnectionAdapters::Jdbc::ArelSupport
|
100
|
+
def self.arel_visitor_type(config = nil)
|
101
|
+
::Arel::Visitors::SQLite
|
102
|
+
end
|
103
|
+
|
104
|
+
# @see ActiveRecord::ConnectionAdapters::JdbcAdapter#bind_substitution
|
105
|
+
# @private
|
106
|
+
class BindSubstitution < ::Arel::Visitors::SQLite
|
107
|
+
include ::Arel::Visitors::BindVisitor
|
108
|
+
end if defined? ::Arel::Visitors::BindVisitor
|
109
|
+
|
110
|
+
ADAPTER_NAME = 'SQLite'.freeze
|
111
|
+
|
112
|
+
def adapter_name
|
113
|
+
ADAPTER_NAME
|
114
|
+
end
|
115
|
+
|
116
|
+
NATIVE_DATABASE_TYPES = {
|
117
|
+
:primary_key => nil,
|
118
|
+
:string => { :name => "varchar", :limit => 255 },
|
119
|
+
:text => { :name => "text" },
|
120
|
+
:integer => { :name => "integer" },
|
121
|
+
:float => { :name => "float" },
|
122
|
+
# :real => { :name=>"real" },
|
123
|
+
:decimal => { :name => "decimal" },
|
124
|
+
:datetime => { :name => "datetime" },
|
125
|
+
:timestamp => { :name => "datetime" },
|
126
|
+
:time => { :name => "time" },
|
127
|
+
:date => { :name => "date" },
|
128
|
+
:binary => { :name => "blob" },
|
129
|
+
:boolean => { :name => "boolean" }
|
130
|
+
}
|
131
|
+
NATIVE_DATABASE_TYPES.update(
|
132
|
+
:string => { :name => "varchar" }
|
133
|
+
) if AR42
|
134
|
+
|
135
|
+
# @override
|
136
|
+
def native_database_types
|
137
|
+
types = NATIVE_DATABASE_TYPES.dup
|
138
|
+
types[:primary_key] = default_primary_key_type
|
139
|
+
types
|
140
|
+
end
|
141
|
+
|
142
|
+
def default_primary_key_type
|
143
|
+
if supports_autoincrement?
|
144
|
+
'integer PRIMARY KEY AUTOINCREMENT NOT NULL'
|
145
|
+
else
|
146
|
+
'integer PRIMARY KEY NOT NULL'
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# @override
|
151
|
+
def supports_ddl_transactions?
|
152
|
+
true
|
153
|
+
end
|
154
|
+
|
155
|
+
# @override
|
156
|
+
def supports_savepoints?
|
157
|
+
sqlite_version >= '3.6.8'
|
158
|
+
end
|
159
|
+
|
160
|
+
# @override
|
161
|
+
def supports_partial_index?
|
162
|
+
sqlite_version >= '3.8.0'
|
163
|
+
end
|
164
|
+
|
165
|
+
# @override
|
166
|
+
def supports_add_column?
|
167
|
+
true
|
168
|
+
end
|
169
|
+
|
170
|
+
# @override
|
171
|
+
def supports_count_distinct?
|
172
|
+
true
|
173
|
+
end
|
174
|
+
|
175
|
+
# @override
|
176
|
+
def supports_autoincrement?
|
177
|
+
true
|
178
|
+
end
|
179
|
+
|
180
|
+
# @override
|
181
|
+
def supports_migrations?
|
182
|
+
true
|
183
|
+
end
|
184
|
+
|
185
|
+
# @override
|
186
|
+
def supports_primary_key?
|
187
|
+
true
|
188
|
+
end
|
189
|
+
|
190
|
+
# @override
|
191
|
+
def supports_add_column?
|
192
|
+
true
|
193
|
+
end
|
194
|
+
|
195
|
+
# @override
|
196
|
+
def supports_count_distinct?
|
197
|
+
true
|
198
|
+
end
|
199
|
+
|
200
|
+
# @override
|
201
|
+
def supports_autoincrement?
|
202
|
+
true
|
203
|
+
end
|
204
|
+
|
205
|
+
# @override
|
206
|
+
def supports_index_sort_order?
|
207
|
+
true
|
208
|
+
end
|
209
|
+
|
210
|
+
# @override
|
211
|
+
def supports_views?
|
212
|
+
true
|
213
|
+
end
|
214
|
+
|
215
|
+
def sqlite_version
|
216
|
+
@sqlite_version ||= Version.new(select_value('SELECT sqlite_version(*)'))
|
217
|
+
end
|
218
|
+
private :sqlite_version
|
219
|
+
|
220
|
+
# @override
|
221
|
+
def quote(value, column = nil)
|
222
|
+
return value if sql_literal?(value)
|
223
|
+
|
224
|
+
if value.kind_of?(String)
|
225
|
+
column_type = column && column.type
|
226
|
+
if column_type == :binary
|
227
|
+
"x'#{value.unpack("H*")[0]}'"
|
228
|
+
else
|
229
|
+
super
|
230
|
+
end
|
231
|
+
else
|
232
|
+
super
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def quote_table_name_for_assignment(table, attr)
|
237
|
+
quote_column_name(attr)
|
238
|
+
end if ::ActiveRecord::VERSION::MAJOR >= 4
|
239
|
+
|
240
|
+
def type_cast(value, column)
|
241
|
+
return value.to_f if BigDecimal === value
|
242
|
+
return super unless String === value
|
243
|
+
return super unless column && value
|
244
|
+
|
245
|
+
value = super
|
246
|
+
if column.type == :string &&
|
247
|
+
value.respond_to?(:encoding) && value.encoding == Encoding::ASCII_8BIT
|
248
|
+
logger.error "Binary data inserted for `string` type on column `#{column.name}`" if logger
|
249
|
+
value = value.encode Encoding::UTF_8
|
250
|
+
end
|
251
|
+
value
|
252
|
+
end
|
253
|
+
|
254
|
+
# Quote date/time values for use in SQL input.
|
255
|
+
# Includes microseconds if the value is a Time responding to usec.
|
256
|
+
# @override
|
257
|
+
def quoted_date(value)
|
258
|
+
if value.acts_like?(:time) && value.respond_to?(:usec)
|
259
|
+
"#{super}.#{sprintf("%06d", value.usec)}"
|
260
|
+
else
|
261
|
+
super
|
262
|
+
end
|
263
|
+
end if ::ActiveRecord::VERSION::MAJOR >= 3
|
264
|
+
|
265
|
+
# @override
|
266
|
+
def tables(name = nil, table_name = nil)
|
267
|
+
sql = "SELECT name FROM sqlite_master WHERE type = 'table'"
|
268
|
+
if table_name
|
269
|
+
sql << " AND name = #{quote_table_name(table_name)}"
|
270
|
+
else
|
271
|
+
sql << " AND NOT name = 'sqlite_sequence'"
|
272
|
+
end
|
273
|
+
|
274
|
+
select_rows(sql, name).map! { |row| row[0] }
|
275
|
+
end
|
276
|
+
|
277
|
+
# @override
|
278
|
+
def table_exists?(table_name)
|
279
|
+
table_name && tables(nil, table_name).any?
|
280
|
+
end
|
281
|
+
|
282
|
+
def truncate_fake(table_name, name = nil)
|
283
|
+
execute "DELETE FROM #{quote_table_name(table_name)}; VACUUM", name
|
284
|
+
end
|
285
|
+
# NOTE: not part of official AR (4.2) alias truncate truncate_fake
|
286
|
+
|
287
|
+
# Returns 62. SQLite supports index names up to 64 characters.
|
288
|
+
# The rest is used by Rails internally to perform temporary rename operations.
|
289
|
+
# @return [Fixnum]
|
290
|
+
def allowed_index_name_length
|
291
|
+
index_name_length - 2
|
292
|
+
end
|
293
|
+
|
294
|
+
# NOTE: handled by JdbcAdapter only to have statements in logs :
|
295
|
+
|
296
|
+
# @override
|
297
|
+
def begin_db_transaction
|
298
|
+
# NOTE: based on TX mode SQLite-JDBC does "begin;"
|
299
|
+
# "begin immediate;" or "begin exclusive;" on setAutoCommit(false)
|
300
|
+
log('BEGIN') { @connection.begin }
|
301
|
+
end
|
302
|
+
|
303
|
+
# @override
|
304
|
+
def commit_db_transaction
|
305
|
+
log('COMMIT') { @connection.commit }
|
306
|
+
end
|
307
|
+
|
308
|
+
# @override
|
309
|
+
def rollback_db_transaction
|
310
|
+
log('ROLLBACK') { @connection.rollback }
|
311
|
+
end
|
312
|
+
|
313
|
+
# @override on **AR-4.0**
|
314
|
+
def begin_isolated_db_transaction(isolation)
|
315
|
+
log("/* TRANSACTION_#{isolation.to_s.upcase} */; BEGIN") do
|
316
|
+
@connection.begin(isolation)
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
# @override
|
321
|
+
def create_savepoint(name = current_savepoint_name(true))
|
322
|
+
log("SAVEPOINT #{name}") { @connection.create_savepoint(name) }
|
323
|
+
end
|
324
|
+
|
325
|
+
# @override
|
326
|
+
def rollback_to_savepoint(name = current_savepoint_name(true))
|
327
|
+
log("ROLLBACK TO SAVEPOINT #{name}") { @connection.rollback_savepoint(name) }
|
328
|
+
end
|
329
|
+
|
330
|
+
# @override
|
331
|
+
def release_savepoint(name = current_savepoint_name(false))
|
332
|
+
log("RELEASE SAVEPOINT #{name}") { @connection.release_savepoint(name) }
|
333
|
+
end
|
334
|
+
|
335
|
+
# @private
|
336
|
+
def recreate_database(name = nil, options = {})
|
337
|
+
drop_database(name)
|
338
|
+
create_database(name, options)
|
339
|
+
end
|
340
|
+
|
341
|
+
# @private
|
342
|
+
def create_database(name = nil, options = {})
|
343
|
+
end
|
344
|
+
|
345
|
+
# @private
|
346
|
+
def drop_database(name = nil)
|
347
|
+
tables.each { |table| drop_table(table) }
|
348
|
+
end
|
349
|
+
|
350
|
+
def select(sql, name = nil, binds = [])
|
351
|
+
result = super # AR::Result (4.0) or Array (<= 3.2)
|
352
|
+
if result.respond_to?(:columns) # 4.0
|
353
|
+
result.columns.map! do |key| # [ [ 'id', ... ]
|
354
|
+
key.is_a?(String) ? key.sub(/^"?\w+"?\./, '') : key
|
355
|
+
end
|
356
|
+
else
|
357
|
+
result.map! do |row| # [ { 'id' => ... }, {...} ]
|
358
|
+
record = {}
|
359
|
+
row.each_key do |key|
|
360
|
+
if key.is_a?(String)
|
361
|
+
record[key.sub(/^"?\w+"?\./, '')] = row[key]
|
362
|
+
end
|
363
|
+
end
|
364
|
+
record
|
365
|
+
end
|
366
|
+
end
|
367
|
+
result
|
368
|
+
end
|
369
|
+
|
370
|
+
# @note We have an extra binds argument at the end due AR-2.3 support.
|
371
|
+
# @override
|
372
|
+
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
|
373
|
+
result = execute(sql, name, binds)
|
374
|
+
id_value || last_inserted_id(result)
|
375
|
+
end
|
376
|
+
|
377
|
+
# @note Does not support prepared statements for INSERT statements.
|
378
|
+
# @override
|
379
|
+
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
|
380
|
+
# NOTE: since SQLite JDBC does not support executeUpdate but only
|
381
|
+
# statement.execute we can not support prepared statements here :
|
382
|
+
execute(sql, name, binds)
|
383
|
+
end
|
384
|
+
|
385
|
+
def table_structure(table_name)
|
386
|
+
sql = "PRAGMA table_info(#{quote_table_name(table_name)})"
|
387
|
+
log(sql, 'SCHEMA') { @connection.execute_query_raw(sql) }
|
388
|
+
rescue ActiveRecord::JDBCError => error
|
389
|
+
e = ActiveRecord::StatementInvalid.new("Could not find table '#{table_name}'")
|
390
|
+
e.set_backtrace error.backtrace
|
391
|
+
raise e
|
392
|
+
end
|
393
|
+
|
394
|
+
# @override
|
395
|
+
def columns(table_name, name = nil)
|
396
|
+
pass_cast_type = respond_to?(:lookup_cast_type)
|
397
|
+
table_structure(table_name).map do |field|
|
398
|
+
sql_type = field['type']
|
399
|
+
if pass_cast_type
|
400
|
+
cast_type = lookup_cast_type(sql_type)
|
401
|
+
Column.new(field['name'], field['dflt_value'], cast_type, sql_type, field['notnull'] == 0)
|
402
|
+
else
|
403
|
+
Column.new(field['name'], field['dflt_value'], sql_type, field['notnull'] == 0)
|
404
|
+
end
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
# @override
|
409
|
+
def primary_key(table_name)
|
410
|
+
column = table_structure(table_name).find { |field| field['pk'].to_i == 1 }
|
411
|
+
column && column['name']
|
412
|
+
end
|
413
|
+
|
414
|
+
# NOTE: do not override indexes without testing support for 3.7.2 & 3.8.7 !
|
415
|
+
# @override
|
416
|
+
def indexes(table_name, name = nil)
|
417
|
+
# on JDBC 3.7 we'll simply do super since it can not handle "PRAGMA index_info"
|
418
|
+
return @connection.indexes(table_name, name) if sqlite_version < '3.8' # super
|
419
|
+
|
420
|
+
name ||= 'SCHEMA'
|
421
|
+
exec_query_raw("PRAGMA index_list(#{quote_table_name(table_name)})", name).map do |row|
|
422
|
+
index_name = row['name']
|
423
|
+
sql = "SELECT sql FROM sqlite_master"
|
424
|
+
sql << " WHERE name=#{quote(index_name)} AND type='index'"
|
425
|
+
sql << " UNION ALL "
|
426
|
+
sql << "SELECT sql FROM sqlite_temp_master"
|
427
|
+
sql << " WHERE name=#{quote(index_name)} AND type='index'"
|
428
|
+
where = nil
|
429
|
+
exec_query_raw(sql, name) do |index_sql|
|
430
|
+
match = /\sWHERE\s+(.+)$/i.match(index_sql)
|
431
|
+
where = match[1] if match
|
432
|
+
end
|
433
|
+
begin
|
434
|
+
columns = exec_query_raw("PRAGMA index_info('#{index_name}')", name).map { |col| col['name'] }
|
435
|
+
rescue => e
|
436
|
+
# NOTE: JDBC <= 3.8.7 bug work-around :
|
437
|
+
if e.message && e.message.index('[SQLITE_ERROR] SQL error or missing database')
|
438
|
+
columns = []
|
439
|
+
end
|
440
|
+
raise e
|
441
|
+
end
|
442
|
+
new_index_definition(table_name, index_name, row['unique'] != 0, columns, nil, nil, where)
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
# @override
|
447
|
+
def remove_index!(table_name, index_name)
|
448
|
+
execute "DROP INDEX #{quote_column_name(index_name)}"
|
449
|
+
end
|
450
|
+
|
451
|
+
# @override
|
452
|
+
def rename_table(table_name, new_name)
|
453
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
454
|
+
rename_table_indexes(table_name, new_name) if respond_to?(:rename_table_indexes) # AR-4.0 SchemaStatements
|
455
|
+
end
|
456
|
+
|
457
|
+
# SQLite has an additional restriction on the ALTER TABLE statement.
|
458
|
+
# @see http://www.sqlite.org/lang_altertable.html
|
459
|
+
def valid_alter_table_options( type, options)
|
460
|
+
type.to_sym != :primary_key
|
461
|
+
end
|
462
|
+
|
463
|
+
def add_column(table_name, column_name, type, options = {})
|
464
|
+
if supports_add_column? && valid_alter_table_options( type, options )
|
465
|
+
super(table_name, column_name, type, options)
|
466
|
+
else
|
467
|
+
alter_table(table_name) do |definition|
|
468
|
+
definition.column(column_name, type, options)
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
if ActiveRecord::VERSION::MAJOR >= 4
|
474
|
+
|
475
|
+
# @private
|
476
|
+
def remove_column(table_name, column_name, type = nil, options = {})
|
477
|
+
alter_table(table_name) do |definition|
|
478
|
+
definition.remove_column column_name
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
else
|
483
|
+
|
484
|
+
# @private
|
485
|
+
def remove_column(table_name, *column_names)
|
486
|
+
if column_names.empty?
|
487
|
+
raise ArgumentError.new(
|
488
|
+
"You must specify at least one column name." +
|
489
|
+
" Example: remove_column(:people, :first_name)"
|
490
|
+
)
|
491
|
+
end
|
492
|
+
column_names.flatten.each do |column_name|
|
493
|
+
alter_table(table_name) do |definition|
|
494
|
+
definition.columns.delete(definition[column_name])
|
495
|
+
end
|
496
|
+
end
|
497
|
+
end
|
498
|
+
alias :remove_columns :remove_column
|
499
|
+
|
500
|
+
end
|
501
|
+
|
502
|
+
def change_column_default(table_name, column_name, default) #:nodoc:
|
503
|
+
alter_table(table_name) do |definition|
|
504
|
+
definition[column_name].default = default
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
def change_column_null(table_name, column_name, null, default = nil)
|
509
|
+
unless null || default.nil?
|
510
|
+
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
511
|
+
end
|
512
|
+
alter_table(table_name) do |definition|
|
513
|
+
definition[column_name].null = null
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
517
|
+
def change_column(table_name, column_name, type, options = {})
|
518
|
+
alter_table(table_name) do |definition|
|
519
|
+
include_default = options_include_default?(options)
|
520
|
+
definition[column_name].instance_eval do
|
521
|
+
self.type = type
|
522
|
+
self.limit = options[:limit] if options.include?(:limit)
|
523
|
+
self.default = options[:default] if include_default
|
524
|
+
self.null = options[:null] if options.include?(:null)
|
525
|
+
self.precision = options[:precision] if options.include?(:precision)
|
526
|
+
self.scale = options[:scale] if options.include?(:scale)
|
527
|
+
end
|
528
|
+
end
|
529
|
+
end
|
530
|
+
|
531
|
+
def rename_column(table_name, column_name, new_column_name)
|
532
|
+
unless columns(table_name).detect{|c| c.name == column_name.to_s }
|
533
|
+
raise ActiveRecord::ActiveRecordError, "Missing column #{table_name}.#{column_name}"
|
534
|
+
end
|
535
|
+
alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s})
|
536
|
+
rename_column_indexes(table_name, column_name, new_column_name) if respond_to?(:rename_column_indexes) # AR-4.0 SchemaStatements
|
537
|
+
end
|
538
|
+
|
539
|
+
# @private
|
540
|
+
def add_lock!(sql, options)
|
541
|
+
sql # SELECT ... FOR UPDATE is redundant since the table is locked
|
542
|
+
end if ::ActiveRecord::VERSION::MAJOR < 3
|
543
|
+
|
544
|
+
def empty_insert_statement_value
|
545
|
+
# inherited (default) on 3.2 : "VALUES(DEFAULT)"
|
546
|
+
# inherited (default) on 4.0 : "DEFAULT VALUES"
|
547
|
+
# re-defined in native adapter on 3.2 "VALUES(NULL)"
|
548
|
+
# on 4.0 no longer re-defined (thus inherits default)
|
549
|
+
"DEFAULT VALUES"
|
550
|
+
end
|
551
|
+
|
552
|
+
def encoding
|
553
|
+
select_value 'PRAGMA encoding'
|
554
|
+
end
|
555
|
+
|
556
|
+
def last_insert_id
|
557
|
+
@connection.last_insert_rowid
|
558
|
+
end
|
559
|
+
|
560
|
+
protected
|
561
|
+
|
562
|
+
def last_inserted_id(result)
|
563
|
+
super || last_insert_id # NOTE: #last_insert_id call should not be needed
|
564
|
+
end
|
565
|
+
|
566
|
+
def translate_exception(exception, message)
|
567
|
+
if msg = exception.message
|
568
|
+
# SQLite 3.8.2 returns a newly formatted error message:
|
569
|
+
# UNIQUE constraint failed: *table_name*.*column_name*
|
570
|
+
# Older versions of SQLite return:
|
571
|
+
# column *column_name* is not unique
|
572
|
+
if msg.index('UNIQUE constraint failed: ') ||
|
573
|
+
msg =~ /column(s)? .* (is|are) not unique/
|
574
|
+
return ::ActiveRecord::RecordNotUnique.new(message, exception)
|
575
|
+
end
|
576
|
+
end
|
577
|
+
super
|
578
|
+
end
|
579
|
+
|
580
|
+
# @private available in native adapter way back to AR-2.3
|
581
|
+
class Version
|
582
|
+
include Comparable
|
583
|
+
|
584
|
+
def initialize(version_string)
|
585
|
+
@version = version_string.split('.').map!(&:to_i)
|
586
|
+
end
|
587
|
+
|
588
|
+
def <=>(version_string)
|
589
|
+
@version <=> version_string.split('.').map!(&:to_i)
|
590
|
+
end
|
591
|
+
|
592
|
+
def to_s
|
593
|
+
@version.join('.')
|
594
|
+
end
|
595
|
+
|
596
|
+
end
|
597
|
+
|
598
|
+
end
|
599
|
+
end
|
600
|
+
|
601
|
+
module ActiveRecord::ConnectionAdapters
|
602
|
+
|
603
|
+
remove_const(:SQLite3Adapter) if const_defined?(:SQLite3Adapter)
|
604
|
+
class SQLite3Adapter < JdbcAdapter
|
605
|
+
include ::ArJdbc::SQLite3
|
606
|
+
include ::ArJdbc::SQLite3::ExplainSupport
|
607
|
+
|
608
|
+
# @private
|
609
|
+
Version = ArJdbc::SQLite3::Version
|
610
|
+
|
611
|
+
class Column < JdbcColumn
|
612
|
+
include ::ArJdbc::SQLite3::ColumnMethods
|
613
|
+
|
614
|
+
def initialize(name, *args)
|
615
|
+
if Hash === name
|
616
|
+
super
|
617
|
+
else
|
618
|
+
super(nil, name, *args)
|
619
|
+
end
|
620
|
+
end
|
621
|
+
|
622
|
+
def self.string_to_binary(value)
|
623
|
+
value
|
624
|
+
end
|
625
|
+
|
626
|
+
def self.binary_to_string(value)
|
627
|
+
if value.respond_to?(:encoding) && value.encoding != Encoding::ASCII_8BIT
|
628
|
+
value = value.force_encoding(Encoding::ASCII_8BIT)
|
629
|
+
end
|
630
|
+
value
|
631
|
+
end
|
632
|
+
end
|
633
|
+
|
634
|
+
end
|
635
|
+
|
636
|
+
# NOTE: SQLite3Column exists in native adapter since AR 4.0
|
637
|
+
remove_const(:SQLite3Column) if const_defined?(:SQLite3Column)
|
638
|
+
SQLite3Column = SQLite3Adapter::Column
|
639
|
+
|
640
|
+
if ActiveRecord::VERSION::MAJOR <= 3
|
641
|
+
remove_const(:SQLiteColumn) if const_defined?(:SQLiteColumn)
|
642
|
+
SQLiteColumn = SQLite3Column
|
643
|
+
|
644
|
+
remove_const(:SQLiteAdapter) if const_defined?(:SQLiteAdapter)
|
645
|
+
|
646
|
+
SQLiteAdapter = SQLite3Adapter
|
647
|
+
end
|
648
|
+
end
|
649
|
+
|
650
|
+
module ArJdbc
|
651
|
+
module SQLite3
|
652
|
+
Column = ::ActiveRecord::ConnectionAdapters::SQLite3Adapter::Column
|
653
|
+
end
|
654
|
+
end
|