Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define adapter type maps statically when possible #2199

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ jobs:
'2.7'
]
env:
ORACLE_HOME: /usr/lib/oracle/18.5/client64
LD_LIBRARY_PATH: /usr/lib/oracle/18.5/client64/lib
ORACLE_HOME: /usr/lib/oracle/21/client64
LD_LIBRARY_PATH: /usr/lib/oracle/21/client64/lib
NLS_LANG: AMERICAN_AMERICA.AL32UTF8
TNS_ADMIN: ./ci/network/admin
DATABASE_NAME: XEPDB1
Expand Down Expand Up @@ -57,14 +57,14 @@ jobs:
sudo apt-get install alien
- name: Download Oracle client
run: |
wget -q https://download.oracle.com/otn_software/linux/instantclient/185000/oracle-instantclient18.5-basic-18.5.0.0.0-3.x86_64.rpm
wget -q https://download.oracle.com/otn_software/linux/instantclient/185000/oracle-instantclient18.5-sqlplus-18.5.0.0.0-3.x86_64.rpm
wget -q https://download.oracle.com/otn_software/linux/instantclient/185000/oracle-instantclient18.5-devel-18.5.0.0.0-3.x86_64.rpm
wget -q https://download.oracle.com/otn_software/linux/instantclient/214000/oracle-instantclient-basic-21.4.0.0.0-1.x86_64.rpm
wget -q https://download.oracle.com/otn_software/linux/instantclient/214000/oracle-instantclient-sqlplus-21.4.0.0.0-1.x86_64.rpm
wget -q https://download.oracle.com/otn_software/linux/instantclient/214000/oracle-instantclient-devel-21.4.0.0.0-1.x86_64.rpm
- name: Install Oracle client
run: |
sudo alien -i oracle-instantclient18.5-basic-18.5.0.0.0-3.x86_64.rpm
sudo alien -i oracle-instantclient18.5-sqlplus-18.5.0.0.0-3.x86_64.rpm
sudo alien -i oracle-instantclient18.5-devel-18.5.0.0.0-3.x86_64.rpm
sudo alien -i oracle-instantclient-basic-21.4.0.0.0-1.x86_64.rpm
sudo alien -i oracle-instantclient-sqlplus-21.4.0.0.0-1.x86_64.rpm
sudo alien -i oracle-instantclient-devel-21.4.0.0.0-1.x86_64.rpm
- name: Install JDBC Driver
run: |
wget -q https://download.oracle.com/otn-pub/otn_software/jdbc/211/ojdbc11.jar -O ./lib/ojdbc11.jar
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@ def execute(sql, name = nil, async: false)
log(sql, name, async: async) { @connection.exec(sql) }
end

def clear_cache! # :nodoc:
reload_type_map
super
end

def exec_query(sql, name = "SQL", binds = [], prepare: false, async: false)
type_casted_binds = type_casted_binds(binds)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module ActiveRecord
module ConnectionAdapters
module OracleEnhanced
module JDBCQuoting
def _type_cast(value)
def type_cast(value)
case value
when ActiveModel::Type::Binary::Data
blob = Java::OracleSql::BLOB.createTemporary(@connection.raw_connection, false, Java::OracleSql::BLOB::DURATION_SESSION)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module ActiveRecord
module ConnectionAdapters
module OracleEnhanced
module OCIQuoting
def _type_cast(value)
def type_cast(value)
case value
when ActiveModel::Type::Binary::Data
lob_value = value == "" ? " " : value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def quote_string(s) # :nodoc:
s.gsub(/'/, "''")
end

def _quote(value) # :nodoc:
def quote(value) # :nodoc:
case value
when Type::OracleEnhanced::CharacterString::Data then
"'#{quote_string(value.to_s)}'"
Expand Down Expand Up @@ -111,7 +111,7 @@ def unquoted_false # :nodoc:
"0"
end

def _type_cast(value)
def type_cast(value)
case value
when Type::OracleEnhanced::TimestampTz::Data, Type::OracleEnhanced::TimestampLtz::Data
if value.acts_like?(:time)
Expand Down
164 changes: 86 additions & 78 deletions lib/active_record/connection_adapters/oracle_enhanced_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -690,98 +690,106 @@ def check_version
end
end

private
def initialize_type_map(m = type_map)
super
# oracle
register_class_with_precision m, %r(WITH TIME ZONE)i, Type::OracleEnhanced::TimestampTz
register_class_with_precision m, %r(WITH LOCAL TIME ZONE)i, Type::OracleEnhanced::TimestampLtz
register_class_with_limit m, %r(raw)i, Type::OracleEnhanced::Raw
register_class_with_limit m, %r{^(char)}i, Type::OracleEnhanced::CharacterString
register_class_with_limit m, %r{^(nchar)}i, Type::OracleEnhanced::String
register_class_with_limit m, %r(varchar)i, Type::OracleEnhanced::String
register_class_with_limit m, %r(clob)i, Type::OracleEnhanced::Text
register_class_with_limit m, %r(nclob)i, Type::OracleEnhanced::NationalCharacterText

m.register_type "NCHAR", Type::OracleEnhanced::NationalCharacterString.new
m.alias_type %r(NVARCHAR2)i, "NCHAR"

m.register_type(%r(NUMBER)i) do |sql_type|
scale = extract_scale(sql_type)
precision = extract_precision(sql_type)
limit = extract_limit(sql_type)
if scale == 0
Type::OracleEnhanced::Integer.new(precision: precision, limit: limit)
else
Type::Decimal.new(precision: precision, scale: scale)
class << self
private
def initialize_type_map(m)
super
# oracle
register_class_with_precision m, %r(WITH TIME ZONE)i, Type::OracleEnhanced::TimestampTz
register_class_with_precision m, %r(WITH LOCAL TIME ZONE)i, Type::OracleEnhanced::TimestampLtz
register_class_with_limit m, %r(raw)i, Type::OracleEnhanced::Raw
register_class_with_limit m, %r{^(char)}i, Type::OracleEnhanced::CharacterString
register_class_with_limit m, %r{^(nchar)}i, Type::OracleEnhanced::String
register_class_with_limit m, %r(varchar)i, Type::OracleEnhanced::String
register_class_with_limit m, %r(clob)i, Type::OracleEnhanced::Text
register_class_with_limit m, %r(nclob)i, Type::OracleEnhanced::NationalCharacterText

m.register_type "NCHAR", Type::OracleEnhanced::NationalCharacterString.new
m.alias_type %r(NVARCHAR2)i, "NCHAR"

m.register_type(%r(NUMBER)i) do |sql_type|
scale = extract_scale(sql_type)
precision = extract_precision(sql_type)
limit = extract_limit(sql_type)
if scale == 0
Type::OracleEnhanced::Integer.new(precision: precision, limit: limit)
else
Type::Decimal.new(precision: precision, scale: scale)
end
end
end

if OracleEnhancedAdapter.emulate_booleans
m.register_type %r(^NUMBER\(1\))i, Type::Boolean.new
if OracleEnhancedAdapter.emulate_booleans
m.register_type %r(^NUMBER\(1\))i, Type::Boolean.new
end
end
end
end

def extract_value_from_default(default)
case default
when String
default.gsub("''", "'")
else
default
end
end
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }

def extract_limit(sql_type) # :nodoc:
case sql_type
when /^bigint/i
19
when /\((.*)\)/
$1.to_i
end
def type_map
TYPE_MAP
end

def extract_value_from_default(default)
case default
when String
default.gsub("''", "'")
else
default
end
end

def translate_exception(exception, message:, sql:, binds:) # :nodoc:
case @connection.error_code(exception)
when 1
RecordNotUnique.new(message, sql: sql, binds: binds)
when 60
Deadlocked.new(message)
when 900, 904, 942, 955, 1418, 2289, 2449, 17008
ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
when 1400
ActiveRecord::NotNullViolation.new(message, sql: sql, binds: binds)
when 2291, 2292
InvalidForeignKey.new(message, sql: sql, binds: binds)
when 12899
ValueTooLong.new(message, sql: sql, binds: binds)
else
super
end
def extract_limit(sql_type) # :nodoc:
case sql_type
when /^bigint/i
19
when /\((.*)\)/
$1.to_i
end
end

# create bind object for type String
def bind_string(name, value)
ActiveRecord::Relation::QueryAttribute.new(name, value, Type::OracleEnhanced::String.new)
def translate_exception(exception, message:, sql:, binds:) # :nodoc:
case @connection.error_code(exception)
when 1
RecordNotUnique.new(message, sql: sql, binds: binds)
when 60
Deadlocked.new(message)
when 900, 904, 942, 955, 1418, 2289, 2449, 17008
ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
when 1400
ActiveRecord::NotNullViolation.new(message, sql: sql, binds: binds)
when 2291, 2292
InvalidForeignKey.new(message, sql: sql, binds: binds)
when 12899
ValueTooLong.new(message, sql: sql, binds: binds)
else
super
end
end

# call select_values using binds even if surrounding SQL preparation/execution is done + # with conn.unprepared_statement (like AR.to_sql)
def select_values_forcing_binds(arel, name, binds)
# remove possible force of unprepared SQL during dictionary access
unprepared_statement_forced = prepared_statements_disabled_cache.include?(object_id)
prepared_statements_disabled_cache.delete(object_id) if unprepared_statement_forced
# create bind object for type String
def bind_string(name, value)
ActiveRecord::Relation::QueryAttribute.new(name, value, Type::OracleEnhanced::String.new)
end

select_values(arel, name, binds)
ensure
# Restore unprepared_statement setting for surrounding SQL
prepared_statements_disabled_cache.add(object_id) if unprepared_statement_forced
end
# call select_values using binds even if surrounding SQL preparation/execution is done + # with conn.unprepared_statement (like AR.to_sql)
def select_values_forcing_binds(arel, name, binds)
# remove possible force of unprepared SQL during dictionary access
unprepared_statement_forced = prepared_statements_disabled_cache.include?(object_id)
prepared_statements_disabled_cache.delete(object_id) if unprepared_statement_forced

def select_value_forcing_binds(arel, name, binds)
single_value_from_rows(select_values_forcing_binds(arel, name, binds))
end
select_values(arel, name, binds)
ensure
# Restore unprepared_statement setting for surrounding SQL
prepared_statements_disabled_cache.add(object_id) if unprepared_statement_forced
end

def select_value_forcing_binds(arel, name, binds)
single_value_from_rows(select_values_forcing_binds(arel, name, binds))
end

ActiveRecord::Type.register(:boolean, Type::OracleEnhanced::Boolean, adapter: :oracle_enhanced)
ActiveRecord::Type.register(:json, Type::OracleEnhanced::Json, adapter: :oracle_enhanced)
ActiveRecord::Type.register(:boolean, Type::OracleEnhanced::Boolean, adapter: :oracle_enhanced)
ActiveRecord::Type.register(:json, Type::OracleEnhanced::Json, adapter: :oracle_enhanced)
end
end
end
Expand Down