From 95c3965a4a302571c49727095b5a3c607267cc71 Mon Sep 17 00:00:00 2001 From: Shehbaz Date: Tue, 1 Sep 2020 13:47:22 +0100 Subject: [PATCH 01/26] Change gemspecs and gemfile --- Gemfile | 3 --- odbc_adapter.gemspec | 12 +++++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Gemfile b/Gemfile index c0cf8f42..fa75df15 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,3 @@ source 'https://rubygems.org' gemspec - -gem 'activerecord', '5.0.1' -gem 'pry', '~> 0.11.1' diff --git a/odbc_adapter.gemspec b/odbc_adapter.gemspec index 4bf0142d..7e3fbd48 100644 --- a/odbc_adapter.gemspec +++ b/odbc_adapter.gemspec @@ -19,11 +19,13 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ['lib'] + spec.add_dependency 'activerecord', '>= 5.0' spec.add_dependency 'ruby-odbc', '~> 0.9' - spec.add_development_dependency 'bundler', '~> 1.14' - spec.add_development_dependency 'minitest', '~> 5.10' - spec.add_development_dependency 'rake', '~> 12.0' - spec.add_development_dependency 'rubocop', '0.48.1' - spec.add_development_dependency 'simplecov', '~> 0.14' + spec.add_development_dependency 'bundler', '>= 1.14' + spec.add_development_dependency 'minitest', '>= 5.10' + spec.add_development_dependency 'rake', '>= 12.0' + spec.add_development_dependency 'rubocop', '<= 0.58' + spec.add_development_dependency 'simplecov', '>= 0.14' + spec.add_development_dependency 'pry', '~> 0.13.1' end From af47f3d61e25d7d4733cd5d07bd18b70fa47b4b9 Mon Sep 17 00:00:00 2001 From: Shehbaz Date: Wed, 2 Sep 2020 13:48:53 +0100 Subject: [PATCH 02/26] Upgrade to rails5.2.1 --- .../connection_adapters/odbc_adapter.rb | 1 - lib/odbc_adapter/adapters/mysql_odbc_adapter.rb | 6 +----- lib/odbc_adapter/adapters/null_odbc_adapter.rb | 5 +---- lib/odbc_adapter/database_statements.rb | 11 ++++++++--- lib/odbc_adapter/version.rb | 2 +- odbc_adapter.gemspec | 14 +++++++------- 6 files changed, 18 insertions(+), 21 deletions(-) diff --git a/lib/active_record/connection_adapters/odbc_adapter.rb b/lib/active_record/connection_adapters/odbc_adapter.rb index 06686785..12ef819e 100644 --- a/lib/active_record/connection_adapters/odbc_adapter.rb +++ b/lib/active_record/connection_adapters/odbc_adapter.rb @@ -1,5 +1,4 @@ require 'active_record' -require 'arel/visitors/bind_visitor' require 'odbc' require 'odbc_utf8' diff --git a/lib/odbc_adapter/adapters/mysql_odbc_adapter.rb b/lib/odbc_adapter/adapters/mysql_odbc_adapter.rb index eaa690ef..0d439462 100644 --- a/lib/odbc_adapter/adapters/mysql_odbc_adapter.rb +++ b/lib/odbc_adapter/adapters/mysql_odbc_adapter.rb @@ -5,12 +5,8 @@ module Adapters class MySQLODBCAdapter < ActiveRecord::ConnectionAdapters::ODBCAdapter PRIMARY_KEY = 'INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY'.freeze - class BindSubstitution < Arel::Visitors::MySQL - include Arel::Visitors::BindVisitor - end - def arel_visitor - BindSubstitution.new(self) + Arel::Visitors::MySQL.new(self) end # Explicitly turning off prepared statements in the MySQL adapter because diff --git a/lib/odbc_adapter/adapters/null_odbc_adapter.rb b/lib/odbc_adapter/adapters/null_odbc_adapter.rb index 1a179905..98cb4149 100644 --- a/lib/odbc_adapter/adapters/null_odbc_adapter.rb +++ b/lib/odbc_adapter/adapters/null_odbc_adapter.rb @@ -4,15 +4,12 @@ module Adapters # registry. This allows for minimal support for DBMSs for which we don't # have an explicit adapter. class NullODBCAdapter < ActiveRecord::ConnectionAdapters::ODBCAdapter - class BindSubstitution < Arel::Visitors::ToSql - include Arel::Visitors::BindVisitor - end # Using a BindVisitor so that the SQL string gets substituted before it is # sent to the DBMS (to attempt to get as much coverage as possible for # DBMSs we don't support). def arel_visitor - BindSubstitution.new(self) + Arel::Visitors::PostgreSQL.new(self) end # Explicitly turning off prepared_statements in the null adapter because diff --git a/lib/odbc_adapter/database_statements.rb b/lib/odbc_adapter/database_statements.rb index cac31682..1922dd1c 100644 --- a/lib/odbc_adapter/database_statements.rb +++ b/lib/odbc_adapter/database_statements.rb @@ -10,7 +10,7 @@ module DatabaseStatements def execute(sql, name = nil, binds = []) log(sql, name) do if prepared_statements - @connection.do(sql, *prepared_binds(binds)) + @connection.do(prepare_statement_sub(sql), *prepared_binds(binds)) else @connection.do(sql) end @@ -24,7 +24,7 @@ def exec_query(sql, name = 'SQL', binds = [], prepare: false) # rubocop:disable log(sql, name) do stmt = if prepared_statements - @connection.run(sql, *prepared_binds(binds)) + @connection.do(prepare_statement_sub(sql), *prepared_binds(binds)) else @connection.run(sql) end @@ -127,8 +127,13 @@ def nullability(col_name, is_nullable, nullable) col_name == 'id' ? false : result end + # Adapt to Rails 5.2 + def prepare_statement_sub(sql) + sql.gsub(/\$\d+/, '?') + end + def prepared_binds(binds) - prepare_binds_for_database(binds).map { |bind| _type_cast(bind) } + binds.map(&:value_for_database).map { |bind| _type_cast(bind) } end end end diff --git a/lib/odbc_adapter/version.rb b/lib/odbc_adapter/version.rb index 4a65ac67..598c96ec 100644 --- a/lib/odbc_adapter/version.rb +++ b/lib/odbc_adapter/version.rb @@ -1,3 +1,3 @@ module ODBCAdapter - VERSION = '5.0.5'.freeze + VERSION = '5.0.6'.freeze end diff --git a/odbc_adapter.gemspec b/odbc_adapter.gemspec index 7e3fbd48..d108cb70 100644 --- a/odbc_adapter.gemspec +++ b/odbc_adapter.gemspec @@ -19,13 +19,13 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ['lib'] - spec.add_dependency 'activerecord', '>= 5.0' + spec.add_dependency 'activerecord', '>= 5.2.1' spec.add_dependency 'ruby-odbc', '~> 0.9' - spec.add_development_dependency 'bundler', '>= 1.14' - spec.add_development_dependency 'minitest', '>= 5.10' - spec.add_development_dependency 'rake', '>= 12.0' - spec.add_development_dependency 'rubocop', '<= 0.58' - spec.add_development_dependency 'simplecov', '>= 0.14' - spec.add_development_dependency 'pry', '~> 0.13.1' + spec.add_development_dependency 'bundler', '~> 1.14' + spec.add_development_dependency 'minitest', '~> 5.10' + spec.add_development_dependency 'rake', '~> 12.0' + spec.add_development_dependency 'rubocop', '0.48.1' + spec.add_development_dependency 'simplecov', '~> 0.14' + spec.add_development_dependency 'pry', '~> 0.11.1' end From abdfd4593514d395c35b98c01bd5330e008f82d9 Mon Sep 17 00:00:00 2001 From: Shehbaz Date: Tue, 29 Sep 2020 08:04:33 +0100 Subject: [PATCH 03/26] Upgrade to rails 6 --- lib/active_record/connection_adapters/odbc_adapter.rb | 2 +- lib/odbc_adapter/column.rb | 4 ++-- lib/odbc_adapter/schema_statements.rb | 2 +- odbc_adapter.gemspec | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/active_record/connection_adapters/odbc_adapter.rb b/lib/active_record/connection_adapters/odbc_adapter.rb index 12ef819e..17a0ef47 100644 --- a/lib/active_record/connection_adapters/odbc_adapter.rb +++ b/lib/active_record/connection_adapters/odbc_adapter.rb @@ -136,7 +136,7 @@ def disconnect! # Build a new column object from the given options. Effectively the same # as super except that it also passes in the native type. # rubocop:disable Metrics/ParameterLists - def new_column(name, default, sql_type_metadata, null, table_name, default_function = nil, collation = nil, native_type = nil) + def new_column(name, default, sql_type_metadata, null, default_function = nil) ::ODBCAdapter::Column.new(name, default, sql_type_metadata, null, table_name, default_function, collation, native_type) end diff --git a/lib/odbc_adapter/column.rb b/lib/odbc_adapter/column.rb index 36492a82..12f3f565 100644 --- a/lib/odbc_adapter/column.rb +++ b/lib/odbc_adapter/column.rb @@ -5,8 +5,8 @@ class Column < ActiveRecord::ConnectionAdapters::Column # Add the native_type accessor to allow the native DBMS to report back what # it uses to represent the column internally. # rubocop:disable Metrics/ParameterLists - def initialize(name, default, sql_type_metadata = nil, null = true, table_name = nil, native_type = nil, default_function = nil, collation = nil) - super(name, default, sql_type_metadata, null, table_name, default_function, collation) + def initialize(name, default, sql_type_metadata = nil, null = true, native_type = nil, default_function = nil) + super(name, default, sql_type_metadata, null, default_function) @native_type = native_type end end diff --git a/lib/odbc_adapter/schema_statements.rb b/lib/odbc_adapter/schema_statements.rb index df149765..88fd2ad7 100644 --- a/lib/odbc_adapter/schema_statements.rb +++ b/lib/odbc_adapter/schema_statements.rb @@ -83,7 +83,7 @@ def columns(table_name, _name = nil) end sql_type_metadata = ActiveRecord::ConnectionAdapters::SqlTypeMetadata.new(**args) - cols << new_column(format_case(col_name), col_default, sql_type_metadata, col_nullable, table_name, col_native_type) + cols << new_column(format_case(col_name), col_default, sql_type_metadata, col_nullable, col_native_type) end end diff --git a/odbc_adapter.gemspec b/odbc_adapter.gemspec index d108cb70..dd63ea94 100644 --- a/odbc_adapter.gemspec +++ b/odbc_adapter.gemspec @@ -19,7 +19,7 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ['lib'] - spec.add_dependency 'activerecord', '>= 5.2.1' + spec.add_dependency 'activerecord', '>= 6.0.1' spec.add_dependency 'ruby-odbc', '~> 0.9' spec.add_development_dependency 'bundler', '~> 1.14' From b51a5939595ceb08267d48049cf18d263366b7d2 Mon Sep 17 00:00:00 2001 From: Shehbaz Date: Tue, 29 Sep 2020 08:27:00 +0100 Subject: [PATCH 04/26] Backward compatibility --- .rubocop.yml | 2 +- odbc_adapter.gemspec | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 9055a997..28d2dcd7 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,7 +1,7 @@ AllCops: DisplayCopNames: true DisplayStyleGuide: true - TargetRubyVersion: 2.1 + TargetRubyVersion: 2.2 Exclude: - 'vendor/**/*' diff --git a/odbc_adapter.gemspec b/odbc_adapter.gemspec index dd63ea94..0eafa155 100644 --- a/odbc_adapter.gemspec +++ b/odbc_adapter.gemspec @@ -19,10 +19,10 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ['lib'] - spec.add_dependency 'activerecord', '>= 6.0.1' + spec.add_dependency 'activerecord', '>= 5.0.1' spec.add_dependency 'ruby-odbc', '~> 0.9' - spec.add_development_dependency 'bundler', '~> 1.14' + spec.add_development_dependency 'bundler', '>= 1.14' spec.add_development_dependency 'minitest', '~> 5.10' spec.add_development_dependency 'rake', '~> 12.0' spec.add_development_dependency 'rubocop', '0.48.1' From 7a43f84c15a0f714c691768df902ccbabfe94a11 Mon Sep 17 00:00:00 2001 From: Shehbaz Date: Tue, 29 Sep 2020 12:57:18 +0100 Subject: [PATCH 05/26] Update bind params --- .../adapters/postgresql_odbc_adapter.rb | 2 +- lib/odbc_adapter/database_statements.rb | 23 ++++++++++--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb b/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb index 28a28f7c..79529b4d 100644 --- a/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb +++ b/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb @@ -35,7 +35,7 @@ def default_sequence_name(table_name, pk = nil) "#{table_name}_#{pk || 'id'}_seq" end - def sql_for_insert(sql, pk, _id_value, _sequence_name, binds) + def sql_for_insert(sql, pk, binds) unless pk table_ref = extract_table_ref_from_insert_sql(sql) pk = primary_key(table_ref) if table_ref diff --git a/lib/odbc_adapter/database_statements.rb b/lib/odbc_adapter/database_statements.rb index 1922dd1c..859fc43a 100644 --- a/lib/odbc_adapter/database_statements.rb +++ b/lib/odbc_adapter/database_statements.rb @@ -9,11 +9,8 @@ module DatabaseStatements # Returns the number of rows affected. def execute(sql, name = nil, binds = []) log(sql, name) do - if prepared_statements - @connection.do(prepare_statement_sub(sql), *prepared_binds(binds)) - else - @connection.do(sql) - end + sql = bind_params(binds, sql) if prepared_statements + @connection.do(sql) end end @@ -22,12 +19,8 @@ def execute(sql, name = nil, binds = []) # the executed +sql+ statement. def exec_query(sql, name = 'SQL', binds = [], prepare: false) # rubocop:disable Lint/UnusedMethodArgument log(sql, name) do - stmt = - if prepared_statements - @connection.do(prepare_statement_sub(sql), *prepared_binds(binds)) - else - @connection.run(sql) - end + sql = bind_params(binds, sql) if prepared_statements + stmt = @connection.run(sql) columns = stmt.columns values = stmt.to_a @@ -81,6 +74,14 @@ def dbms_type_cast(_columns, values) values end + def bind_params(binds, sql) + prepared_binds = *prepared_binds(binds) + prepared_binds.each.with_index(1) do |val, ind| + sql = sql.gsub("$#{ind}", "'#{val}'") + end + sql + end + # Assume received identifier is in DBMS's data dictionary case. def format_case(identifier) if database_metadata.upcase_identifiers? From 7c56bcc61bd6c75d61c23be45cbcb3c9448483eb Mon Sep 17 00:00:00 2001 From: Shehbaz Date: Thu, 8 Oct 2020 14:24:32 +0100 Subject: [PATCH 06/26] Change params for Column class initializer --- lib/active_record/connection_adapters/odbc_adapter.rb | 4 ++-- lib/odbc_adapter/column.rb | 4 ++-- lib/odbc_adapter/schema_statements.rb | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/active_record/connection_adapters/odbc_adapter.rb b/lib/active_record/connection_adapters/odbc_adapter.rb index 17a0ef47..3422cd61 100644 --- a/lib/active_record/connection_adapters/odbc_adapter.rb +++ b/lib/active_record/connection_adapters/odbc_adapter.rb @@ -136,8 +136,8 @@ def disconnect! # Build a new column object from the given options. Effectively the same # as super except that it also passes in the native type. # rubocop:disable Metrics/ParameterLists - def new_column(name, default, sql_type_metadata, null, default_function = nil) - ::ODBCAdapter::Column.new(name, default, sql_type_metadata, null, table_name, default_function, collation, native_type) + def new_column(name, default, sql_type_metadata, null, table_name, native_type = nil) + ::ODBCAdapter::Column.new(name, default, sql_type_metadata, null, table_name, native_type) end protected diff --git a/lib/odbc_adapter/column.rb b/lib/odbc_adapter/column.rb index 12f3f565..93044fea 100644 --- a/lib/odbc_adapter/column.rb +++ b/lib/odbc_adapter/column.rb @@ -5,8 +5,8 @@ class Column < ActiveRecord::ConnectionAdapters::Column # Add the native_type accessor to allow the native DBMS to report back what # it uses to represent the column internally. # rubocop:disable Metrics/ParameterLists - def initialize(name, default, sql_type_metadata = nil, null = true, native_type = nil, default_function = nil) - super(name, default, sql_type_metadata, null, default_function) + def initialize(name, default, sql_type_metadata = nil, null = true, table_name = nil, native_type = nil) + super(name, default, sql_type_metadata, null, table_name) @native_type = native_type end end diff --git a/lib/odbc_adapter/schema_statements.rb b/lib/odbc_adapter/schema_statements.rb index 88fd2ad7..df149765 100644 --- a/lib/odbc_adapter/schema_statements.rb +++ b/lib/odbc_adapter/schema_statements.rb @@ -83,7 +83,7 @@ def columns(table_name, _name = nil) end sql_type_metadata = ActiveRecord::ConnectionAdapters::SqlTypeMetadata.new(**args) - cols << new_column(format_case(col_name), col_default, sql_type_metadata, col_nullable, col_native_type) + cols << new_column(format_case(col_name), col_default, sql_type_metadata, col_nullable, table_name, col_native_type) end end From bf7a016c37be8ce05861952e7698dee7ae5b65c6 Mon Sep 17 00:00:00 2001 From: Shehbaz Date: Fri, 16 Oct 2020 13:40:38 +0100 Subject: [PATCH 07/26] Support json type field --- lib/odbc_adapter/database_statements.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/odbc_adapter/database_statements.rb b/lib/odbc_adapter/database_statements.rb index 859fc43a..d4eed5bf 100644 --- a/lib/odbc_adapter/database_statements.rb +++ b/lib/odbc_adapter/database_statements.rb @@ -28,6 +28,12 @@ def exec_query(sql, name = 'SQL', binds = [], prepare: false) # rubocop:disable values = dbms_type_cast(columns.values, values) column_names = columns.keys.map { |key| format_case(key) } + upcase_coulmn_names = columns.keys + values.each.with_index(0) do |_row_value, row_index| + columns.each.with_index(0) do |_col_value, col_index| + values[row_index][col_index] = json_parsing(values[row_index][col_index], columns[upcase_coulmn_names[col_index]]) + end + end ActiveRecord::Result.new(column_names, values) end end @@ -74,6 +80,17 @@ def dbms_type_cast(_columns, values) values end + # A custom fuction to check the string and json column. + # If the column value is string, JSON.parse will raise expection, which will just + # return the original value, Otherwise this will return parsed value. + def json_parsing(values, column) + return values unless column.type == SQL_CHARACTER_VARYING_DATATYPE && values.include?('{') && values.include?('}') + + JSON.parse values + rescue + values + end + def bind_params(binds, sql) prepared_binds = *prepared_binds(binds) prepared_binds.each.with_index(1) do |val, ind| From 2696c7a7ae0a50002d086edc1d70051846ff9fc4 Mon Sep 17 00:00:00 2001 From: Shehbaz Date: Thu, 29 Oct 2020 06:30:36 +0000 Subject: [PATCH 08/26] Support json and date datatype --- .../connection_adapters/odbc_adapter.rb | 4 ++++ lib/odbc_adapter/adapters/null_odbc_adapter.rb | 4 +++- .../adapters/postgresql_odbc_adapter.rb | 4 ++++ lib/odbc_adapter/database_statements.rb | 17 ----------------- lib/odbc_adapter/schema_statements.rb | 2 ++ 5 files changed, 13 insertions(+), 18 deletions(-) diff --git a/lib/active_record/connection_adapters/odbc_adapter.rb b/lib/active_record/connection_adapters/odbc_adapter.rb index 3422cd61..b7242022 100644 --- a/lib/active_record/connection_adapters/odbc_adapter.rb +++ b/lib/active_record/connection_adapters/odbc_adapter.rb @@ -74,6 +74,9 @@ class ODBCAdapter < AbstractAdapter ADAPTER_NAME = 'ODBC'.freeze BOOLEAN_TYPE = 'BOOLEAN'.freeze + VARIANT_TYPE = 'VARIANT'.freeze + DATE_TYPE = 'DATE'.freeze + JSON_TYPE = 'json'.freeze ERR_DUPLICATE_KEY_VALUE = 23_505 ERR_QUERY_TIMED_OUT = 57_014 @@ -146,6 +149,7 @@ def new_column(name, default, sql_type_metadata, null, table_name, native_type = # Here, ODBC and ODBC_UTF8 constants are interchangeable def initialize_type_map(map) map.register_type 'boolean', Type::Boolean.new + map.register_type 'json', Type::Json.new map.register_type ODBC::SQL_CHAR, Type::String.new map.register_type ODBC::SQL_LONGVARCHAR, Type::Text.new map.register_type ODBC::SQL_TINYINT, Type::Integer.new(limit: 4) diff --git a/lib/odbc_adapter/adapters/null_odbc_adapter.rb b/lib/odbc_adapter/adapters/null_odbc_adapter.rb index 98cb4149..70a51e01 100644 --- a/lib/odbc_adapter/adapters/null_odbc_adapter.rb +++ b/lib/odbc_adapter/adapters/null_odbc_adapter.rb @@ -4,7 +4,9 @@ module Adapters # registry. This allows for minimal support for DBMSs for which we don't # have an explicit adapter. class NullODBCAdapter < ActiveRecord::ConnectionAdapters::ODBCAdapter - + VARIANT_TYPE = 'VARIANT'.freeze + DATE_TYPE = 'DATE'.freeze + JSON_TYPE = 'json'.freeze # Using a BindVisitor so that the SQL string gets substituted before it is # sent to the DBMS (to attempt to get as much coverage as possible for # DBMSs we don't support). diff --git a/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb b/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb index 79529b4d..81cd4f27 100644 --- a/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb +++ b/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb @@ -5,12 +5,16 @@ module Adapters class PostgreSQLODBCAdapter < ActiveRecord::ConnectionAdapters::ODBCAdapter BOOLEAN_TYPE = 'bool'.freeze PRIMARY_KEY = 'SERIAL PRIMARY KEY'.freeze + VARIANT_TYPE = 'VARIANT'.freeze + DATE_TYPE = 'DATE'.freeze + JSON_TYPE = 'json'.freeze alias create insert # Override to handle booleans appropriately def native_database_types @native_database_types ||= super.merge(boolean: { name: 'bool' }) + @native_database_types ||= super.merge(json: { name: 'json' }) end def arel_visitor diff --git a/lib/odbc_adapter/database_statements.rb b/lib/odbc_adapter/database_statements.rb index d4eed5bf..859fc43a 100644 --- a/lib/odbc_adapter/database_statements.rb +++ b/lib/odbc_adapter/database_statements.rb @@ -28,12 +28,6 @@ def exec_query(sql, name = 'SQL', binds = [], prepare: false) # rubocop:disable values = dbms_type_cast(columns.values, values) column_names = columns.keys.map { |key| format_case(key) } - upcase_coulmn_names = columns.keys - values.each.with_index(0) do |_row_value, row_index| - columns.each.with_index(0) do |_col_value, col_index| - values[row_index][col_index] = json_parsing(values[row_index][col_index], columns[upcase_coulmn_names[col_index]]) - end - end ActiveRecord::Result.new(column_names, values) end end @@ -80,17 +74,6 @@ def dbms_type_cast(_columns, values) values end - # A custom fuction to check the string and json column. - # If the column value is string, JSON.parse will raise expection, which will just - # return the original value, Otherwise this will return parsed value. - def json_parsing(values, column) - return values unless column.type == SQL_CHARACTER_VARYING_DATATYPE && values.include?('{') && values.include?('}') - - JSON.parse values - rescue - values - end - def bind_params(binds, sql) prepared_binds = *prepared_binds(binds) prepared_binds.each.with_index(1) do |val, ind| diff --git a/lib/odbc_adapter/schema_statements.rb b/lib/odbc_adapter/schema_statements.rb index df149765..3df30c9c 100644 --- a/lib/odbc_adapter/schema_statements.rb +++ b/lib/odbc_adapter/schema_statements.rb @@ -76,6 +76,8 @@ def columns(table_name, _name = nil) args = { sql_type: col_sql_type, type: col_sql_type, limit: col_limit } args[:sql_type] = 'boolean' if col_native_type == self.class::BOOLEAN_TYPE + args[:sql_type] = 'json' if col_native_type == self.class::VARIANT_TYPE || col_native_type == self.class::JSON_TYPE + args[:sql_type] = 'date' if col_native_type == self.class::DATE_TYPE if [ODBC::SQL_DECIMAL, ODBC::SQL_NUMERIC].include?(col_sql_type) args[:scale] = col_scale || 0 From fb7aa4a781a34df3122c9d7af939a7728157f699 Mon Sep 17 00:00:00 2001 From: Shehbaz Date: Thu, 29 Oct 2020 08:25:43 +0000 Subject: [PATCH 09/26] Typo fix --- lib/active_record/connection_adapters/odbc_adapter.rb | 2 +- lib/odbc_adapter/adapters/null_odbc_adapter.rb | 2 +- lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/active_record/connection_adapters/odbc_adapter.rb b/lib/active_record/connection_adapters/odbc_adapter.rb index b7242022..e4887723 100644 --- a/lib/active_record/connection_adapters/odbc_adapter.rb +++ b/lib/active_record/connection_adapters/odbc_adapter.rb @@ -76,7 +76,7 @@ class ODBCAdapter < AbstractAdapter BOOLEAN_TYPE = 'BOOLEAN'.freeze VARIANT_TYPE = 'VARIANT'.freeze DATE_TYPE = 'DATE'.freeze - JSON_TYPE = 'json'.freeze + JSON_TYPE = 'JSON'.freeze ERR_DUPLICATE_KEY_VALUE = 23_505 ERR_QUERY_TIMED_OUT = 57_014 diff --git a/lib/odbc_adapter/adapters/null_odbc_adapter.rb b/lib/odbc_adapter/adapters/null_odbc_adapter.rb index 70a51e01..c78e991f 100644 --- a/lib/odbc_adapter/adapters/null_odbc_adapter.rb +++ b/lib/odbc_adapter/adapters/null_odbc_adapter.rb @@ -6,7 +6,7 @@ module Adapters class NullODBCAdapter < ActiveRecord::ConnectionAdapters::ODBCAdapter VARIANT_TYPE = 'VARIANT'.freeze DATE_TYPE = 'DATE'.freeze - JSON_TYPE = 'json'.freeze + JSON_TYPE = 'JSON'.freeze # Using a BindVisitor so that the SQL string gets substituted before it is # sent to the DBMS (to attempt to get as much coverage as possible for # DBMSs we don't support). diff --git a/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb b/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb index 81cd4f27..05fa17e0 100644 --- a/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb +++ b/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb @@ -7,14 +7,13 @@ class PostgreSQLODBCAdapter < ActiveRecord::ConnectionAdapters::ODBCAdapter PRIMARY_KEY = 'SERIAL PRIMARY KEY'.freeze VARIANT_TYPE = 'VARIANT'.freeze DATE_TYPE = 'DATE'.freeze - JSON_TYPE = 'json'.freeze + JSON_TYPE = 'JSON'.freeze alias create insert # Override to handle booleans appropriately def native_database_types @native_database_types ||= super.merge(boolean: { name: 'bool' }) - @native_database_types ||= super.merge(json: { name: 'json' }) end def arel_visitor From c74949a83d220dee4304611021da2123bedee29f Mon Sep 17 00:00:00 2001 From: Matt Smith Date: Wed, 4 Aug 2021 13:52:17 -0600 Subject: [PATCH 10/26] Minor updates to the README --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index 0f5b5519..1b6945d9 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,6 @@ # ODBCAdapter -[![Build Status](https://travis-ci.org/localytics/odbc_adapter.svg?branch=master)](https://travis-ci.org/localytics/odbc_adapter) -[![Gem](https://img.shields.io/gem/v/odbc_adapter.svg)](https://rubygems.org/gems/odbc_adapter) - -An ActiveRecord ODBC adapter. Master branch is working off of Rails 5.0.1. Previous work has been done to make it compatible with Rails 3.2 and 4.2; for those versions use the 3.2.x or 4.2.x gem releases. +An ActiveRecord ODBC adapter. Pattern has made some updates to get this working with Rails 6 as forked from repo was not actively being maintained to do so. This adapter will work for basic queries for most DBMSs out of the box, without support for migrations. Full support is built-in for MySQL 5 and PostgreSQL 9 databases. You can register your own adapter to get more support for your DBMS using the `ODBCAdapter.register` function. From d8f7c9501679c3e66cdecd4e7da9e8b0723ab70e Mon Sep 17 00:00:00 2001 From: Satish Date: Thu, 8 Dec 2022 16:51:46 +0530 Subject: [PATCH 11/26] Updating for keyword arguments as per Ruby 3 --- lib/active_record/connection_adapters/odbc_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/odbc_adapter.rb b/lib/active_record/connection_adapters/odbc_adapter.rb index e4887723..fe20d616 100644 --- a/lib/active_record/connection_adapters/odbc_adapter.rb +++ b/lib/active_record/connection_adapters/odbc_adapter.rb @@ -182,7 +182,7 @@ def initialize_type_map(map) # Translate an exception from the native DBMS to something usable by # ActiveRecord. - def translate_exception(exception, message) + def translate_exception(exception, **message) error_number = exception.message[/^\d+/].to_i if error_number == ERR_DUPLICATE_KEY_VALUE From e1ff7229575e0fb10af154dad092186e0a2f32da Mon Sep 17 00:00:00 2001 From: Ajit Konde Date: Tue, 14 Feb 2023 00:50:40 +0530 Subject: [PATCH 12/26] disbaling the call for disable_referential_integrity --- lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb b/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb index 05fa17e0..9bcc8953 100644 --- a/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb +++ b/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb @@ -67,10 +67,11 @@ def quote_string(string) end def disable_referential_integrity - execute(tables.map { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(';')) - yield - ensure - execute(tables.map { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(';')) + #execute(tables.map { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(';')) + #yield + #ensure + #execute(tables.map { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(';')) + [] end # Create a new PostgreSQL database. Options include :owner, From e9c43b948fa6ca5203a364ae50c6b5fe11794a6e Mon Sep 17 00:00:00 2001 From: Ajit Konde Date: Tue, 14 Feb 2023 01:17:29 +0530 Subject: [PATCH 13/26] Replaced fetch_all with fetch --- lib/odbc_adapter/schema_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/odbc_adapter/schema_statements.rb b/lib/odbc_adapter/schema_statements.rb index 3df30c9c..54deea9d 100644 --- a/lib/odbc_adapter/schema_statements.rb +++ b/lib/odbc_adapter/schema_statements.rb @@ -11,7 +11,7 @@ def native_database_types # current connection. def tables(_name = nil) stmt = @connection.tables - result = stmt.fetch_all || [] + result = stmt.fetch || [] stmt.drop result.each_with_object([]) do |row, table_names| From 5ece8fe8943e3675d03760b7f15a563d4a1713c3 Mon Sep 17 00:00:00 2001 From: Vishal Zambre Date: Thu, 16 Mar 2023 12:50:33 +0530 Subject: [PATCH 14/26] Fixed boolean conversion to int bug --- lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb b/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb index 9bcc8953..8596510a 100644 --- a/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb +++ b/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb @@ -3,7 +3,7 @@ module Adapters # Overrides specific to PostgreSQL. Mostly taken from # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter class PostgreSQLODBCAdapter < ActiveRecord::ConnectionAdapters::ODBCAdapter - BOOLEAN_TYPE = 'bool'.freeze + BOOLEAN_TYPE = 'BOOLEAN'.freeze PRIMARY_KEY = 'SERIAL PRIMARY KEY'.freeze VARIANT_TYPE = 'VARIANT'.freeze DATE_TYPE = 'DATE'.freeze From 4ef32d73e325e372fcc2f741dfca6c7be02aa528 Mon Sep 17 00:00:00 2001 From: Vishal Zambre Date: Thu, 16 Mar 2023 22:30:06 +0530 Subject: [PATCH 15/26] Use native data type to map rails data types --- .../connection_adapters/odbc_adapter.rb | 52 +++++++------------ .../adapters/postgresql_odbc_adapter.rb | 4 -- lib/odbc_adapter/schema_statements.rb | 5 +- 3 files changed, 19 insertions(+), 42 deletions(-) diff --git a/lib/active_record/connection_adapters/odbc_adapter.rb b/lib/active_record/connection_adapters/odbc_adapter.rb index fe20d616..e4695396 100644 --- a/lib/active_record/connection_adapters/odbc_adapter.rb +++ b/lib/active_record/connection_adapters/odbc_adapter.rb @@ -73,10 +73,7 @@ class ODBCAdapter < AbstractAdapter include ::ODBCAdapter::SchemaStatements ADAPTER_NAME = 'ODBC'.freeze - BOOLEAN_TYPE = 'BOOLEAN'.freeze VARIANT_TYPE = 'VARIANT'.freeze - DATE_TYPE = 'DATE'.freeze - JSON_TYPE = 'JSON'.freeze ERR_DUPLICATE_KEY_VALUE = 23_505 ERR_QUERY_TIMED_OUT = 57_014 @@ -143,41 +140,28 @@ def new_column(name, default, sql_type_metadata, null, table_name, native_type = ::ODBCAdapter::Column.new(name, default, sql_type_metadata, null, table_name, native_type) end + def clear_cache! # :nodoc: + reload_type_map + super + end + protected # Build the type map for ActiveRecord # Here, ODBC and ODBC_UTF8 constants are interchangeable - def initialize_type_map(map) - map.register_type 'boolean', Type::Boolean.new - map.register_type 'json', Type::Json.new - map.register_type ODBC::SQL_CHAR, Type::String.new - map.register_type ODBC::SQL_LONGVARCHAR, Type::Text.new - map.register_type ODBC::SQL_TINYINT, Type::Integer.new(limit: 4) - map.register_type ODBC::SQL_SMALLINT, Type::Integer.new(limit: 8) - map.register_type ODBC::SQL_INTEGER, Type::Integer.new(limit: 16) - map.register_type ODBC::SQL_BIGINT, Type::BigInteger.new(limit: 32) - map.register_type ODBC::SQL_REAL, Type::Float.new(limit: 24) - map.register_type ODBC::SQL_FLOAT, Type::Float.new - map.register_type ODBC::SQL_DOUBLE, Type::Float.new(limit: 53) - map.register_type ODBC::SQL_DECIMAL, Type::Float.new - map.register_type ODBC::SQL_NUMERIC, Type::Integer.new - map.register_type ODBC::SQL_BINARY, Type::Binary.new - map.register_type ODBC::SQL_DATE, Type::Date.new - map.register_type ODBC::SQL_DATETIME, Type::DateTime.new - map.register_type ODBC::SQL_TIME, Type::Time.new - map.register_type ODBC::SQL_TIMESTAMP, Type::DateTime.new - map.register_type ODBC::SQL_GUID, Type::String.new - - alias_type map, ODBC::SQL_BIT, 'boolean' - alias_type map, ODBC::SQL_VARCHAR, ODBC::SQL_CHAR - alias_type map, ODBC::SQL_WCHAR, ODBC::SQL_CHAR - alias_type map, ODBC::SQL_WVARCHAR, ODBC::SQL_CHAR - alias_type map, ODBC::SQL_WLONGVARCHAR, ODBC::SQL_LONGVARCHAR - alias_type map, ODBC::SQL_VARBINARY, ODBC::SQL_BINARY - alias_type map, ODBC::SQL_LONGVARBINARY, ODBC::SQL_BINARY - alias_type map, ODBC::SQL_TYPE_DATE, ODBC::SQL_DATE - alias_type map, ODBC::SQL_TYPE_TIME, ODBC::SQL_TIME - alias_type map, ODBC::SQL_TYPE_TIMESTAMP, ODBC::SQL_TIMESTAMP + def initialize_type_map(m = type_map) + super + + m.register_type %r(bigint)i, Type::BigInteger.new + m.alias_type "float4", "float" + m.alias_type "float8", "float" + m.alias_type "double", "decimal" + m.alias_type "number", "decimal" + m.alias_type "numeric", "decimal" + m.alias_type "real", "decimal" + m.alias_type "string", "char" + m.alias_type "bool", "boolean" + m.alias_type "varbinary", "binary" end # Translate an exception from the native DBMS to something usable by diff --git a/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb b/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb index 8596510a..1e6d53f2 100644 --- a/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb +++ b/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb @@ -3,11 +3,7 @@ module Adapters # Overrides specific to PostgreSQL. Mostly taken from # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter class PostgreSQLODBCAdapter < ActiveRecord::ConnectionAdapters::ODBCAdapter - BOOLEAN_TYPE = 'BOOLEAN'.freeze PRIMARY_KEY = 'SERIAL PRIMARY KEY'.freeze - VARIANT_TYPE = 'VARIANT'.freeze - DATE_TYPE = 'DATE'.freeze - JSON_TYPE = 'JSON'.freeze alias create insert diff --git a/lib/odbc_adapter/schema_statements.rb b/lib/odbc_adapter/schema_statements.rb index 54deea9d..13fcd6b0 100644 --- a/lib/odbc_adapter/schema_statements.rb +++ b/lib/odbc_adapter/schema_statements.rb @@ -74,10 +74,7 @@ def columns(table_name, _name = nil) # SQLColumns: IS_NULLABLE, SQLColumns: NULLABLE col_nullable = nullability(col_name, col[17], col[10]) - args = { sql_type: col_sql_type, type: col_sql_type, limit: col_limit } - args[:sql_type] = 'boolean' if col_native_type == self.class::BOOLEAN_TYPE - args[:sql_type] = 'json' if col_native_type == self.class::VARIANT_TYPE || col_native_type == self.class::JSON_TYPE - args[:sql_type] = 'date' if col_native_type == self.class::DATE_TYPE + args = { sql_type: col_native_type.downcase, type: col_sql_type, limit: col_limit } if [ODBC::SQL_DECIMAL, ODBC::SQL_NUMERIC].include?(col_sql_type) args[:scale] = col_scale || 0 From 869c5f2db39fd6def540047634e0b915eef62c8b Mon Sep 17 00:00:00 2001 From: Vishal Zambre Date: Thu, 16 Mar 2023 22:32:10 +0530 Subject: [PATCH 16/26] Upgrade rails dependency --- odbc_adapter.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/odbc_adapter.gemspec b/odbc_adapter.gemspec index 0eafa155..53eb0b78 100644 --- a/odbc_adapter.gemspec +++ b/odbc_adapter.gemspec @@ -19,7 +19,7 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ['lib'] - spec.add_dependency 'activerecord', '>= 5.0.1' + spec.add_dependency 'activerecord', '>= 6.1.7' spec.add_dependency 'ruby-odbc', '~> 0.9' spec.add_development_dependency 'bundler', '>= 1.14' From d9ef63a138b0cd73aaae4e260f354e22b7b5e472 Mon Sep 17 00:00:00 2001 From: Vishal Zambre Date: Fri, 17 Mar 2023 10:58:03 +0530 Subject: [PATCH 17/26] Rubocop updated --- .rubocop.yml | 35 +++-------- .rubocop_todo.yml | 137 +++++++++++++++++++++++++++++++++++++++++++ odbc_adapter.gemspec | 11 ++-- 3 files changed, 152 insertions(+), 31 deletions(-) create mode 100644 .rubocop_todo.yml diff --git a/.rubocop.yml b/.rubocop.yml index 28d2dcd7..154f84f0 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,35 +1,16 @@ +require: + - rubocop-rake + - rubocop-minitest + +inherit_from: .rubocop_todo.yml + AllCops: + NewCops: enable DisplayCopNames: true DisplayStyleGuide: true TargetRubyVersion: 2.2 Exclude: - 'vendor/**/*' -Lint/AmbiguousBlockAssociation: +Minitest/MultipleAssertions: Enabled: false - -Metrics/AbcSize: - Enabled: false - -Metrics/ClassLength: - Enabled: false - -Metrics/CyclomaticComplexity: - Enabled: false - -Metrics/MethodLength: - Enabled: false - -Metrics/LineLength: - Enabled: false - -Metrics/PerceivedComplexity: - Enabled: false - -Style/Documentation: - Enabled: false - -Style/PercentLiteralDelimiters: - PreferredDelimiters: - default: '[]' - '%r': '{}' diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml new file mode 100644 index 00000000..edb065af --- /dev/null +++ b/.rubocop_todo.yml @@ -0,0 +1,137 @@ +# This configuration was generated by +# `rubocop --auto-gen-config` +# on 2023-03-17 05:25:22 UTC using RuboCop version 1.48.1. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Offense count: 1 +# Configuration parameters: Severity, Include. +# Include: **/*.gemspec +Gemspec/RequiredRubyVersion: + Exclude: + - 'odbc_adapter.gemspec' + +# Offense count: 3 +# Configuration parameters: MaximumRangeSize. +Lint/MissingCopEnableDirective: + Exclude: + - 'lib/active_record/connection_adapters/odbc_adapter.rb' + - 'lib/odbc_adapter/column.rb' + - 'test/registry_test.rb' + +# Offense count: 3 +# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. +Metrics/AbcSize: + Max: 33 + +# Offense count: 1 +# Configuration parameters: CountComments, CountAsOne. +Metrics/ClassLength: + Max: 115 + +# Offense count: 1 +# Configuration parameters: AllowedMethods, AllowedPatterns. +Metrics/CyclomaticComplexity: + Max: 9 + +# Offense count: 7 +# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. +Metrics/MethodLength: + Max: 19 + +# Offense count: 2 +# Configuration parameters: Max, CountKeywordArgs. +Metrics/ParameterLists: + MaxOptionalParameters: 4 + +# Offense count: 1 +# Configuration parameters: AllowedMethods, AllowedPatterns. +Metrics/PerceivedComplexity: + Max: 9 + +# Offense count: 5 +# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames. +# AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to +Naming/MethodParameterName: + Exclude: + - 'lib/active_record/connection_adapters/odbc_adapter.rb' + - 'lib/odbc_adapter/adapters/mysql_odbc_adapter.rb' + - 'lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb' + +# Offense count: 10 +# Configuration parameters: AllowedConstants. +Style/Documentation: + Exclude: + - 'spec/**/*' + - 'test/**/*' + - 'lib/active_record/connection_adapters/odbc_adapter.rb' + - 'lib/odbc_adapter/column.rb' + - 'lib/odbc_adapter/column_metadata.rb' + - 'lib/odbc_adapter/database_limits.rb' + - 'lib/odbc_adapter/database_statements.rb' + - 'lib/odbc_adapter/quoting.rb' + - 'lib/odbc_adapter/registry.rb' + - 'lib/odbc_adapter/schema_statements.rb' + +# Offense count: 30 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: always, always_true, never +Style/FrozenStringLiteralComment: + Enabled: false + +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: InverseMethods, InverseBlocks. +Style/InverseMethods: + Exclude: + - 'lib/odbc_adapter/quoting.rb' + +# Offense count: 2 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: literals, strict +Style/MutableConstant: + Exclude: + - 'lib/active_record/connection_adapters/odbc_adapter.rb' + +# Offense count: 2 +# Configuration parameters: AllowedMethods. +# AllowedMethods: respond_to_missing? +Style/OptionalBooleanParameter: + Exclude: + - 'lib/odbc_adapter/column.rb' + - 'lib/odbc_adapter/database_metadata.rb' + +# Offense count: 6 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength. +# AllowedMethods: present?, blank?, presence, try, try! +Style/SafeNavigation: + Exclude: + - 'lib/active_record/connection_adapters/odbc_adapter.rb' + - 'lib/odbc_adapter/column_metadata.rb' + - 'lib/odbc_adapter/schema_statements.rb' + +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: forbid_for_all_comparison_operators, forbid_for_equality_operators_only, require_for_all_comparison_operators, require_for_equality_operators_only +Style/YodaCondition: + Exclude: + - 'lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb' + +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +Style/ZeroLengthPredicate: + Exclude: + - 'lib/odbc_adapter/quoting.rb' + +# Offense count: 9 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns. +# URISchemes: http, https +Layout/LineLength: + Max: 206 diff --git a/odbc_adapter.gemspec b/odbc_adapter.gemspec index 53eb0b78..5eba9580 100644 --- a/odbc_adapter.gemspec +++ b/odbc_adapter.gemspec @@ -1,4 +1,4 @@ -lib = File.expand_path('../lib', __FILE__) +lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'odbc_adapter/version' @@ -24,8 +24,11 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'bundler', '>= 1.14' spec.add_development_dependency 'minitest', '~> 5.10' - spec.add_development_dependency 'rake', '~> 12.0' - spec.add_development_dependency 'rubocop', '0.48.1' - spec.add_development_dependency 'simplecov', '~> 0.14' spec.add_development_dependency 'pry', '~> 0.11.1' + spec.add_development_dependency 'rake', '~> 13.0' + spec.add_development_dependency 'rubocop', '1.48.1' + spec.add_development_dependency 'rubocop-minitest', '0.29.0' + spec.add_development_dependency 'rubocop-rake', '0.6.0' + spec.add_development_dependency 'simplecov', '~> 0.14' + spec.metadata['rubygems_mfa_required'] = 'true' end From c4c2c6d2c8d4d5d9b58635126318be1e4b3afa72 Mon Sep 17 00:00:00 2001 From: Vishal Zambre Date: Fri, 17 Mar 2023 10:58:35 +0530 Subject: [PATCH 18/26] Added additional datatypes --- .../connection_adapters/odbc_adapter.rb | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/lib/active_record/connection_adapters/odbc_adapter.rb b/lib/active_record/connection_adapters/odbc_adapter.rb index e4695396..241022dc 100644 --- a/lib/active_record/connection_adapters/odbc_adapter.rb +++ b/lib/active_record/connection_adapters/odbc_adapter.rb @@ -152,16 +152,21 @@ def clear_cache! # :nodoc: def initialize_type_map(m = type_map) super - m.register_type %r(bigint)i, Type::BigInteger.new - m.alias_type "float4", "float" - m.alias_type "float8", "float" - m.alias_type "double", "decimal" - m.alias_type "number", "decimal" - m.alias_type "numeric", "decimal" - m.alias_type "real", "decimal" - m.alias_type "string", "char" - m.alias_type "bool", "boolean" - m.alias_type "varbinary", "binary" + m.register_type(/bigint/i, Type::BigInteger.new) + m.alias_type 'float4', 'float' + m.alias_type 'float8', 'float' + m.alias_type 'double', 'decimal' + m.alias_type 'number', 'decimal' + m.alias_type 'numeric', 'decimal' + m.alias_type 'real', 'decimal' + m.alias_type 'string', 'char' + m.alias_type 'bool', 'boolean' + m.alias_type 'varbinary', 'binary' + m.alias_type 'variant', 'json' + m.alias_type 'object', 'json' + m.alias_type 'array', 'json' + m.alias_type 'geography', 'char' + m.alias_type 'geometry', 'char' end # Translate an exception from the native DBMS to something usable by From 421b23c0fe4d4378f00e895482f02c71d508abe3 Mon Sep 17 00:00:00 2001 From: Vishal Zambre Date: Fri, 17 Mar 2023 11:16:05 +0530 Subject: [PATCH 19/26] Rubocop corrected --- .rubocop.yml | 14 ++- .rubocop_todo.yml | 107 +----------------- Gemfile | 2 + Rakefile | 2 + bin/console | 1 + .../connection_adapters/odbc_adapter.rb | 24 ++-- lib/odbc_adapter.rb | 2 + .../adapters/mysql_odbc_adapter.rb | 17 ++- .../adapters/null_odbc_adapter.rb | 8 +- .../adapters/postgresql_odbc_adapter.rb | 22 ++-- lib/odbc_adapter/column.rb | 5 +- lib/odbc_adapter/column_metadata.rb | 28 +++-- lib/odbc_adapter/database_limits.rb | 2 + lib/odbc_adapter/database_metadata.rb | 8 +- lib/odbc_adapter/database_statements.rb | 2 + lib/odbc_adapter/error.rb | 3 + lib/odbc_adapter/quoting.rb | 15 ++- lib/odbc_adapter/registry.rb | 7 +- lib/odbc_adapter/schema_statements.rb | 25 ++-- lib/odbc_adapter/version.rb | 4 +- odbc_adapter.gemspec | 3 + test/attributes_test.rb | 6 +- test/calculations_test.rb | 4 +- test/connection_fail_test.rb | 4 +- test/connection_management_test.rb | 13 ++- test/crud_test.rb | 4 + test/metadata_test.rb | 3 + test/migrations_test.rb | 7 ++ test/registry_test.rb | 6 +- test/selection_test.rb | 5 + test/test_helper.rb | 4 +- test/version_test.rb | 2 + 32 files changed, 175 insertions(+), 184 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 154f84f0..bb8a8d65 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -8,9 +8,21 @@ AllCops: NewCops: enable DisplayCopNames: true DisplayStyleGuide: true - TargetRubyVersion: 2.2 + TargetRubyVersion: 2.7 Exclude: - 'vendor/**/*' Minitest/MultipleAssertions: Enabled: false + +Metrics/MethodLength: + Enabled: false + +Style/Documentation: + Enabled: false + +Metrics/ClassLength: + Enabled: false + +Layout/LineLength: + Max: 170 diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index edb065af..ff62286e 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,46 +1,21 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2023-03-17 05:25:22 UTC using RuboCop version 1.48.1. +# on 2023-03-17 05:40:41 UTC using RuboCop version 1.48.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# Offense count: 1 -# Configuration parameters: Severity, Include. -# Include: **/*.gemspec -Gemspec/RequiredRubyVersion: - Exclude: - - 'odbc_adapter.gemspec' - -# Offense count: 3 -# Configuration parameters: MaximumRangeSize. -Lint/MissingCopEnableDirective: - Exclude: - - 'lib/active_record/connection_adapters/odbc_adapter.rb' - - 'lib/odbc_adapter/column.rb' - - 'test/registry_test.rb' - -# Offense count: 3 +# Offense count: 2 # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. Metrics/AbcSize: - Max: 33 - -# Offense count: 1 -# Configuration parameters: CountComments, CountAsOne. -Metrics/ClassLength: - Max: 115 + Max: 32 # Offense count: 1 # Configuration parameters: AllowedMethods, AllowedPatterns. Metrics/CyclomaticComplexity: Max: 9 -# Offense count: 7 -# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. -Metrics/MethodLength: - Max: 19 - # Offense count: 2 # Configuration parameters: Max, CountKeywordArgs. Metrics/ParameterLists: @@ -59,79 +34,3 @@ Naming/MethodParameterName: - 'lib/active_record/connection_adapters/odbc_adapter.rb' - 'lib/odbc_adapter/adapters/mysql_odbc_adapter.rb' - 'lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb' - -# Offense count: 10 -# Configuration parameters: AllowedConstants. -Style/Documentation: - Exclude: - - 'spec/**/*' - - 'test/**/*' - - 'lib/active_record/connection_adapters/odbc_adapter.rb' - - 'lib/odbc_adapter/column.rb' - - 'lib/odbc_adapter/column_metadata.rb' - - 'lib/odbc_adapter/database_limits.rb' - - 'lib/odbc_adapter/database_statements.rb' - - 'lib/odbc_adapter/quoting.rb' - - 'lib/odbc_adapter/registry.rb' - - 'lib/odbc_adapter/schema_statements.rb' - -# Offense count: 30 -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: always, always_true, never -Style/FrozenStringLiteralComment: - Enabled: false - -# Offense count: 1 -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: InverseMethods, InverseBlocks. -Style/InverseMethods: - Exclude: - - 'lib/odbc_adapter/quoting.rb' - -# Offense count: 2 -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: literals, strict -Style/MutableConstant: - Exclude: - - 'lib/active_record/connection_adapters/odbc_adapter.rb' - -# Offense count: 2 -# Configuration parameters: AllowedMethods. -# AllowedMethods: respond_to_missing? -Style/OptionalBooleanParameter: - Exclude: - - 'lib/odbc_adapter/column.rb' - - 'lib/odbc_adapter/database_metadata.rb' - -# Offense count: 6 -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength. -# AllowedMethods: present?, blank?, presence, try, try! -Style/SafeNavigation: - Exclude: - - 'lib/active_record/connection_adapters/odbc_adapter.rb' - - 'lib/odbc_adapter/column_metadata.rb' - - 'lib/odbc_adapter/schema_statements.rb' - -# Offense count: 1 -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: forbid_for_all_comparison_operators, forbid_for_equality_operators_only, require_for_all_comparison_operators, require_for_equality_operators_only -Style/YodaCondition: - Exclude: - - 'lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb' - -# Offense count: 1 -# This cop supports unsafe autocorrection (--autocorrect-all). -Style/ZeroLengthPredicate: - Exclude: - - 'lib/odbc_adapter/quoting.rb' - -# Offense count: 9 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns. -# URISchemes: http, https -Layout/LineLength: - Max: 206 diff --git a/Gemfile b/Gemfile index fa75df15..7f4f5e95 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,5 @@ +# frozen_string_literal: true + source 'https://rubygems.org' gemspec diff --git a/Rakefile b/Rakefile index 6af9c2b8..7a327cb5 100644 --- a/Rakefile +++ b/Rakefile @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'bundler/gem_tasks' require 'rake/testtask' require 'rubocop/rake_task' diff --git a/bin/console b/bin/console index 7853a78b..39ac58ab 100755 --- a/bin/console +++ b/bin/console @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true require 'bundler/setup' require 'odbc_adapter' diff --git a/lib/active_record/connection_adapters/odbc_adapter.rb b/lib/active_record/connection_adapters/odbc_adapter.rb index 241022dc..3c1b59dc 100644 --- a/lib/active_record/connection_adapters/odbc_adapter.rb +++ b/lib/active_record/connection_adapters/odbc_adapter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'active_record' require 'odbc' require 'odbc_utf8' @@ -38,8 +40,8 @@ def odbc_connection(config) # Connect using a predefined DSN. def odbc_dsn_connection(config) - username = config[:username] ? config[:username].to_s : nil - password = config[:password] ? config[:password].to_s : nil + username = config[:username]&.to_s + password = config[:password]&.to_s odbc_module = config[:encoding] == 'utf8' ? ODBC_UTF8 : ODBC connection = odbc_module.connect(config[:dsn], username, password) @@ -52,7 +54,7 @@ def odbc_dsn_connection(config) # e.g. "DSN=virt5;UID=rails;PWD=rails" # "DRIVER={OpenLink Virtuoso};HOST=carlmbp;UID=rails;PWD=rails" def odbc_conn_str_connection(config) - attrs = config[:conn_str].split(';').map { |option| option.split('=', 2) }.to_h + attrs = config[:conn_str].split(';').to_h { |option| option.split('=', 2) } odbc_module = attrs['ENCODING'] == 'utf8' ? ODBC_UTF8 : ODBC driver = odbc_module::Driver.new driver.name = 'odbc' @@ -60,7 +62,8 @@ def odbc_conn_str_connection(config) connection = odbc_module::Database.new.drvconnect(driver) # encoding_bug indicates that the driver is using non ASCII and has the issue referenced here https://github.com/larskanis/ruby-odbc/issues/2 - [connection, config.merge(driver: driver, encoding: attrs['ENCODING'], encoding_bug: attrs['ENCODING'] == 'utf8')] + [connection, + config.merge(driver: driver, encoding: attrs['ENCODING'], encoding_bug: attrs['ENCODING'] == 'utf8')] end end end @@ -72,14 +75,14 @@ class ODBCAdapter < AbstractAdapter include ::ODBCAdapter::Quoting include ::ODBCAdapter::SchemaStatements - ADAPTER_NAME = 'ODBC'.freeze - VARIANT_TYPE = 'VARIANT'.freeze + ADAPTER_NAME = 'ODBC' + VARIANT_TYPE = 'VARIANT' ERR_DUPLICATE_KEY_VALUE = 23_505 ERR_QUERY_TIMED_OUT = 57_014 - ERR_QUERY_TIMED_OUT_MESSAGE = /Query has timed out/ - ERR_CONNECTION_FAILED_REGEX = '^08[0S]0[12347]'.freeze - ERR_CONNECTION_FAILED_MESSAGE = /Client connection failed/ + ERR_QUERY_TIMED_OUT_MESSAGE = /Query has timed out/.freeze + ERR_CONNECTION_FAILED_REGEX = '^08[0S]0[12347]' + ERR_CONNECTION_FAILED_MESSAGE = /Client connection failed/.freeze # The object that stores the information that is fetched from the DBMS # when a connection is first established. @@ -139,6 +142,7 @@ def disconnect! def new_column(name, default, sql_type_metadata, null, table_name, native_type = nil) ::ODBCAdapter::Column.new(name, default, sql_type_metadata, null, table_name, native_type) end + # rubocop:enable Metrics/ParameterLists def clear_cache! # :nodoc: reload_type_map @@ -182,7 +186,7 @@ def translate_exception(exception, **message) begin reconnect! ::ODBCAdapter::ConnectionFailedError.new(message, exception) - rescue => e + rescue StandardError => e puts "unable to reconnect #{e}" end else diff --git a/lib/odbc_adapter.rb b/lib/odbc_adapter.rb index 194fb562..2d2a16cc 100644 --- a/lib/odbc_adapter.rb +++ b/lib/odbc_adapter.rb @@ -1,2 +1,4 @@ +# frozen_string_literal: true + # Requiring with this pattern to mirror ActiveRecord require 'active_record/connection_adapters/odbc_adapter' diff --git a/lib/odbc_adapter/adapters/mysql_odbc_adapter.rb b/lib/odbc_adapter/adapters/mysql_odbc_adapter.rb index 0d439462..c4a4a2fe 100644 --- a/lib/odbc_adapter/adapters/mysql_odbc_adapter.rb +++ b/lib/odbc_adapter/adapters/mysql_odbc_adapter.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + module ODBCAdapter module Adapters # Overrides specific to MySQL. Mostly taken from # ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter class MySQLODBCAdapter < ActiveRecord::ConnectionAdapters::ODBCAdapter - PRIMARY_KEY = 'INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY'.freeze + PRIMARY_KEY = 'INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY' def arel_visitor Arel::Visitors::MySQL.new(self) @@ -90,11 +92,10 @@ def rename_table(name, new_name) end def change_column(table_name, column_name, type, options = {}) - unless options_include_default?(options) - options[:default] = column_for(table_name, column_name).default - end + options[:default] = column_for(table_name, column_name).default unless options_include_default?(options) - change_column_sql = "ALTER TABLE #{table_name} CHANGE #{column_name} #{column_name} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" + change_column_sql = "ALTER TABLE #{table_name} CHANGE #{column_name} #{column_name} #{type_to_sql(type, + options[:limit], options[:precision], options[:scale])}" add_column_options!(change_column_sql, options) execute(change_column_sql) end @@ -129,11 +130,7 @@ def indexes(table_name, name = nil) # MySQL 5.x doesn't allow DEFAULT NULL for first timestamp column in a # table def options_include_default?(options) - if options.include?(:default) && options[:default].nil? - if options.include?(:column) && options[:column].native_type =~ /timestamp/i - options.delete(:default) - end - end + options.delete(:default) if options.include?(:default) && options[:default].nil? && (options.include?(:column) && options[:column].native_type =~ /timestamp/i) super(options) end diff --git a/lib/odbc_adapter/adapters/null_odbc_adapter.rb b/lib/odbc_adapter/adapters/null_odbc_adapter.rb index c78e991f..9ddeef71 100644 --- a/lib/odbc_adapter/adapters/null_odbc_adapter.rb +++ b/lib/odbc_adapter/adapters/null_odbc_adapter.rb @@ -1,12 +1,14 @@ +# frozen_string_literal: true + module ODBCAdapter module Adapters # A default adapter used for databases that are no explicitly listed in the # registry. This allows for minimal support for DBMSs for which we don't # have an explicit adapter. class NullODBCAdapter < ActiveRecord::ConnectionAdapters::ODBCAdapter - VARIANT_TYPE = 'VARIANT'.freeze - DATE_TYPE = 'DATE'.freeze - JSON_TYPE = 'JSON'.freeze + VARIANT_TYPE = 'VARIANT' + DATE_TYPE = 'DATE' + JSON_TYPE = 'JSON' # Using a BindVisitor so that the SQL string gets substituted before it is # sent to the DBMS (to attempt to get as much coverage as possible for # DBMSs we don't support). diff --git a/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb b/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb index 1e6d53f2..e9758884 100644 --- a/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb +++ b/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + module ODBCAdapter module Adapters # Overrides specific to PostgreSQL. Mostly taken from # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter class PostgreSQLODBCAdapter < ActiveRecord::ConnectionAdapters::ODBCAdapter - PRIMARY_KEY = 'SERIAL PRIMARY KEY'.freeze + PRIMARY_KEY = 'SERIAL PRIMARY KEY' alias create insert @@ -49,7 +51,8 @@ def type_cast(value, column) case value when String - return super unless 'bytea' == column.native_type + return super unless column.native_type == 'bytea' + { value: value, format: 1 } else super @@ -63,10 +66,10 @@ def quote_string(string) end def disable_referential_integrity - #execute(tables.map { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(';')) - #yield - #ensure - #execute(tables.map { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(';')) + # execute(tables.map { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(';')) + # yield + # ensure + # execute(tables.map { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(';')) [] end @@ -115,7 +118,8 @@ def rename_table(name, new_name) end def change_column(table_name, column_name, type, options = {}) - execute("ALTER TABLE #{table_name} ALTER #{column_name} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}") + execute("ALTER TABLE #{table_name} ALTER #{column_name} TYPE #{type_to_sql(type, options[:limit], + options[:precision], options[:scale])}") change_column_default(table_name, column_name, options[:default]) if options_include_default?(options) end @@ -180,9 +184,9 @@ def last_insert_id(sequence_name) private def serial_sequence(table, column) - result = exec_query(<<-eosql, 'SCHEMA') + result = exec_query(<<-EOSQL, 'SCHEMA') SELECT pg_get_serial_sequence('#{table}', '#{column}') - eosql + EOSQL result.rows.first.first end end diff --git a/lib/odbc_adapter/column.rb b/lib/odbc_adapter/column.rb index 93044fea..3093eb6f 100644 --- a/lib/odbc_adapter/column.rb +++ b/lib/odbc_adapter/column.rb @@ -1,13 +1,16 @@ +# frozen_string_literal: true + module ODBCAdapter class Column < ActiveRecord::ConnectionAdapters::Column attr_reader :native_type # Add the native_type accessor to allow the native DBMS to report back what # it uses to represent the column internally. - # rubocop:disable Metrics/ParameterLists + # rubocop:disable Metrics/ParameterLists, Style/OptionalBooleanParameter def initialize(name, default, sql_type_metadata = nil, null = true, table_name = nil, native_type = nil) super(name, default, sql_type_metadata, null, table_name) @native_type = native_type end + # rubocop:enable Metrics/ParameterLists, Style/OptionalBooleanParameter end end diff --git a/lib/odbc_adapter/column_metadata.rb b/lib/odbc_adapter/column_metadata.rb index 8ef89ac4..920efa7b 100644 --- a/lib/odbc_adapter/column_metadata.rb +++ b/lib/odbc_adapter/column_metadata.rb @@ -1,18 +1,20 @@ +# frozen_string_literal: true + module ODBCAdapter class ColumnMetadata GENERICS = { primary_key: [ODBC::SQL_INTEGER, ODBC::SQL_SMALLINT], - string: [ODBC::SQL_VARCHAR], - text: [ODBC::SQL_LONGVARCHAR, ODBC::SQL_VARCHAR], - integer: [ODBC::SQL_INTEGER, ODBC::SQL_SMALLINT], - decimal: [ODBC::SQL_NUMERIC, ODBC::SQL_DECIMAL], - float: [ODBC::SQL_DOUBLE, ODBC::SQL_REAL], - datetime: [ODBC::SQL_TYPE_TIMESTAMP, ODBC::SQL_TIMESTAMP], - timestamp: [ODBC::SQL_TYPE_TIMESTAMP, ODBC::SQL_TIMESTAMP], - time: [ODBC::SQL_TYPE_TIME, ODBC::SQL_TIME, ODBC::SQL_TYPE_TIMESTAMP, ODBC::SQL_TIMESTAMP], - date: [ODBC::SQL_TYPE_DATE, ODBC::SQL_DATE, ODBC::SQL_TYPE_TIMESTAMP, ODBC::SQL_TIMESTAMP], - binary: [ODBC::SQL_LONGVARBINARY, ODBC::SQL_VARBINARY], - boolean: [ODBC::SQL_BIT, ODBC::SQL_TINYINT, ODBC::SQL_SMALLINT, ODBC::SQL_INTEGER] + string: [ODBC::SQL_VARCHAR], + text: [ODBC::SQL_LONGVARCHAR, ODBC::SQL_VARCHAR], + integer: [ODBC::SQL_INTEGER, ODBC::SQL_SMALLINT], + decimal: [ODBC::SQL_NUMERIC, ODBC::SQL_DECIMAL], + float: [ODBC::SQL_DOUBLE, ODBC::SQL_REAL], + datetime: [ODBC::SQL_TYPE_TIMESTAMP, ODBC::SQL_TIMESTAMP], + timestamp: [ODBC::SQL_TYPE_TIMESTAMP, ODBC::SQL_TIMESTAMP], + time: [ODBC::SQL_TYPE_TIME, ODBC::SQL_TIME, ODBC::SQL_TYPE_TIMESTAMP, ODBC::SQL_TIMESTAMP], + date: [ODBC::SQL_TYPE_DATE, ODBC::SQL_DATE, ODBC::SQL_TYPE_TIMESTAMP, ODBC::SQL_TIMESTAMP], + binary: [ODBC::SQL_LONGVARBINARY, ODBC::SQL_VARBINARY], + boolean: [ODBC::SQL_BIT, ODBC::SQL_TINYINT, ODBC::SQL_SMALLINT, ODBC::SQL_INTEGER] }.freeze attr_reader :adapter @@ -27,6 +29,7 @@ def native_database_types GENERICS.each_with_object({}) do |(abstract, candidates), mapped| candidates.detect do |candidate| next unless grouped[candidate] + mapped[abstract] = native_type_mapping(abstract, grouped[candidate]) end end @@ -41,6 +44,7 @@ def native_type_mapping(abstract, rows) # ODBC doesn't provide any info on a DBMS's native syntax for # autoincrement columns. So we use a lookup instead. return adapter.class::PRIMARY_KEY if abstract == :primary_key + selected_row = rows[0] # If more than one native type corresponds to the SQL type we're @@ -69,7 +73,7 @@ def reported_types stmt = adapter.raw_connection.types stmt.fetch_all ensure - stmt.drop unless stmt.nil? + stmt&.drop end end end diff --git a/lib/odbc_adapter/database_limits.rb b/lib/odbc_adapter/database_limits.rb index bd6bf032..96aa108d 100644 --- a/lib/odbc_adapter/database_limits.rb +++ b/lib/odbc_adapter/database_limits.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ODBCAdapter module DatabaseLimits # Returns the maximum length of a table name. diff --git a/lib/odbc_adapter/database_metadata.rb b/lib/odbc_adapter/database_metadata.rb index 11fa9255..5c2814f4 100644 --- a/lib/odbc_adapter/database_metadata.rb +++ b/lib/odbc_adapter/database_metadata.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ODBCAdapter # Caches SQLGetInfo output class DatabaseMetadata @@ -17,14 +19,16 @@ class DatabaseMetadata # has_encoding_bug refers to https://github.com/larskanis/ruby-odbc/issues/2 where ruby-odbc in UTF8 mode # returns incorrectly encoded responses to getInfo + # rubocop:disable Style/OptionalBooleanParameter def initialize(connection, has_encoding_bug = false) - @values = Hash[FIELDS.map do |field| + @values = FIELDS.to_h do |field| info = connection.get_info(ODBC.const_get(field)) info = info.encode(Encoding.default_external, 'UTF-16LE') if info.is_a?(String) && has_encoding_bug [field, info] - end] + end end + # rubocop:enable Style/OptionalBooleanParameter def adapter_class ODBCAdapter.adapter_for(dbms_name) diff --git a/lib/odbc_adapter/database_statements.rb b/lib/odbc_adapter/database_statements.rb index 859fc43a..f8656352 100644 --- a/lib/odbc_adapter/database_statements.rb +++ b/lib/odbc_adapter/database_statements.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ODBCAdapter module DatabaseStatements # ODBC constants missing from Christian Werner's Ruby ODBC driver diff --git a/lib/odbc_adapter/error.rb b/lib/odbc_adapter/error.rb index d0e0172b..6cd0eea4 100644 --- a/lib/odbc_adapter/error.rb +++ b/lib/odbc_adapter/error.rb @@ -1,6 +1,9 @@ +# frozen_string_literal: true + module ODBCAdapter class QueryTimeoutError < ActiveRecord::StatementInvalid end + class ConnectionFailedError < ActiveRecord::StatementInvalid end end diff --git a/lib/odbc_adapter/quoting.rb b/lib/odbc_adapter/quoting.rb index a499612e..fcdd056c 100644 --- a/lib/odbc_adapter/quoting.rb +++ b/lib/odbc_adapter/quoting.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + module ODBCAdapter module Quoting # Quotes a string, escaping any ' (single quote) characters. def quote_string(string) - string.gsub(/\'/, "''") + string.gsub(/'/, "''") end # Returns a quoted form of the column name. @@ -10,16 +12,15 @@ def quote_column_name(name) name = name.to_s quote_char = database_metadata.identifier_quote_char.to_s.strip - return name if quote_char.length.zero? + return name if quote_char.empty? + quote_char = quote_char[0] # Avoid quoting any already quoted name return name if name[0] == quote_char && name[-1] == quote_char # If upcase identifiers, only quote mixed case names. - if database_metadata.upcase_identifiers? - return name unless name =~ /([A-Z]+[a-z])|([a-z]+[A-Z])/ - end + return name if database_metadata.upcase_identifiers? && name !~ /([A-Z]+[a-z])|([a-z]+[A-Z])/ "#{quote_char.chr}#{name}#{quote_char.chr}" end @@ -30,9 +31,7 @@ def quoted_date(value) if value.acts_like?(:time) zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal - if value.respond_to?(zone_conversion_method) - value = value.send(zone_conversion_method) - end + value = value.send(zone_conversion_method) if value.respond_to?(zone_conversion_method) value.strftime('%Y-%m-%d %H:%M:%S') # Time, DateTime else value.strftime('%Y-%m-%d') # Date diff --git a/lib/odbc_adapter/registry.rb b/lib/odbc_adapter/registry.rb index 1bb7264e..78f0a18f 100644 --- a/lib/odbc_adapter/registry.rb +++ b/lib/odbc_adapter/registry.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + module ODBCAdapter class Registry attr_reader :dbs def initialize @dbs = { - /my.*sql/i => :MySQL, + /my.*sql/i => :MySQL, /postgres/i => :PostgreSQL } end @@ -16,7 +18,7 @@ def adapter_for(reported_name) adapter if reported_name =~ pattern end - normalize_adapter(found && found.last || :Null) + normalize_adapter((found && found.last) || :Null) end def register(pattern, superclass = Object, &block) @@ -27,6 +29,7 @@ def register(pattern, superclass = Object, &block) def normalize_adapter(adapter) return adapter unless adapter.is_a?(Symbol) + require "odbc_adapter/adapters/#{adapter.downcase}_odbc_adapter" Adapters.const_get(:"#{adapter}ODBCAdapter") end diff --git a/lib/odbc_adapter/schema_statements.rb b/lib/odbc_adapter/schema_statements.rb index 13fcd6b0..10a02d56 100644 --- a/lib/odbc_adapter/schema_statements.rb +++ b/lib/odbc_adapter/schema_statements.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ODBCAdapter module SchemaStatements # Returns a Hash of mappings from the abstract data types to the native @@ -17,6 +19,7 @@ def tables(_name = nil) result.each_with_object([]) do |row, table_names| schema_name, table_name, table_type = row[1..3] next if respond_to?(:table_filtered?) && table_filtered?(schema_name, table_type) + table_names << format_case(table_name) end end @@ -30,7 +33,7 @@ def views def indexes(table_name, _name = nil) stmt = @connection.indexes(native_case(table_name.to_s)) result = stmt.fetch_all || [] - stmt.drop unless stmt.nil? + stmt&.drop index_cols = [] index_name = nil @@ -51,7 +54,8 @@ def indexes(table_name, _name = nil) next_row = result[row_idx + 1] if (row_idx == result.length - 1) || (next_row[6].zero? || next_row[7] == 1) - indices << ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, format_case(index_name), unique, index_cols) + indices << ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, format_case(index_name), unique, + index_cols) end end end @@ -82,7 +86,8 @@ def columns(table_name, _name = nil) end sql_type_metadata = ActiveRecord::ConnectionAdapters::SqlTypeMetadata.new(**args) - cols << new_column(format_case(col_name), col_default, sql_type_metadata, col_nullable, table_name, col_native_type) + cols << new_column(format_case(col_name), col_default, sql_type_metadata, col_nullable, table_name, + col_native_type) end end @@ -90,14 +95,14 @@ def columns(table_name, _name = nil) def primary_key(table_name) stmt = @connection.primary_keys(native_case(table_name.to_s)) result = stmt.fetch_all || [] - stmt.drop unless stmt.nil? + stmt&.drop result[0] && result[0][3] end def foreign_keys(table_name) stmt = @connection.foreign_keys(native_case(table_name.to_s)) result = stmt.fetch_all || [] - stmt.drop unless stmt.nil? + stmt&.drop result.map do |key| fk_from_table = key[2] # PKTABLE_NAME @@ -106,11 +111,11 @@ def foreign_keys(table_name) ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new( fk_from_table, fk_to_table, - name: key[11], # FK_NAME - column: key[3], # PKCOLUMN_NAME - primary_key: key[7], # FKCOLUMN_NAME - on_delete: key[10], # DELETE_RULE - on_update: key[9] # UPDATE_RULE + name: key[11], # FK_NAME + column: key[3], # PKCOLUMN_NAME + primary_key: key[7], # FKCOLUMN_NAME + on_delete: key[10], # DELETE_RULE + on_update: key[9] # UPDATE_RULE ) end end diff --git a/lib/odbc_adapter/version.rb b/lib/odbc_adapter/version.rb index 598c96ec..59470555 100644 --- a/lib/odbc_adapter/version.rb +++ b/lib/odbc_adapter/version.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ODBCAdapter - VERSION = '5.0.6'.freeze + VERSION = '5.0.6' end diff --git a/odbc_adapter.gemspec b/odbc_adapter.gemspec index 5eba9580..d78c9455 100644 --- a/odbc_adapter.gemspec +++ b/odbc_adapter.gemspec @@ -1,3 +1,5 @@ +# frozen_string_literal: true + lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'odbc_adapter/version' @@ -21,6 +23,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'activerecord', '>= 6.1.7' spec.add_dependency 'ruby-odbc', '~> 0.9' + spec.required_ruby_version = '~> 2.7' spec.add_development_dependency 'bundler', '>= 1.14' spec.add_development_dependency 'minitest', '~> 5.10' diff --git a/test/attributes_test.rb b/test/attributes_test.rb index 8a0cbf43..0291d397 100644 --- a/test/attributes_test.rb +++ b/test/attributes_test.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + require 'test_helper' class AttributesTest < Minitest::Test def test_booleans? - assert_equal true, Todo.first.published? - assert_equal false, Todo.last.published? + assert_predicate Todo.first, :published? + refute_predicate Todo.last, :published? end def test_integers diff --git a/test/calculations_test.rb b/test/calculations_test.rb index 627b4bd1..3f37c15a 100644 --- a/test/calculations_test.rb +++ b/test/calculations_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class CalculationsTest < Minitest::Test @@ -8,6 +10,6 @@ def test_count end def test_average - assert_equal 10.33, User.average(:letters).round(2) + assert_in_delta(10.33, User.average(:letters).round(2)) end end diff --git a/test/connection_fail_test.rb b/test/connection_fail_test.rb index 8061af5e..fc6f2e15 100644 --- a/test/connection_fail_test.rb +++ b/test/connection_fail_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class ConnectionFailTest < Minitest::Test @@ -7,7 +9,7 @@ def test_connection_fail skip 'Only executed for MySQL' unless ActiveRecord::Base.connection.instance_values['config'][:conn_str].include? 'MySQL' begin conn.execute('KILL CONNECTION_ID();') - rescue => e + rescue StandardError => e puts "caught exception #{e}" end assert_raises(ODBCAdapter::ConnectionFailedError) { User.average(:letters).round(2) } diff --git a/test/connection_management_test.rb b/test/connection_management_test.rb index 57009a5a..b4f43af6 100644 --- a/test/connection_management_test.rb +++ b/test/connection_management_test.rb @@ -1,17 +1,22 @@ +# frozen_string_literal: true + require 'test_helper' class ConnectionManagementTest < Minitest::Test def test_connection_management - assert conn.active? + assert_predicate conn, :active? conn.disconnect! - refute conn.active? + + refute_predicate conn, :active? conn.disconnect! - refute conn.active? + + refute_predicate conn, :active? conn.reconnect! - assert conn.active? + + assert_predicate conn, :active? ensure conn.reconnect! end diff --git a/test/crud_test.rb b/test/crud_test.rb index 39665a35..4e9be1ab 100644 --- a/test/crud_test.rb +++ b/test/crud_test.rb @@ -1,9 +1,12 @@ +# frozen_string_literal: true + require 'test_helper' class CRUDTest < Minitest::Test def test_creation with_transaction do User.create(first_name: 'foo', last_name: 'bar') + assert_equal 7, User.count end end @@ -21,6 +24,7 @@ def test_update def test_destroy with_transaction do User.last.destroy + assert_equal 5, User.count end end diff --git a/test/metadata_test.rb b/test/metadata_test.rb index eaa75091..24ffc6a9 100644 --- a/test/metadata_test.rb +++ b/test/metadata_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class MetadataTest < Minitest::Test @@ -7,6 +9,7 @@ def test_data_sources def test_column_names expected = %w[created_at first_name id last_name letters updated_at] + assert_equal expected, User.column_names.sort end diff --git a/test/migrations_test.rb b/test/migrations_test.rb index 76e3fbce..1a609c47 100644 --- a/test/migrations_test.rb +++ b/test/migrations_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class MigrationsTest < Minitest::Test @@ -9,9 +11,11 @@ def test_table_crud @connection.create_table(:foos, force: true) do |t| t.timestamps null: false end + assert_equal 3, @connection.columns(:foos).count @connection.rename_table(:foos, :bars) + assert_equal 3, @connection.columns(:bars).count @connection.drop_table(:bars) @@ -21,12 +25,15 @@ def test_column_crud previous_count = @connection.columns(:users).count @connection.add_column(:users, :foo, :integer) + assert_equal previous_count + 1, @connection.columns(:users).count @connection.rename_column(:users, :foo, :bar) + assert_equal previous_count + 1, @connection.columns(:users).count @connection.remove_column(:users, :bar) + assert_equal previous_count, @connection.columns(:users).count end end diff --git a/test/registry_test.rb b/test/registry_test.rb index eb1afe2c..f1b73c7f 100644 --- a/test/registry_test.rb +++ b/test/registry_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class RegistryTest < Minitest::Test @@ -6,6 +8,7 @@ def test_register register_foobar(registry) adapter = registry.adapter_for('Foo Bar') + assert_kind_of Class, adapter assert_equal ODBCAdapter::Adapters::MySQLODBCAdapter, adapter.superclass assert_equal 'foobar', adapter.new.quoted_true @@ -17,11 +20,10 @@ def test_register def register_foobar(registry) require File.join('odbc_adapter', 'adapters', 'mysql_odbc_adapter') registry.register(/foobar/, ODBCAdapter::Adapters::MySQLODBCAdapter) do - def initialize() end - def quoted_true 'foobar' end end end + # rubocop:enable Lint/NestedMethodDefinition end diff --git a/test/selection_test.rb b/test/selection_test.rb index 667dbbf6..41df3226 100644 --- a/test/selection_test.rb +++ b/test/selection_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class SelectionTest < Minitest::Test @@ -7,16 +9,19 @@ def test_first def test_pluck expected = %w[Ash Jason Kevin Michal Ryan Sharif] + assert_equal expected, User.order(:first_name).pluck(:first_name) end def test_limitations expected = %w[Kevin Michal Ryan] + assert_equal expected, User.order(:first_name).limit(3).offset(2).pluck(:first_name) end def test_find user = User.last + assert_equal user, User.find(user.id) end diff --git a/test/test_helper.rb b/test/test_helper.rb index 623b1960..5171e7a2 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,7 +1,9 @@ +# frozen_string_literal: true + require 'simplecov' SimpleCov.start -$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) +$LOAD_PATH.unshift File.expand_path('../lib', __dir__) require 'odbc_adapter' require 'minitest/autorun' diff --git a/test/version_test.rb b/test/version_test.rb index 232a7c6f..e1cc6f99 100644 --- a/test/version_test.rb +++ b/test/version_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'test_helper' class VersionTest < Minitest::Test From 560c67ebbde4696c94db644030a4d7347d22ae05 Mon Sep 17 00:00:00 2001 From: Vishal Zambre Date: Fri, 17 Mar 2023 11:19:19 +0530 Subject: [PATCH 20/26] Rubocop corrected --- odbc_adapter.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/odbc_adapter.gemspec b/odbc_adapter.gemspec index d78c9455..ab326002 100644 --- a/odbc_adapter.gemspec +++ b/odbc_adapter.gemspec @@ -23,7 +23,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'activerecord', '>= 6.1.7' spec.add_dependency 'ruby-odbc', '~> 0.9' - spec.required_ruby_version = '~> 2.7' + spec.required_ruby_version = '>= 2.7' spec.add_development_dependency 'bundler', '>= 1.14' spec.add_development_dependency 'minitest', '~> 5.10' From 46061645d9a7e83728eb0d72ffbbeedf37138dbf Mon Sep 17 00:00:00 2001 From: mattbrown-msb Date: Mon, 20 Mar 2023 09:12:00 -0600 Subject: [PATCH 21/26] Undo changes to fetch_all and disable_referential_integrity --- lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb | 9 ++++----- lib/odbc_adapter/schema_statements.rb | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb b/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb index e9758884..f7f85ff5 100644 --- a/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb +++ b/lib/odbc_adapter/adapters/postgresql_odbc_adapter.rb @@ -66,11 +66,10 @@ def quote_string(string) end def disable_referential_integrity - # execute(tables.map { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(';')) - # yield - # ensure - # execute(tables.map { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(';')) - [] + execute(tables.map { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(';')) + yield + ensure + execute(tables.map { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(';')) end # Create a new PostgreSQL database. Options include :owner, diff --git a/lib/odbc_adapter/schema_statements.rb b/lib/odbc_adapter/schema_statements.rb index 10a02d56..da187418 100644 --- a/lib/odbc_adapter/schema_statements.rb +++ b/lib/odbc_adapter/schema_statements.rb @@ -13,7 +13,7 @@ def native_database_types # current connection. def tables(_name = nil) stmt = @connection.tables - result = stmt.fetch || [] + result = stmt.fetch_all || [] stmt.drop result.each_with_object([]) do |row, table_names| From 91786549c60122d70bc02dad6ae5259cc99b7fbf Mon Sep 17 00:00:00 2001 From: Vishal Zambre Date: Tue, 21 Mar 2023 13:12:44 +0530 Subject: [PATCH 22/26] Datatypes corrected --- lib/active_record/connection_adapters/odbc_adapter.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/odbc_adapter.rb b/lib/active_record/connection_adapters/odbc_adapter.rb index 3c1b59dc..f495f6e0 100644 --- a/lib/active_record/connection_adapters/odbc_adapter.rb +++ b/lib/active_record/connection_adapters/odbc_adapter.rb @@ -159,10 +159,10 @@ def initialize_type_map(m = type_map) m.register_type(/bigint/i, Type::BigInteger.new) m.alias_type 'float4', 'float' m.alias_type 'float8', 'float' - m.alias_type 'double', 'decimal' + m.alias_type 'double', 'float' m.alias_type 'number', 'decimal' m.alias_type 'numeric', 'decimal' - m.alias_type 'real', 'decimal' + m.alias_type 'real', 'float' m.alias_type 'string', 'char' m.alias_type 'bool', 'boolean' m.alias_type 'varbinary', 'binary' From 52df6df823c8691c46e7c4431a32f1e768c2563a Mon Sep 17 00:00:00 2001 From: Vishal Zambre Date: Tue, 21 Mar 2023 13:48:09 +0530 Subject: [PATCH 23/26] Readme --- README.md | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/README.md b/README.md index 1b6945d9..36895050 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,93 @@ docker run -it --rm -v $(pwd):/workspace -v /tmp/mysql:/var/lib/mysql odbc-dev:l docker/test.sh ``` +## Datatype References + + +Reference https://docs.snowflake.com/en/sql-reference/intro-summary-data-types + +https://github.com/rails/rails/blob/main/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb#L877-L908 + +``` +["number", + "decimal", + "numeric", + "int", + "integer", + "bigint", + "smallint", + "tinyint", + "byteint", + "float", + "float4", + "float8", + "double", + "real", + "varchar", + "char", + "character", + "string", + "text", + "binary", + "varbinary", + "boolean", + "date", + "datetime", + "time", + "timestamp", + "timestamp_ltz", + "timestamp_ntz", + "timestamp_tz", + "variant", + "object", + "array", + "geography", + "geometry"] +``` + + +Possible mapping from chatgpt + +| Snowflake Data Type | Ruby ActiveRecord Type | PostgreSQL Type | +| --------------------- | ---------------------- | ---------------- | +| number | :decimal | numeric +| decimal | :decimal | numeric +| numeric | :decimal | numeric +| int | :integer | integer +| integer | :integer | integer +| bigint | :bigint | bigint +| smallint | :integer | smallint +| tinyint | :integer | smallint +| byteint | :integer | smallint +| float | :float | double precision +| float4 | :float | real +| float8 | :float | double precision +| double | :float | double precision +| real | :float | real +| varchar | :string | character varying +| char | :string | character +| character | :string | character +| string | :string | character varying +| text | :text | text +| binary | :binary | bytea +| varbinary | :binary | bytea +| boolean | :boolean | boolean +| date | :date | date +| datetime | :datetime | timestamp without time zone +| time | :time | time without time zone +| timestamp | :timestamp | timestamp without time zone +| timestamp_ltz | :timestamp | timestamp without time zone +| timestamp_ntz | :timestamp | timestamp without time zone +| timestamp_tz | :timestamp | timestamp with time zone +| variant | :jsonb | jsonb +| object | :jsonb | jsonb +| array | :jsonb | jsonb +| geography | :st_point, :st_polygon,| geography +| | :st_multipolygon | +| geometry | :st_point, :st_polygon,| geometry +| | :st_multipolygon | + + ## Contributing Bug reports and pull requests are welcome on GitHub at https://github.com/localytics/odbc_adapter. From 1306c5ccf68d7a69a9a975b987360681b712157a Mon Sep 17 00:00:00 2001 From: mattbrown-msb Date: Tue, 21 Mar 2023 09:35:41 -0600 Subject: [PATCH 24/26] Updated mapping of number field to float. --- lib/active_record/connection_adapters/odbc_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/odbc_adapter.rb b/lib/active_record/connection_adapters/odbc_adapter.rb index f495f6e0..b8b3af65 100644 --- a/lib/active_record/connection_adapters/odbc_adapter.rb +++ b/lib/active_record/connection_adapters/odbc_adapter.rb @@ -160,7 +160,7 @@ def initialize_type_map(m = type_map) m.alias_type 'float4', 'float' m.alias_type 'float8', 'float' m.alias_type 'double', 'float' - m.alias_type 'number', 'decimal' + m.alias_type 'number', 'float' m.alias_type 'numeric', 'decimal' m.alias_type 'real', 'float' m.alias_type 'string', 'char' From 7f7509530226e5722ac4672ac29b75d0e3b58eb3 Mon Sep 17 00:00:00 2001 From: mattbrown-msb Date: Tue, 21 Mar 2023 15:05:35 -0600 Subject: [PATCH 25/26] Data type changes. --- lib/active_record/connection_adapters/odbc_adapter.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/active_record/connection_adapters/odbc_adapter.rb b/lib/active_record/connection_adapters/odbc_adapter.rb index b8b3af65..95935d70 100644 --- a/lib/active_record/connection_adapters/odbc_adapter.rb +++ b/lib/active_record/connection_adapters/odbc_adapter.rb @@ -160,17 +160,20 @@ def initialize_type_map(m = type_map) m.alias_type 'float4', 'float' m.alias_type 'float8', 'float' m.alias_type 'double', 'float' - m.alias_type 'number', 'float' + m.alias_type 'number', 'decimal' m.alias_type 'numeric', 'decimal' m.alias_type 'real', 'float' m.alias_type 'string', 'char' m.alias_type 'bool', 'boolean' m.alias_type 'varbinary', 'binary' m.alias_type 'variant', 'json' - m.alias_type 'object', 'json' - m.alias_type 'array', 'json' + m.alias_type 'object', 'string' + m.alias_type 'array', 'string' m.alias_type 'geography', 'char' m.alias_type 'geometry', 'char' + + # number() data types in Snowflake are interpreted as decimal and must be mapped back to a float + m.alias_type 'decimal', 'float' if @database_metadata&.dbms_name&.downcase == "snowflake" end # Translate an exception from the native DBMS to something usable by From 8b44387a64285d3cc457633368ee56dc2c7900df Mon Sep 17 00:00:00 2001 From: mattbrown-msb Date: Tue, 21 Mar 2023 22:14:00 -0600 Subject: [PATCH 26/26] Removed condition. --- lib/active_record/connection_adapters/odbc_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/odbc_adapter.rb b/lib/active_record/connection_adapters/odbc_adapter.rb index 95935d70..9dac72e2 100644 --- a/lib/active_record/connection_adapters/odbc_adapter.rb +++ b/lib/active_record/connection_adapters/odbc_adapter.rb @@ -173,7 +173,7 @@ def initialize_type_map(m = type_map) m.alias_type 'geometry', 'char' # number() data types in Snowflake are interpreted as decimal and must be mapped back to a float - m.alias_type 'decimal', 'float' if @database_metadata&.dbms_name&.downcase == "snowflake" + m.alias_type 'decimal', 'float' end # Translate an exception from the native DBMS to something usable by