activerecord-jdbc-alt-adapter 70.2.0-java → 71.0.0.alpha2-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +141 -24
- data/.github/workflows/ruby.yml +12 -12
- data/.gitignore +7 -3
- data/.solargraph.yml +15 -0
- data/Gemfile +17 -4
- data/README.md +7 -3
- data/RUNNING_TESTS.md +36 -0
- data/activerecord-jdbc-adapter.gemspec +2 -2
- data/activerecord-jdbc-alt-adapter.gemspec +1 -1
- data/lib/arjdbc/abstract/connection_management.rb +26 -10
- data/lib/arjdbc/abstract/core.rb +5 -12
- data/lib/arjdbc/abstract/database_statements.rb +35 -25
- data/lib/arjdbc/abstract/statement_cache.rb +2 -7
- data/lib/arjdbc/abstract/transaction_support.rb +37 -22
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/jdbc/column.rb +0 -34
- data/lib/arjdbc/jdbc/connection_methods.rb +1 -1
- data/lib/arjdbc/mssql/adapter.rb +101 -79
- data/lib/arjdbc/mssql/column.rb +1 -0
- data/lib/arjdbc/mssql/connection_methods.rb +7 -55
- data/lib/arjdbc/mssql/database_statements.rb +182 -71
- data/lib/arjdbc/mssql/explain_support.rb +8 -5
- data/lib/arjdbc/mssql/schema_creation.rb +1 -1
- data/lib/arjdbc/mssql/schema_definitions.rb +10 -0
- data/lib/arjdbc/mssql/schema_statements.rb +25 -14
- data/lib/arjdbc/mssql/server_version.rb +56 -0
- data/lib/arjdbc/mssql/utils.rb +23 -9
- data/lib/arjdbc/mysql/adapter.rb +104 -27
- data/lib/arjdbc/postgresql/adapter.rb +71 -44
- data/lib/arjdbc/postgresql/oid_types.rb +8 -27
- data/lib/arjdbc/postgresql/schema_statements.rb +57 -0
- data/lib/arjdbc/sqlite3/adapter.rb +205 -147
- data/lib/arjdbc/sqlite3/column.rb +103 -0
- data/lib/arjdbc/sqlite3/connection_methods.rb +7 -2
- data/lib/arjdbc/tasks/mssql_database_tasks.rb +9 -5
- data/lib/arjdbc/version.rb +1 -1
- data/rakelib/02-test.rake +1 -1
- data/rakelib/rails.rake +2 -0
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +3 -1
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +11 -0
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +2 -1
- metadata +10 -12
- data/lib/arel/visitors/sql_server/ng42.rb +0 -294
- data/lib/arel/visitors/sql_server.rb +0 -124
- data/lib/arjdbc/mssql/limit_helpers.rb +0 -231
- data/lib/arjdbc/mssql/lock_methods.rb +0 -77
- data/lib/arjdbc/mssql/old_adapter.rb +0 -804
- data/lib/arjdbc/mssql/old_column.rb +0 -200
| @@ -16,6 +16,7 @@ require "active_record/connection_adapters/sqlite3/schema_definitions" | |
| 16 16 | 
             
            require "active_record/connection_adapters/sqlite3/schema_dumper"
         | 
| 17 17 | 
             
            require "active_record/connection_adapters/sqlite3/schema_statements"
         | 
| 18 18 | 
             
            require "active_support/core_ext/class/attribute"
         | 
| 19 | 
            +
            require "arjdbc/sqlite3/column"
         | 
| 19 20 |  | 
| 20 21 | 
             
            module SQLite3
         | 
| 21 22 | 
             
              module Constants
         | 
| @@ -89,20 +90,8 @@ module ArJdbc | |
| 89 90 | 
             
                  end
         | 
| 90 91 | 
             
                end
         | 
| 91 92 |  | 
| 92 | 
            -
                def initialize(connection, logger, connection_options, config)
         | 
| 93 | 
            -
                  @memory_database = config[:database] == ":memory:"
         | 
| 94 | 
            -
                  super(connection, logger, config)
         | 
| 95 | 
            -
                  configure_connection
         | 
| 96 | 
            -
                end
         | 
| 97 | 
            -
             | 
| 98 93 | 
             
                def self.database_exists?(config)
         | 
| 99 | 
            -
                  config  | 
| 100 | 
            -
                  if config[:database] == ":memory:"
         | 
| 101 | 
            -
                    true
         | 
| 102 | 
            -
                  else
         | 
| 103 | 
            -
                    database_file = defined?(Rails.root) ? File.expand_path(config[:database], Rails.root) : config[:database]
         | 
| 104 | 
            -
                    File.exist?(database_file)
         | 
| 105 | 
            -
                  end
         | 
| 94 | 
            +
                  @config[:database] == ":memory:" || File.exist?(@config[:database].to_s)
         | 
| 106 95 | 
             
                end
         | 
| 107 96 |  | 
| 108 97 | 
             
                def supports_ddl_transactions?
         | 
| @@ -153,6 +142,10 @@ module ArJdbc | |
| 153 142 | 
             
                  database_version >= "3.8.3"
         | 
| 154 143 | 
             
                end
         | 
| 155 144 |  | 
| 145 | 
            +
                def supports_insert_returning?
         | 
| 146 | 
            +
                  database_version >= "3.35.0"
         | 
| 147 | 
            +
                end
         | 
| 148 | 
            +
             | 
| 156 149 | 
             
                def supports_insert_on_conflict?
         | 
| 157 150 | 
             
                  database_version >= "3.24.0"
         | 
| 158 151 | 
             
                end
         | 
| @@ -166,19 +159,22 @@ module ArJdbc | |
| 166 159 | 
             
                end
         | 
| 167 160 |  | 
| 168 161 | 
             
                def active?
         | 
| 169 | 
            -
                  !@raw_connection.closed?
         | 
| 162 | 
            +
                  @raw_connection && !@raw_connection.closed?
         | 
| 170 163 | 
             
                end
         | 
| 171 164 |  | 
| 172 | 
            -
                def  | 
| 173 | 
            -
                   | 
| 174 | 
            -
                  connect if @connection.closed?
         | 
| 165 | 
            +
                def return_value_after_insert?(column) # :nodoc:
         | 
| 166 | 
            +
                  column.auto_populated?
         | 
| 175 167 | 
             
                end
         | 
| 176 168 |  | 
| 169 | 
            +
                # MISSING:       alias :reset! :reconnect!
         | 
| 170 | 
            +
             | 
| 177 171 | 
             
                # Disconnects from the database if already connected. Otherwise, this
         | 
| 178 172 | 
             
                # method does nothing.
         | 
| 179 173 | 
             
                def disconnect!
         | 
| 180 174 | 
             
                  super
         | 
| 181 | 
            -
             | 
| 175 | 
            +
             | 
| 176 | 
            +
                  @raw_connection&.close rescue nil
         | 
| 177 | 
            +
                  @raw_connection = nil
         | 
| 182 178 | 
             
                end
         | 
| 183 179 |  | 
| 184 180 | 
             
                def supports_index_sort_order?
         | 
| @@ -191,7 +187,7 @@ module ArJdbc | |
| 191 187 |  | 
| 192 188 | 
             
                # Returns the current database encoding format as a string, eg: 'UTF-8'
         | 
| 193 189 | 
             
                def encoding
         | 
| 194 | 
            -
                   | 
| 190 | 
            +
                  any_raw_connection.encoding.to_s
         | 
| 195 191 | 
             
                end
         | 
| 196 192 |  | 
| 197 193 | 
             
                def supports_explain?
         | 
| @@ -218,8 +214,14 @@ module ArJdbc | |
| 218 214 | 
             
                  end
         | 
| 219 215 | 
             
                end
         | 
| 220 216 |  | 
| 221 | 
            -
                def  | 
| 222 | 
            -
                   | 
| 217 | 
            +
                def check_all_foreign_keys_valid! # :nodoc:
         | 
| 218 | 
            +
                  sql = "PRAGMA foreign_key_check"
         | 
| 219 | 
            +
                  result = execute(sql)
         | 
| 220 | 
            +
             | 
| 221 | 
            +
                  unless result.blank?
         | 
| 222 | 
            +
                    tables = result.map { |row| row["table"] }
         | 
| 223 | 
            +
                    raise ActiveRecord::StatementInvalid.new("Foreign key violations found: #{tables.join(", ")}", sql: sql)
         | 
| 224 | 
            +
                  end
         | 
| 223 225 | 
             
                end
         | 
| 224 226 |  | 
| 225 227 | 
             
                # SCHEMA STATEMENTS ========================================
         | 
| @@ -234,7 +236,7 @@ module ArJdbc | |
| 234 236 |  | 
| 235 237 | 
             
                  index_name = index_name_for_remove(table_name, column_name, options)
         | 
| 236 238 |  | 
| 237 | 
            -
                   | 
| 239 | 
            +
                  internal_exec_query "DROP INDEX #{quote_column_name(index_name)}"
         | 
| 238 240 | 
             
                end
         | 
| 239 241 |  | 
| 240 242 |  | 
| @@ -242,10 +244,11 @@ module ArJdbc | |
| 242 244 | 
             
                #
         | 
| 243 245 | 
             
                # Example:
         | 
| 244 246 | 
             
                #   rename_table('octopuses', 'octopi')
         | 
| 245 | 
            -
                def rename_table(table_name, new_name)
         | 
| 247 | 
            +
                def rename_table(table_name, new_name, **options)
         | 
| 248 | 
            +
                  validate_table_length!(new_name) unless options[:_uses_legacy_table_name]      
         | 
| 246 249 | 
             
                  schema_cache.clear_data_source_cache!(table_name.to_s)
         | 
| 247 250 | 
             
                  schema_cache.clear_data_source_cache!(new_name.to_s)
         | 
| 248 | 
            -
                   | 
| 251 | 
            +
                  internal_exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
         | 
| 249 252 | 
             
                  rename_table_indexes(table_name, new_name)
         | 
| 250 253 | 
             
                end
         | 
| 251 254 |  | 
| @@ -285,8 +288,10 @@ module ArJdbc | |
| 285 288 | 
             
                end
         | 
| 286 289 |  | 
| 287 290 | 
             
                def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
         | 
| 291 | 
            +
                  validate_change_column_null_argument!(null)
         | 
| 292 | 
            +
             | 
| 288 293 | 
             
                  unless null || default.nil?
         | 
| 289 | 
            -
                     | 
| 294 | 
            +
                    internal_exec_query("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
         | 
| 290 295 | 
             
                  end
         | 
| 291 296 | 
             
                  alter_table(table_name) do |definition|
         | 
| 292 297 | 
             
                    definition[column_name].null = null
         | 
| @@ -295,10 +300,7 @@ module ArJdbc | |
| 295 300 |  | 
| 296 301 | 
             
                def change_column(table_name, column_name, type, **options) #:nodoc:
         | 
| 297 302 | 
             
                  alter_table(table_name) do |definition|
         | 
| 298 | 
            -
                    definition | 
| 299 | 
            -
                      self.type = aliased_types(type.to_s, type)
         | 
| 300 | 
            -
                      self.options.merge!(options)
         | 
| 301 | 
            -
                    end
         | 
| 303 | 
            +
                    definition.change_column(column_name, type, **options)
         | 
| 302 304 | 
             
                  end
         | 
| 303 305 | 
             
                end
         | 
| 304 306 |  | 
| @@ -308,20 +310,42 @@ module ArJdbc | |
| 308 310 | 
             
                  rename_column_indexes(table_name, column.name, new_column_name)
         | 
| 309 311 | 
             
                end
         | 
| 310 312 |  | 
| 313 | 
            +
                def add_timestamps(table_name, **options)
         | 
| 314 | 
            +
                  options[:null] = false if options[:null].nil?
         | 
| 315 | 
            +
             | 
| 316 | 
            +
                  if !options.key?(:precision)
         | 
| 317 | 
            +
                    options[:precision] = 6
         | 
| 318 | 
            +
                  end
         | 
| 319 | 
            +
             | 
| 320 | 
            +
                  alter_table(table_name) do |definition|
         | 
| 321 | 
            +
                    definition.column :created_at, :datetime, **options
         | 
| 322 | 
            +
                    definition.column :updated_at, :datetime, **options
         | 
| 323 | 
            +
                  end
         | 
| 324 | 
            +
                end
         | 
| 325 | 
            +
             | 
| 311 326 | 
             
                def add_reference(table_name, ref_name, **options) # :nodoc:
         | 
| 312 327 | 
             
                  super(table_name, ref_name, type: :integer, **options)
         | 
| 313 328 | 
             
                end
         | 
| 314 329 | 
             
                alias :add_belongs_to :add_reference
         | 
| 315 330 |  | 
| 316 331 | 
             
                def foreign_keys(table_name)
         | 
| 317 | 
            -
                   | 
| 318 | 
            -
                  fk_info | 
| 332 | 
            +
                  # SQLite returns 1 row for each column of composite foreign keys.
         | 
| 333 | 
            +
                  fk_info = internal_exec_query("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA")
         | 
| 334 | 
            +
                  grouped_fk = fk_info.group_by { |row| row["id"] }.values.each { |group| group.sort_by! { |row| row["seq"] } }
         | 
| 335 | 
            +
                  grouped_fk.map do |group|
         | 
| 336 | 
            +
                    row = group.first
         | 
| 319 337 | 
             
                    options = {
         | 
| 320 | 
            -
                      column: row["from"],
         | 
| 321 | 
            -
                      primary_key: row["to"],
         | 
| 322 338 | 
             
                      on_delete: extract_foreign_key_action(row["on_delete"]),
         | 
| 323 339 | 
             
                      on_update: extract_foreign_key_action(row["on_update"])
         | 
| 324 340 | 
             
                    }
         | 
| 341 | 
            +
             | 
| 342 | 
            +
                    if group.one?
         | 
| 343 | 
            +
                      options[:column] = row["from"]
         | 
| 344 | 
            +
                      options[:primary_key] = row["to"]
         | 
| 345 | 
            +
                    else
         | 
| 346 | 
            +
                      options[:column] = group.map { |row| row["from"] }
         | 
| 347 | 
            +
                      options[:primary_key] = group.map { |row| row["to"] }
         | 
| 348 | 
            +
                    end
         | 
| 325 349 | 
             
                    # DIFFERENCE: FQN
         | 
| 326 350 | 
             
                    ::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new(table_name, row["table"], options)
         | 
| 327 351 | 
             
                  end
         | 
| @@ -342,6 +366,7 @@ module ArJdbc | |
| 342 366 | 
             
                    end
         | 
| 343 367 | 
             
                  end
         | 
| 344 368 |  | 
| 369 | 
            +
                  sql << " RETURNING #{insert.returning}" if insert.returning
         | 
| 345 370 | 
             
                  sql
         | 
| 346 371 | 
             
                end
         | 
| 347 372 |  | 
| @@ -349,6 +374,10 @@ module ArJdbc | |
| 349 374 | 
             
                  @config.fetch(:flags, 0).anybits?(::SQLite3::Constants::Open::SHAREDCACHE)
         | 
| 350 375 | 
             
                end
         | 
| 351 376 |  | 
| 377 | 
            +
                def use_insert_returning?
         | 
| 378 | 
            +
                  @use_insert_returning
         | 
| 379 | 
            +
                end
         | 
| 380 | 
            +
             | 
| 352 381 | 
             
                def get_database_version # :nodoc:
         | 
| 353 382 | 
             
                  SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)", "SCHEMA"))
         | 
| 354 383 | 
             
                end
         | 
| @@ -359,6 +388,27 @@ module ArJdbc | |
| 359 388 | 
             
                  end
         | 
| 360 389 | 
             
                end
         | 
| 361 390 |  | 
| 391 | 
            +
                # DIFFERENCE: here to 
         | 
| 392 | 
            +
                def new_column_from_field(table_name, field, definitions)
         | 
| 393 | 
            +
                  default = field["dflt_value"]
         | 
| 394 | 
            +
             | 
| 395 | 
            +
                  type_metadata = fetch_type_metadata(field["type"])
         | 
| 396 | 
            +
                  default_value = extract_value_from_default(default)
         | 
| 397 | 
            +
                  default_function = extract_default_function(default_value, default)
         | 
| 398 | 
            +
                  rowid = is_column_the_rowid?(field, definitions)
         | 
| 399 | 
            +
             | 
| 400 | 
            +
                  ActiveRecord::ConnectionAdapters::SQLite3Column.new(
         | 
| 401 | 
            +
                    field["name"],
         | 
| 402 | 
            +
                    default_value,
         | 
| 403 | 
            +
                    type_metadata,
         | 
| 404 | 
            +
                    field["notnull"].to_i == 0,
         | 
| 405 | 
            +
                    default_function,
         | 
| 406 | 
            +
                    collation: field["collation"],
         | 
| 407 | 
            +
                    auto_increment: field["auto_increment"],
         | 
| 408 | 
            +
                    rowid: rowid
         | 
| 409 | 
            +
                  )
         | 
| 410 | 
            +
                end
         | 
| 411 | 
            +
             | 
| 362 412 | 
             
                private
         | 
| 363 413 | 
             
                # See https://www.sqlite.org/limits.html,
         | 
| 364 414 | 
             
                # the default value is 999 when not configured.
         | 
| @@ -367,7 +417,7 @@ module ArJdbc | |
| 367 417 | 
             
                end
         | 
| 368 418 |  | 
| 369 419 | 
             
                def table_structure(table_name)
         | 
| 370 | 
            -
                  structure =  | 
| 420 | 
            +
                  structure = internal_exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
         | 
| 371 421 | 
             
                  raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
         | 
| 372 422 | 
             
                  table_structure_with_collation(table_name, structure)
         | 
| 373 423 | 
             
                end
         | 
| @@ -377,15 +427,18 @@ module ArJdbc | |
| 377 427 | 
             
                  case default
         | 
| 378 428 | 
             
                  when /^null$/i
         | 
| 379 429 | 
             
                    nil
         | 
| 380 | 
            -
             | 
| 381 | 
            -
                  when /^'( | 
| 430 | 
            +
                  # Quoted types
         | 
| 431 | 
            +
                  when /^'([^|]*)'$/m
         | 
| 382 432 | 
             
                    $1.gsub("''", "'")
         | 
| 383 | 
            -
             | 
| 384 | 
            -
                  when /^"( | 
| 433 | 
            +
                  # Quoted types
         | 
| 434 | 
            +
                  when /^"([^|]*)"$/m
         | 
| 385 435 | 
             
                    $1.gsub('""', '"')
         | 
| 386 | 
            -
             | 
| 436 | 
            +
                  # Numeric types
         | 
| 387 437 | 
             
                  when /\A-?\d+(\.\d*)?\z/
         | 
| 388 438 | 
             
                    $&
         | 
| 439 | 
            +
                  # Binary columns
         | 
| 440 | 
            +
                  when /x'(.*)'/
         | 
| 441 | 
            +
                    [ $1 ].pack("H*")
         | 
| 389 442 | 
             
                  else
         | 
| 390 443 | 
             
                    # Anything else is blank or some function
         | 
| 391 444 | 
             
                    # and we can't know the value of that, so return nil.
         | 
| @@ -398,7 +451,7 @@ module ArJdbc | |
| 398 451 | 
             
                end
         | 
| 399 452 |  | 
| 400 453 | 
             
                def has_default_function?(default_value, default)
         | 
| 401 | 
            -
                  !default_value && %r{\w+\(.*\)|CURRENT_TIME|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
         | 
| 454 | 
            +
                  !default_value && %r{\w+\(.*\)|CURRENT_TIME|CURRENT_DATE|CURRENT_TIMESTAMP|\|\|}.match?(default)
         | 
| 402 455 | 
             
                end
         | 
| 403 456 |  | 
| 404 457 | 
             
                # See: https://www.sqlite.org/lang_altertable.html
         | 
| @@ -464,14 +517,24 @@ module ArJdbc | |
| 464 517 | 
             
                      if column.has_default?
         | 
| 465 518 | 
             
                        type = lookup_cast_type_from_column(column)
         | 
| 466 519 | 
             
                        default = type.deserialize(column.default)
         | 
| 520 | 
            +
                        default = -> { column.default_function } if default.nil?
         | 
| 467 521 | 
             
                      end
         | 
| 468 522 |  | 
| 469 | 
            -
                       | 
| 470 | 
            -
                        limit: column.limit, | 
| 471 | 
            -
                        precision: column.precision, | 
| 472 | 
            -
                         | 
| 523 | 
            +
                      column_options = {
         | 
| 524 | 
            +
                        limit: column.limit,
         | 
| 525 | 
            +
                        precision: column.precision,
         | 
| 526 | 
            +
                        scale: column.scale,
         | 
| 527 | 
            +
                        null: column.null,
         | 
| 528 | 
            +
                        collation: column.collation,
         | 
| 473 529 | 
             
                        primary_key: column_name == from_primary_key
         | 
| 474 | 
            -
                       | 
| 530 | 
            +
                      }
         | 
| 531 | 
            +
             | 
| 532 | 
            +
                      unless column.auto_increment?
         | 
| 533 | 
            +
                        column_options[:default] = default
         | 
| 534 | 
            +
                      end
         | 
| 535 | 
            +
             | 
| 536 | 
            +
                      column_type = column.bigint? ? :bigint : column.type
         | 
| 537 | 
            +
                      @definition.column(column_name, column_type, **column_options)
         | 
| 475 538 | 
             
                    end
         | 
| 476 539 |  | 
| 477 540 | 
             
                    yield @definition if block_given?
         | 
| @@ -519,8 +582,8 @@ module ArJdbc | |
| 519 582 | 
             
                  quoted_columns = columns.map { |col| quote_column_name(col) } * ","
         | 
| 520 583 | 
             
                  quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ","
         | 
| 521 584 |  | 
| 522 | 
            -
                   | 
| 523 | 
            -
             | 
| 585 | 
            +
                  internal_exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
         | 
| 586 | 
            +
                                        SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
         | 
| 524 587 | 
             
                end
         | 
| 525 588 |  | 
| 526 589 | 
             
                def translate_exception(exception, message:, sql:, binds:)
         | 
| @@ -530,25 +593,32 @@ module ArJdbc | |
| 530 593 | 
             
                  #   column *column_name* is not unique
         | 
| 531 594 | 
             
                  if exception.message.match?(/(column(s)? .* (is|are) not unique|UNIQUE constraint failed: .*)/i)
         | 
| 532 595 | 
             
                    # DIFFERENCE: FQN
         | 
| 533 | 
            -
                    ::ActiveRecord::RecordNotUnique.new(message, sql: sql, binds: binds)
         | 
| 596 | 
            +
                    ::ActiveRecord::RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
         | 
| 534 597 | 
             
                  elsif exception.message.match?(/(.* may not be NULL|NOT NULL constraint failed: .*)/i)
         | 
| 535 598 | 
             
                    # DIFFERENCE: FQN
         | 
| 536 | 
            -
                    ::ActiveRecord::NotNullViolation.new(message, sql: sql, binds: binds)
         | 
| 599 | 
            +
                    ::ActiveRecord::NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
         | 
| 537 600 | 
             
                  elsif exception.message.match?(/FOREIGN KEY constraint failed/i)
         | 
| 538 601 | 
             
                    # DIFFERENCE: FQN
         | 
| 539 | 
            -
                    ::ActiveRecord::InvalidForeignKey.new(message, sql: sql, binds: binds)
         | 
| 602 | 
            +
                    ::ActiveRecord::InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
         | 
| 540 603 | 
             
                  elsif exception.message.match?(/called on a closed database/i)
         | 
| 541 604 | 
             
                    # DIFFERENCE: FQN
         | 
| 542 | 
            -
                    ::ActiveRecord::ConnectionNotEstablished.new(exception)
         | 
| 605 | 
            +
                    ::ActiveRecord::ConnectionNotEstablished.new(exception, connection_pool: @pool)
         | 
| 606 | 
            +
                  elsif exception.message.match?(/sql error/i)
         | 
| 607 | 
            +
                    ::ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
         | 
| 608 | 
            +
                  elsif exception.message.match?(/write a readonly database/i)
         | 
| 609 | 
            +
                    message = message.sub('org.sqlite.SQLiteException', 'SQLite3::ReadOnlyException')
         | 
| 610 | 
            +
                    ::ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
         | 
| 543 611 | 
             
                  else
         | 
| 544 612 | 
             
                    super
         | 
| 545 613 | 
             
                  end
         | 
| 546 614 | 
             
                end
         | 
| 547 615 |  | 
| 548 616 | 
             
                COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
         | 
| 617 | 
            +
                PRIMARY_KEY_AUTOINCREMENT_REGEX = /.*\"(\w+)\".+PRIMARY KEY AUTOINCREMENT/i
         | 
| 549 618 |  | 
| 550 619 | 
             
                def table_structure_with_collation(table_name, basic_structure)
         | 
| 551 620 | 
             
                  collation_hash = {}
         | 
| 621 | 
            +
                  auto_increments = {}
         | 
| 552 622 | 
             
                  sql = <<~SQL
         | 
| 553 623 | 
             
                    SELECT sql FROM
         | 
| 554 624 | 
             
                      (SELECT * FROM sqlite_master UNION ALL
         | 
| @@ -570,6 +640,7 @@ module ArJdbc | |
| 570 640 | 
             
                      # This regex will match the column name and collation type and will save
         | 
| 571 641 | 
             
                      # the value in $1 and $2 respectively.
         | 
| 572 642 | 
             
                      collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
         | 
| 643 | 
            +
                      auto_increments[$1] = true if PRIMARY_KEY_AUTOINCREMENT_REGEX =~ column_string
         | 
| 573 644 | 
             
                    end
         | 
| 574 645 |  | 
| 575 646 | 
             
                    basic_structure.map do |column|
         | 
| @@ -579,6 +650,10 @@ module ArJdbc | |
| 579 650 | 
             
                        column["collation"] = collation_hash[column_name]
         | 
| 580 651 | 
             
                      end
         | 
| 581 652 |  | 
| 653 | 
            +
                      if auto_increments.has_key?(column_name)
         | 
| 654 | 
            +
                        column["auto_increment"] = true
         | 
| 655 | 
            +
                      end
         | 
| 656 | 
            +
             | 
| 582 657 | 
             
                      column
         | 
| 583 658 | 
             
                    end
         | 
| 584 659 | 
             
                  else
         | 
| @@ -594,99 +669,48 @@ module ArJdbc | |
| 594 669 | 
             
                  StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
         | 
| 595 670 | 
             
                end
         | 
| 596 671 |  | 
| 597 | 
            -
                def connect
         | 
| 598 | 
            -
                  @connection = ::SQLite3::Database.new(
         | 
| 599 | 
            -
                    @config[:database].to_s,
         | 
| 600 | 
            -
                    @config.merge(results_as_hash: true)
         | 
| 601 | 
            -
                  )
         | 
| 602 | 
            -
                end
         | 
| 603 | 
            -
             | 
| 604 672 | 
             
                def configure_connection
         | 
| 605 | 
            -
                   | 
| 606 | 
            -
             | 
| 673 | 
            +
                  if @config[:timeout] && @config[:retries]
         | 
| 674 | 
            +
                    raise ArgumentError, "Cannot specify both timeout and retries arguments"
         | 
| 675 | 
            +
                  elsif @config[:timeout]
         | 
| 676 | 
            +
                    # FIXME: missing from adapter
         | 
| 677 | 
            +
                    # @raw_connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout]))
         | 
| 678 | 
            +
                  elsif @config[:retries]
         | 
| 679 | 
            +
                    retries = self.class.type_cast_config_to_integer(@config[:retries])
         | 
| 680 | 
            +
                    raw_connection.busy_handler do |count|
         | 
| 681 | 
            +
                      count <= retries
         | 
| 682 | 
            +
                    end
         | 
| 683 | 
            +
                  end
         | 
| 607 684 |  | 
| 608 | 
            -
                   | 
| 685 | 
            +
                  # Enforce foreign key constraints
         | 
| 686 | 
            +
                  # https://www.sqlite.org/pragma.html#pragma_foreign_keys
         | 
| 687 | 
            +
                  # https://www.sqlite.org/foreignkeys.html
         | 
| 688 | 
            +
                  raw_execute("PRAGMA foreign_keys = ON", "SCHEMA")
         | 
| 689 | 
            +
                  unless @memory_database
         | 
| 690 | 
            +
                    # Journal mode WAL allows for greater concurrency (many readers + one writer)
         | 
| 691 | 
            +
                    # https://www.sqlite.org/pragma.html#pragma_journal_mode
         | 
| 692 | 
            +
                    raw_execute("PRAGMA journal_mode = WAL", "SCHEMA")
         | 
| 693 | 
            +
                    # Set more relaxed level of database durability
         | 
| 694 | 
            +
                    # 2 = "FULL" (sync on every write), 1 = "NORMAL" (sync every 1000 written pages) and 0 = "NONE"
         | 
| 695 | 
            +
                    # https://www.sqlite.org/pragma.html#pragma_synchronous
         | 
| 696 | 
            +
                    raw_execute("PRAGMA synchronous = NORMAL", "SCHEMA")
         | 
| 697 | 
            +
                    # Set the global memory map so all processes can share some data
         | 
| 698 | 
            +
                    # https://www.sqlite.org/pragma.html#pragma_mmap_size
         | 
| 699 | 
            +
                    # https://www.sqlite.org/mmap.html
         | 
| 700 | 
            +
                    raw_execute("PRAGMA mmap_size = #{128.megabytes}", "SCHEMA")
         | 
| 701 | 
            +
                  end
         | 
| 702 | 
            +
                  # Impose a limit on the WAL file to prevent unlimited growth
         | 
| 703 | 
            +
                  # https://www.sqlite.org/pragma.html#pragma_journal_size_limit
         | 
| 704 | 
            +
                  raw_execute("PRAGMA journal_size_limit = #{64.megabytes}", "SCHEMA")
         | 
| 705 | 
            +
                  # Set the local connection cache to 2000 pages
         | 
| 706 | 
            +
                  # https://www.sqlite.org/pragma.html#pragma_cache_size
         | 
| 707 | 
            +
                  raw_execute("PRAGMA cache_size = 2000", "SCHEMA")
         | 
| 609 708 | 
             
                end
         | 
| 610 | 
            -
             | 
| 611 709 | 
             
              end
         | 
| 612 710 | 
             
              # DIFFERENCE: A registration here is moved down to concrete class so we are not registering part of an adapter.
         | 
| 613 711 | 
             
            end
         | 
| 614 712 |  | 
| 615 713 | 
             
            module ActiveRecord::ConnectionAdapters
         | 
| 616 | 
            -
              class SQLite3Column < JdbcColumn
         | 
| 617 | 
            -
                def initialize(name, *args)
         | 
| 618 | 
            -
                  if Hash === name
         | 
| 619 | 
            -
                    super
         | 
| 620 | 
            -
                  else
         | 
| 621 | 
            -
                    super(nil, name, *args)
         | 
| 622 | 
            -
                  end
         | 
| 623 | 
            -
                end
         | 
| 624 | 
            -
             | 
| 625 | 
            -
                def self.string_to_binary(value)
         | 
| 626 | 
            -
                  value
         | 
| 627 | 
            -
                end
         | 
| 628 | 
            -
             | 
| 629 | 
            -
                def self.binary_to_string(value)
         | 
| 630 | 
            -
                  if value.respond_to?(:encoding) && value.encoding != Encoding::ASCII_8BIT
         | 
| 631 | 
            -
                    value = value.force_encoding(Encoding::ASCII_8BIT)
         | 
| 632 | 
            -
                  end
         | 
| 633 | 
            -
                  value
         | 
| 634 | 
            -
                end
         | 
| 635 | 
            -
             | 
| 636 | 
            -
                # @override {ActiveRecord::ConnectionAdapters::JdbcColumn#init_column}
         | 
| 637 | 
            -
                def init_column(name, default, *args)
         | 
| 638 | 
            -
                  if default =~ /NULL/
         | 
| 639 | 
            -
                    @default = nil
         | 
| 640 | 
            -
                  else
         | 
| 641 | 
            -
                    super
         | 
| 642 | 
            -
                  end
         | 
| 643 | 
            -
                end
         | 
| 644 | 
            -
             | 
| 645 | 
            -
                # @override {ActiveRecord::ConnectionAdapters::JdbcColumn#default_value}
         | 
| 646 | 
            -
                def default_value(value)
         | 
| 647 | 
            -
                  # JDBC returns column default strings with actual single quotes :
         | 
| 648 | 
            -
                  return $1 if value =~ /^'(.*)'$/
         | 
| 649 | 
            -
             | 
| 650 | 
            -
                  value
         | 
| 651 | 
            -
                end
         | 
| 652 | 
            -
             | 
| 653 | 
            -
                # @override {ActiveRecord::ConnectionAdapters::Column#type_cast}
         | 
| 654 | 
            -
                def type_cast(value)
         | 
| 655 | 
            -
                  return nil if value.nil?
         | 
| 656 | 
            -
                  case type
         | 
| 657 | 
            -
                    when :string then value
         | 
| 658 | 
            -
                    when :primary_key
         | 
| 659 | 
            -
                      value.respond_to?(:to_i) ? value.to_i : ( value ? 1 : 0 )
         | 
| 660 | 
            -
                    when :float    then value.to_f
         | 
| 661 | 
            -
                    when :decimal  then self.class.value_to_decimal(value)
         | 
| 662 | 
            -
                    when :boolean  then self.class.value_to_boolean(value)
         | 
| 663 | 
            -
                    else super
         | 
| 664 | 
            -
                  end
         | 
| 665 | 
            -
                end
         | 
| 666 | 
            -
             | 
| 667 | 
            -
                private
         | 
| 668 | 
            -
             | 
| 669 | 
            -
                # @override {ActiveRecord::ConnectionAdapters::Column#extract_limit}
         | 
| 670 | 
            -
                def extract_limit(sql_type)
         | 
| 671 | 
            -
                  return nil if sql_type =~ /^(real)\(\d+/i
         | 
| 672 | 
            -
                  super
         | 
| 673 | 
            -
                end
         | 
| 674 | 
            -
             | 
| 675 | 
            -
                def extract_precision(sql_type)
         | 
| 676 | 
            -
                  case sql_type
         | 
| 677 | 
            -
                    when /^(real)\((\d+)(,\d+)?\)/i then $2.to_i
         | 
| 678 | 
            -
                    else super
         | 
| 679 | 
            -
                  end
         | 
| 680 | 
            -
                end
         | 
| 681 | 
            -
             | 
| 682 | 
            -
                def extract_scale(sql_type)
         | 
| 683 | 
            -
                  case sql_type
         | 
| 684 | 
            -
                    when /^(real)\((\d+)\)/i then 0
         | 
| 685 | 
            -
                    when /^(real)\((\d+)(,(\d+))\)/i then $4.to_i
         | 
| 686 | 
            -
                    else super
         | 
| 687 | 
            -
                  end
         | 
| 688 | 
            -
                end
         | 
| 689 | 
            -
              end
         | 
| 690 714 |  | 
| 691 715 | 
             
              remove_const(:SQLite3Adapter) if const_defined?(:SQLite3Adapter)
         | 
| 692 716 |  | 
| @@ -702,6 +726,30 @@ module ActiveRecord::ConnectionAdapters | |
| 702 726 | 
             
                include ArJdbc::Abstract::StatementCache
         | 
| 703 727 | 
             
                include ArJdbc::Abstract::TransactionSupport
         | 
| 704 728 |  | 
| 729 | 
            +
                ##
         | 
| 730 | 
            +
                # :singleton-method:
         | 
| 731 | 
            +
                # Configure the SQLite3Adapter to be used in a strict strings mode.
         | 
| 732 | 
            +
                # This will disable double-quoted string literals, because otherwise typos can silently go unnoticed.
         | 
| 733 | 
            +
                # For example, it is possible to create an index for a non existing column.
         | 
| 734 | 
            +
                # If you wish to enable this mode you can add the following line to your application.rb file:
         | 
| 735 | 
            +
                #
         | 
| 736 | 
            +
                #   config.active_record.sqlite3_adapter_strict_strings_by_default = true
         | 
| 737 | 
            +
                class_attribute :strict_strings_by_default, default: false # Does not actually do anything right now
         | 
| 738 | 
            +
             | 
| 739 | 
            +
                def initialize(...)
         | 
| 740 | 
            +
                  super
         | 
| 741 | 
            +
             | 
| 742 | 
            +
                  conn_params = @config.compact
         | 
| 743 | 
            +
             | 
| 744 | 
            +
                  # NOTE: strict strings is not supported by the jdbc driver yet,
         | 
| 745 | 
            +
                  # hope it will supported soon, I open a issue in their repository.
         | 
| 746 | 
            +
                  #   https://github.com/xerial/sqlite-jdbc/issues/1153
         | 
| 747 | 
            +
                  #
         | 
| 748 | 
            +
                  # @config[:strict] = ConnectionAdapters::SQLite3Adapter.strict_strings_by_default unless @config.key?(:strict)
         | 
| 749 | 
            +
             | 
| 750 | 
            +
                  @connection_parameters = conn_params
         | 
| 751 | 
            +
                end
         | 
| 752 | 
            +
             | 
| 705 753 | 
             
                def self.represent_boolean_as_integer=(value) # :nodoc:
         | 
| 706 754 | 
             
                  if value == false
         | 
| 707 755 | 
             
                    raise "`.represent_boolean_as_integer=` is now always true, so make sure your application can work with it and remove this settings."
         | 
| @@ -736,23 +784,15 @@ module ActiveRecord::ConnectionAdapters | |
| 736 784 | 
             
                # SQLite driver doesn't support all types of insert statements with executeUpdate so
         | 
| 737 785 | 
             
                # make it act like a regular query and the ids will be returned from #last_inserted_id
         | 
| 738 786 | 
             
                # example: INSERT INTO "aircraft" DEFAULT VALUES
         | 
| 739 | 
            -
                def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
         | 
| 740 | 
            -
                   | 
| 787 | 
            +
                def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil)
         | 
| 788 | 
            +
                  sql, binds = sql_for_insert(sql, pk, binds, returning)
         | 
| 789 | 
            +
                  internal_exec_query(sql, name, binds)
         | 
| 741 790 | 
             
                end
         | 
| 742 791 |  | 
| 743 792 | 
             
                def jdbc_column_class
         | 
| 744 793 | 
             
                  ::ActiveRecord::ConnectionAdapters::SQLite3Column
         | 
| 745 794 | 
             
                end
         | 
| 746 795 |  | 
| 747 | 
            -
                def jdbc_connection_class(spec)
         | 
| 748 | 
            -
                  self.class.jdbc_connection_class
         | 
| 749 | 
            -
                end
         | 
| 750 | 
            -
             | 
| 751 | 
            -
                # @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_connection_class
         | 
| 752 | 
            -
                def self.jdbc_connection_class
         | 
| 753 | 
            -
                  ::ActiveRecord::ConnectionAdapters::SQLite3JdbcConnection
         | 
| 754 | 
            -
                end
         | 
| 755 | 
            -
             | 
| 756 796 | 
             
                # Note: This is not an override of ours but a moved line from AR Sqlite3Adapter to register ours vs our copied module (which would be their class).
         | 
| 757 797 | 
             
            #    ActiveSupport.run_load_hooks(:active_record_sqlite3adapter, SQLite3Adapter)
         | 
| 758 798 |  | 
| @@ -770,6 +810,24 @@ module ActiveRecord::ConnectionAdapters | |
| 770 810 | 
             
                ::ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3)
         | 
| 771 811 |  | 
| 772 812 | 
             
                class << self
         | 
| 813 | 
            +
                  def jdbc_connection_class
         | 
| 814 | 
            +
                    ::ActiveRecord::ConnectionAdapters::SQLite3JdbcConnection
         | 
| 815 | 
            +
                  end
         | 
| 816 | 
            +
             | 
| 817 | 
            +
                  def new_client(conn_params, adapter_instance)
         | 
| 818 | 
            +
                    jdbc_connection_class.new(conn_params, adapter_instance)
         | 
| 819 | 
            +
                  end
         | 
| 820 | 
            +
             | 
| 821 | 
            +
                  def dbconsole(config, options = {})
         | 
| 822 | 
            +
                    args = []
         | 
| 823 | 
            +
             | 
| 824 | 
            +
                    args << "-#{options[:mode]}" if options[:mode]
         | 
| 825 | 
            +
                    args << "-header" if options[:header]
         | 
| 826 | 
            +
                    args << File.expand_path(config.database, const_defined?(:Rails) && Rails.respond_to?(:root) ? Rails.root : nil)
         | 
| 827 | 
            +
             | 
| 828 | 
            +
                    find_cmd_and_exec("sqlite3", *args)
         | 
| 829 | 
            +
                  end
         | 
| 830 | 
            +
             | 
| 773 831 | 
             
                  private
         | 
| 774 832 | 
             
                    def initialize_type_map(m)
         | 
| 775 833 | 
             
                      super
         | 
| @@ -0,0 +1,103 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module ActiveRecord::ConnectionAdapters
         | 
| 4 | 
            +
              class SQLite3Column < JdbcColumn
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                attr_reader :rowid
         | 
| 7 | 
            +
                
         | 
| 8 | 
            +
                def initialize(name, default, sql_type_metadata = nil, null = true, default_function = nil, collation: nil, comment: nil, auto_increment: nil, rowid: false, **)
         | 
| 9 | 
            +
                  super
         | 
| 10 | 
            +
                  @auto_increment = auto_increment
         | 
| 11 | 
            +
                  @default = nil if default =~ /NULL/
         | 
| 12 | 
            +
                  @rowid = rowid
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def self.string_to_binary(value)
         | 
| 16 | 
            +
                  value
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def self.binary_to_string(value)
         | 
| 20 | 
            +
                  if value.respond_to?(:encoding) && value.encoding != Encoding::ASCII_8BIT
         | 
| 21 | 
            +
                    value = value.force_encoding(Encoding::ASCII_8BIT)
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
                  value
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                # @override {ActiveRecord::ConnectionAdapters::JdbcColumn#default_value}
         | 
| 27 | 
            +
                def default_value(value)
         | 
| 28 | 
            +
                  # JDBC returns column default strings with actual single quotes :
         | 
| 29 | 
            +
                  return $1 if value =~ /^'(.*)'$/
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  value
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def auto_increment?
         | 
| 35 | 
            +
                  @auto_increment
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                def auto_incremented_by_db?
         | 
| 39 | 
            +
                  auto_increment? || rowid
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                def init_with(coder)
         | 
| 43 | 
            +
                  @auto_increment = coder["auto_increment"]
         | 
| 44 | 
            +
                  super
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                def encode_with(coder)
         | 
| 48 | 
            +
                  coder["auto_increment"] = @auto_increment
         | 
| 49 | 
            +
                  super
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                def ==(other)
         | 
| 53 | 
            +
                  other.is_a?(Column) &&
         | 
| 54 | 
            +
                    super &&
         | 
| 55 | 
            +
                    auto_increment? == other.auto_increment?
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
                alias :eql? :==
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                def hash
         | 
| 60 | 
            +
                  Column.hash ^
         | 
| 61 | 
            +
                    super.hash ^
         | 
| 62 | 
            +
                    auto_increment?.hash ^
         | 
| 63 | 
            +
                    rowid.hash
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                # @override {ActiveRecord::ConnectionAdapters::Column#type_cast}
         | 
| 67 | 
            +
                def type_cast(value)
         | 
| 68 | 
            +
                  return nil if value.nil?
         | 
| 69 | 
            +
                  case type
         | 
| 70 | 
            +
                    when :string then value
         | 
| 71 | 
            +
                    when :primary_key
         | 
| 72 | 
            +
                      value.respond_to?(:to_i) ? value.to_i : ( value ? 1 : 0 )
         | 
| 73 | 
            +
                    when :float    then value.to_f
         | 
| 74 | 
            +
                    when :decimal  then self.class.value_to_decimal(value)
         | 
| 75 | 
            +
                    when :boolean  then self.class.value_to_boolean(value)
         | 
| 76 | 
            +
                    else super
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
                end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                private
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                # @override {ActiveRecord::ConnectionAdapters::Column#extract_limit}
         | 
| 83 | 
            +
                def extract_limit(sql_type)
         | 
| 84 | 
            +
                  return nil if sql_type =~ /^(real)\(\d+/i
         | 
| 85 | 
            +
                  super
         | 
| 86 | 
            +
                end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                def extract_precision(sql_type)
         | 
| 89 | 
            +
                  case sql_type
         | 
| 90 | 
            +
                    when /^(real)\((\d+)(,\d+)?\)/i then $2.to_i
         | 
| 91 | 
            +
                    else super
         | 
| 92 | 
            +
                  end
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                def extract_scale(sql_type)
         | 
| 96 | 
            +
                  case sql_type
         | 
| 97 | 
            +
                    when /^(real)\((\d+)\)/i then 0
         | 
| 98 | 
            +
                    when /^(real)\((\d+)(,(\d+))\)/i then $4.to_i
         | 
| 99 | 
            +
                    else super
         | 
| 100 | 
            +
                  end
         | 
| 101 | 
            +
                end
         | 
| 102 | 
            +
              end
         | 
| 103 | 
            +
            end
         |