Giter VIP home page Giter VIP logo

torque-postgresql's Introduction

Torque PostgreSQL - Advanced PG features in a seamlessly RoR interface

CircleCI Code Climate Gem Version

Description

torque-postgresql is a plugin that enhances Ruby on Rails enabling easy access to existing PostgreSQL advanced resources, such as data types and query statements. Its features are designed to be similar to Rails architecture and work as smoothly as possible.

Fully compatible with schema.rb and 100% plug-and-play, with optional configurations, so that it can be adapted to your project's design pattern.

Installation

To install torque-postgresql you need to add the following to your Gemfile:

gem 'torque-postgresql', '~> 2.0'   # For Rails >= 6.0 < 6.1
gem 'torque-postgresql', '~> 2.0.4' # For Rails >= 6.1
gem 'torque-postgresql', '~> 3.0'   # For Rails >= 7.0 < 7.1
gem 'torque-postgresql', '~> 3.3'   # For Rails >= 7.1 < 7.2
gem 'torque-postgresql', '~> 3.4'   # For Rails >= 7.2

Also, run:

$ bundle

Or, for non-Gemfile related usage, simply:

gem install torque-postgresql

Usage

These are the currently available features:

Data types

Querying

How to Contribute

To start, simply fork the project, create a .env file following this example:

DATABASE_URL="postgres://USER:PASSWORD@localhost/DATABASE"

Run local tests using:

$ bundle install
$ bundle exec rake spec

Finally, fix and send a pull request.

License

Copyright © 2017- Carlos Silva. See The MIT License for further details.

torque-postgresql's People

Contributors

crashtech avatar etoundi2nd avatar kronn avatar mlt avatar olucasmarquess avatar petergoldstein avatar rklemme avatar smarquez1 avatar tomasc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

torque-postgresql's Issues

ActiveSupport::Duration::ISO8601Parser::ParsingError when working with interval fields under certain curcumstances

Steps to reproduce:

  1. Have an ActiveRecord model with an interval field. Let's say Event and duration.
  2. Invoke statements:
event = Event.find(1)
event.update(duration: ActiveSupport::Duration.build(1000000)) # 1000000 is just a large number of seconds
event.duration

The last statement event.duration chokes with an ActiveSupport::Duration::ISO8601Parser::ParsingError exception with message like Invalid ISO 8601 duration: "P1W4DT13H46M40S" mixing weeks with other date parts not allowed.

Reason: ActiveSupport::Duration.build assings the weeks field of the Duration instance, then such a value during #update gets incorrectly serialized in Torque::PostgreSQL::Adapter::OID::Interval#serialize to a string like P1W4DT13H46M40S. After #update on access to the field, the value tries to deserialize from such a string and ISO8601Parser actually does care of the format correctness.

Possible fix: sanitize ActiveSupport::Duration values somehow in Torque::PostgreSQL::Adapter::OID::Interval#cast

enum.base_method configuration has no effect

I'm on Rails 5.2 and I have ActiveRecord's featured enums in some models. Thus torque-postgresql breaks the code by bringing it's own enum class method into ActiveRecord::Base.

Have tried to alter the method name as stated in the docs by placing the following code into application.rb:

Torque::PostgreSQL.configure do |c|
  c.enum.base_method = :pg_enum
end

Which seems to have no effect.

From what I understand, all components of Torque::PostgreSQL are require'ed on gem loading, including Torque::PostgreSQL::Attributes::Enum::Base, which body is evaluated resulting in the definition of the conflicting enum method -- all of that happens BEFORE the configuration statement I've put into application.rb is executed.

I've resorted to a kinda dirty fix by just removing the method:

Torque::PostgreSQL::Attributes::Enum::Base.module_eval do
  remove_method :enum
end

I have torque-postgresql (0.2.13) in Gemfile.lock.

Torque slows down requests

Hi, I just installed torque-posgresql and while it works very well it really slows down my application.
I created a dummy Rails app with a seed file to test this, and just by including the gem my requests are being slowing down:

time curl localhost:3000/projects.

# Adding torque-postgresql 1.1.1 to Gemfile
1st request 9.44s
2nd request 7.6s

# Commenting out torque-postgresql from Gemfile
1st request 1.11s
2nd request 599ms

Tested in development using:
Rails 5.2.4
Ruby 2.5.5p157
Postgresql 12.1

Dummy Rails App: https://github.com/smarquez1/torque_test

Error trying to associate records to a new model

When I try to associate records to a new mode, I get the following ActiveRecord error:

> tag = FactoryBot.create(:tag)
> Video.new tags: [tag]

 ActiveRecord::ActiveRecordError:
       cannot update a new record

I think that the following line is the culprit, since it tries to update the record without knowing if it's already persisted: https://github.com/crashtech/torque-postgresql/blob/master/lib/torque/postgresql/associations/belongs_to_many_association.rb#L36

Smart association reusage

Rails is not smart when the subject is association shortcuts.
Something like:

class Text < ActiveRecord::Base
  belongs_to :user
  has_many :user_comments, through: :user, source: :comments
end

class User < ActiveRecord::Base
  has_many :comments
end

query = Text.includes(user: :comments).strict_loading
query.first.user_comments # Raises error

We can safely assume that: Because association :user_comments has no scope, it is the equivalent of calling user.comments, as long as the comments association was loaded without any additional scope (or even equivalent scopes).

The idea is to assign the @target and mark the association as loaded as long as it complies with the necessary requirements. Otherwise, just go ahead and load the association.

has_many array: custom column name

Hi,

I am doing migration in my application for normal has many to using an array.
Since my app already uses tag_ids in events we send any many other places, I named the new column tags_ids.

  • Is it possible to pass specific column for has_many array?
  • my current relation is has_and_belongs_to_many - does it supports an array?

belongs_to_many: don't allow to inlined

Hi!

Working with the latest version of the gem and rails 6.1.1, I have noticed an issue:

# frozen_string_literal: true
require 'torque-postgresql'

require "active_record"
require "minitest/autorun"
require "logger"

ActiveRecord::Base.establish_connection(
  adapter:  "postgresql",
  database: "test",
  encoding: "unicode",
  host:     "localhost",
  port:     "5432",
  password: "12345",
  username: "test")

ActiveRecord::Schema.define do
  drop_table "employees"
  drop_table "projects"

  create_table "employees" do |t|
    t.string "name"
    t.timestamps
  end

  create_table "projects" do |t|
    t.bigint "employees_ids", array: true
    t.string "title"
    t.timestamps
  end
end

class Employee < ActiveRecord::Base
end

class Project < ActiveRecord::Base
  belongs_to_many :employees, foreign_key: :employees_ids
end


class BugTest < Minitest::Test
  def test_create
    # create project with employee
    employee = Employee.create!
    project = Project.create!(employees: [employee])

    assert_equal 1, project.employees.count
    assert_equal 1, project.employees_ids.count

    project.reload

    assert_equal 1, project.employees.count
    assert_equal 1, project.employees_ids.count
  end
end

The employees_ids in this case aren't persisted to the DB, this the query that is running:

INSERT INTO "projects" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id"

Looks like that concat_recordsis called - https://github.com/crashtech/torque-postgresql/blob/master/lib/torque/postgresql/associations/belongs_to_many_association.rb#L94
result is an array of employees - which is good, but ids_reader returns an empty array.

deduplicated: undefined method '-@' for []:Array with Rails 6.1 and arrays with default value

Here's test code:

# frozen_string_literal: true

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  # Activate the gem you are reporting the issue against.
  gem "activerecord", "6.1.0"
  gem "torque-postgresql"
  gem "pg"
end

require "active_record"
require "logger"

ActiveRecord::Base.establish_connection(
  adapter:  "postgresql",
  database: "torque_pg_test",
  encoding: "unicode",
  host:     "localhost",
  port:     "5432",
  password: "postgres",
  username: "postgres"
)

ActiveRecord::Schema.define do
  drop_table "employees" if table_exists?("employees")
  drop_table "projects" if table_exists?("projects")

  create_table "employees" do |t|
    t.string "name"
    t.timestamps
  end

  create_table "projects" do |t|
    t.bigint "employees_ids", array: true, default: []
    t.string "title"
    t.timestamps
  end
end

class Employee < ActiveRecord::Base
end

class Project < ActiveRecord::Base
  belongs_to_many :employees, foreign_key: :employees_ids
end

Project.inspect

Notice array: true, default: [] in t.bigint "employees_ids", array: true, default: []

When calling Project.inspect since Rails 6.1 there is an exception in AR:

lib/active_record/connection_adapters/column.rb:94:in `deduplicated': undefined method `-@' for []:Array (NoMethodError)

Looks like AR now tries to unfreeze @default, which cannot be done to an Array

Allow Distinct On to auto set the order

Give a setting like distinct_on.auto_order and when is set to true and a query statement that has Distinct On is created without an order, auto set the same columns from the Distinct On operator to the order.

cast_record doesn't work with belong and duration cannot be sum with time value

Consider

app/models/event.rb

def start_at=(value)
  start_at = value.to_time
  finish_at = start_at + activity.reload.cast_record.duration.to_i
  self.period = (start_at finish_at)
end
  • It is necessary to use 'reload' to work and acess duration
    activity.reload.cast_record.duration

  • It is necessary to use to_i to work and sum some time value
    activity.reload.cast_record.duration.to_i

belongs_to_many: associated relations are removed

Hi!

I am trying out this gem, with belongs_to_many on rails 6.1, running my tests I found a bug which causes relations to be removed.

Once:

  • I have a before_save callback which does .count
  • ..and I do #update to update another column

The associated relations are removed, with the script below test_sanity_with_callback will fail
Both on rails 6.1 and 6.0

# frozen_string_literal: true

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  # Activate the gem you are reporting the issue against.
  gem "activerecord", "6.1.0"
  gem "torque-postgresql"
  gem "pg"
end

require "active_record"
require "minitest/autorun"
require "logger"

ActiveRecord::Base.establish_connection(
  adapter:  "postgresql",
  database: "test",
  encoding: "unicode",
  host:     "localhost",
  port:     "5432",
  password: "12345",
  username: "test")

ActiveRecord::Schema.define do
  drop_table "employees"
  drop_table "projects"

  create_table "employees" do |t|
    t.string "name"
    t.timestamps
  end

  create_table "projects" do |t|
    t.bigint "employees_ids", array: true
    t.string "title"
    t.timestamps
  end
end

class Employee < ActiveRecord::Base
end

class Project < ActiveRecord::Base
  belongs_to_many :employees, foreign_key: :employees_ids

  before_save :check_count

  def check_count
    # NOTE: the `.count` here is important for the failure
    puts "Project#check_count - #{employees.count}"
  end
end


class BugTest < Minitest::Test
  def test_sanity
    # create project with employee
    employee = Employee.create!
    project = Project.create!
    project.employees << employee

    project.update(title: "new title")

    project.reload

    assert_equal 1, project.employees.count
    assert_equal 1, project.employees_ids.count
  end

  def test_sanity_with_callback
    # create project with employee
    employee = Employee.create!
    project = Project.create!
    project.employees << employee

    # NOTE
    #
    # Same as `test_sanity`, but here the difference is:
    # * I am doing `.reload`
    # * We have a callback
    #
    # Both of these will cause `update` to remove `employees_ids`
    #
    # happens because of: https://github.com/crashtech/torque-postgresql/blob/master/lib/torque/postgresql/autosave_association.rb#L38
    # Set `employees_ids` to empty array
    #
    project.reload

    project.update(title: "new title")

    assert_equal 1, project.employees.count
    assert_equal 1, project.employees_ids.count
  end
end

`after_add` callback not called when updating using the `foreign_key`

Hey, for some reason, when I update the foreign_key of the collection it does not run the after_add callback

# frozen_string_literal: true
require 'torque-postgresql'

require 'byebug'
require "active_record"
require "minitest/autorun"
require "logger"

ActiveRecord::Base.establish_connection(
  adapter:  "postgresql",
  database: "test",
  encoding: "unicode",
  host:     "localhost",
  port:     "5432",
  password: "12345",
  username: "test")

ActiveRecord::Schema.define do
  drop_table "employees", if_exists: true
  drop_table "projects", if_exists: true

  create_table "employees" do |t|
    t.string "name"
    t.timestamps
  end

  create_table "projects" do |t|
    t.string "title"
    t.bigint "employees_ids", array: true
    t.timestamps
  end
end

class Employee < ActiveRecord::Base
  has_many :projects, array: true, foreign_key: :employees_ids

  after_commit :on_update, on: :update

  def on_update
    puts "Employee got updated"
  end
end

class Project < ActiveRecord::Base
  belongs_to_many :employees, foreign_key: "employees_ids" , after_add: :on_employee_added

  def on_employee_added(employee)
    puts "project: #{self.title} | employee added: #{employee.name}"
  end
end

# Not working
def run_using_ids
  employee_id = (Employee.create!(name: 'employee_by_id')).id
  project_id = (Project.create!(title: 'project_by_id')).id

  puts "[ids] Adding employee to project"
  project = Project.find_by(id: project_id)
  project.employees_ids = [employee_id]
  project.save!
end

# Working
def run_using_records
  employee = (Employee.create!(name: 'employee_by_record'))
  project_id = (Project.create!(title: 'project_by_record')).id

  puts "[records] Adding employee to project"
  project = Project.find_by(id: project_id)
  project.employees = [employee]
  project.save!
end



run_using_ids

run_using_records

the run_by_ids function does not call the on_employee_added after_add method while the run_using_records does

Currently, this will be logged would be:

[ids] Adding employee to project
[records] Adding employee to project
project: project_by_record | employee added: employee_by_record

(we missing project: project_by_id | employee added: employee_by_id line)

Rails 7.1.1 incompatibilities

On upgrading to Rails 7.1.1 from Rails 7.0 we encountered a couple of failures

We only use has_many :table_name, array: true and belongs_to_many features and these fixes allowed us to continue forward

Fix 1:

lib/torque/postgresql/inheritance.rb:60

def physically_inherited?
  return @physically_inherited if defined?(@physically_inherited)

  @physically_inherited = connection.schema_cache.dependencies(
    defined?(@table_name) ? @table_name : decorated_table_name,
  ).present?
rescue ActiveRecord::ConnectionNotEstablished
  false
end

This crashes on rails c when our Company model gets loaded with this error:

undefined method `dependencies' for #<ActiveRecord::ConnectionAdapters::BoundSchemaReflection

We don't use table inheritance so replacing the middle 3 lines with false fixed it

Fix 2:

lib/torque/postgresql/inheritance.rb:69

def inheritance_dependents
  connection.schema_cache.associations(table_name) || []
end

This fails with the below. We don't use inheritance features so commenting out the line and returning an empty array fixed this.

NoMethodError: undefined method `associations' for #<ActiveRecord::ConnectionAdapters::BoundSchemaReflection...

Fix 3:

lib/torque/postgresql/reflection/association_reflection.rb:20

def derive_foreign_key
  result = super
  result = ActiveSupport::Inflector.pluralize(result) \
    if collection? && connected_through_array?
  result
end

In rails 7.1 the kwarg was added infer_from_inverse_of with a boolean value defaulted to true. We fixed it by adding the same kwarg

See the super implementation:
/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/activerecord-7.1.1/lib/active_record/reflection.rb:761

def derive_foreign_key(infer_from_inverse_of: true)
  if belongs_to?
    "#{name}_id"
  elsif options[:as]
    "#{options[:as]}_id"
  elsif options[:inverse_of] && infer_from_inverse_of
    inverse_of.foreign_key(infer_from_inverse_of: false)
  else
    active_record.model_name.to_s.foreign_key
  end
end

Caller:

/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/activerecord-7.1.1/lib/active_record/reflection.rb:507:in `foreign_key'"
/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/activerecord-7.1.1/lib/active_record/associations/builder/belongs_to.rb:79:in `add_touch_callbacks'",
/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/activerecord-7.1.1/lib/active_record/associations/builder/belongs_to.rb:23:in `define_callbacks'",
/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/activerecord-7.1.1/lib/active_record/associations/builder/association.rb:34:in `build'",
/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/activerecord-7.1.1/lib/active_record/associations.rb:1887:in `belongs_to'",
/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/activestorage-7.1.1/app/models/active_storage/attachment.rb:27:in `<class:Attachment>'",
/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/activestorage-7.1.1/app/models/active_storage/attachment.rb:20:in `<main>'",

Fix 4:

lib/torque/postgresql/schema_cache.rb:215

def prepare_data_sources
  super
  @data_sources_model_names = Torque::PostgreSQL.config
    .irregular_models.slice(*@data_sources.keys).map do |table_name, model_name|
    [table_name, (model_name.is_a?(Class) ? model_name : model_name.constantize)]
  end.to_h
end

This method now take 1 arg which is a connection. See super implementation:

/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/activerecord-7.1.1/lib/active_record/connection_adapters/schema_cache.rb:462

def prepare_data_sources(connection)
  tables_to_cache(connection).each do |source|
    @data_sources[source] = true
  end
end

Adding this 1 arg fixed it from crashing. We don't use connections to different dbs or schemas so this isn't affecting us

We won't be submitting a PR because we are damaging things

We're using
torque-postgresql (3.2.2)
rails (7.1.1)
ruby "3.2.2"

Rails 7.1: data_source_exists? wrong number of arguments

Hi,

Working on upgrading my project to Rails 7.1.2 (together with torque-postgresql 3.3.0) and getting an error (with eager_load = true):

From: https://github.com/crashtech/torque-postgresql/blob/master/lib/torque/postgresql/schema_cache.rb#L101
Calling: https://github.com/rails/rails/blob/main/activerecord/lib/active_record/connection_adapters/schema_cache.rb#L41 which accepts extra-parameter of connection.

/Users/yosi/.rvm/gems/ruby-3.2.2/gems/activerecord-7.1.2/lib/active_record/connection_adapters/schema_cache.rb:316:in `data_source_exists?': wrong number of arguments (given 1, expected 2) (ArgumentError)
        from /Users/yosi/.rvm/gems/ruby-3.2.2/gems/torque-postgresql-3.3.0/lib/torque/postgresql/schema_cache.rb:102:in `add_model_name'
        from /Users/yosi/.rvm/gems/ruby-3.2.2/gems/torque-postgresql-3.3.0/lib/torque/postgresql/schema_cache/schema_reflection.rb:7:in `add_model_name'
        from /Users/yosi/.rvm/gems/ruby-3.2.2/gems/torque-postgresql-3.3.0/lib/torque/postgresql/schema_cache/bound_schema_reflection.rb:7:in `add_model_name'
        from /Users/yosi/.rvm/gems/ruby-3.2.2/gems/torque-postgresql-3.3.0/lib/torque/postgresql/inheritance.rb:93:in `reset_table_name'
        from /Users/yosi/.rvm/gems/ruby-3.2.2/gems/torque-postgresql-3.3.0/lib/torque/postgresql/base.rb:24:in `reset_table_name'

Made PR to fix this issue - #88

[request] Downgrade role after test fixtures loaded

Here is another idea... It is a known limitation that test environment has to use PostgreSQL superuser to be able to disable/enable triggers on fixtures loading.
I think it would be very nice if we could also issue set role ... to something configurable and reset role at appropriate time. This way we can test things as they are meant to be tested with normal unprivileged user.

BelongsToMany: can't reset column to empty array with defaults

Hi,

Checked the latest commit, following the discussion here - #56 (comment)

# frozen_string_literal: true
require 'torque-postgresql'

require 'byebug'
require "active_record"
require "minitest/autorun"
require "logger"

ActiveRecord::Base.establish_connection(
  adapter:  "postgresql",
  database: "test",
  encoding: "unicode",
  host:     "localhost",
  port:     "5432",
  password: "12345",
  username: "test")

ActiveRecord::Schema.define do
  drop_table "employees", if_exists: true
  drop_table "projects", if_exists: true

  create_table "employees" do |t|
    t.string "name"
    t.timestamps
  end

  create_table "projects" do |t|
    t.string "title"
    t.timestamps
  end

  execute "alter table projects add column employees_ids integer[] default '{}'::integer[]"
end

class Employee < ActiveRecord::Base
  has_many :projects, array: true, foreign_key: :employees_ids
end

class Project < ActiveRecord::Base
  belongs_to_many :employees, foreign_key: "employees_ids"
end

class BugTest < Minitest::Test
  def test_bug
    employee = Employee.create!

    project = Project.create!(employees_ids: [employee.id])

    # Accessing `project.employees`, cause the next update to get dismissed
    pp project.employees

    project.update(employees_ids: [])

    project.reload

    pp project.employees_ids
    pp project.employees
  end
end

I am trying to update the employee_ids to be an empty array, and it doesn't work.
This happens because ids_reader takes the ids from the target (while the target is stale)

polymorphic belongs_to_many?

I wonder why current implementation of belongs_to_many cannot be polymorphic. Is there some restriction inside AR or is it just a lack of time to add support for it?

Add #map to enum class

Be able to iterate over real instances of the enum class, plus be able to .map over the values.

[rails 7] preloader bug: "private method `load_records' called for"

Hi,

When running torque-postgresql with rails 7 I get this error when I am using the preloader directly:

Reproduce script:

# frozen_string_literal: true

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  # Activate the gem you are reporting the issue against.
  gem "activerecord", "~> 7.0.0"
  gem "pg"
  gem "torque-postgresql"
end

require "active_record"
require "logger"
require 'torque-postgresql'

ActiveRecord::Base.establish_connection(
  adapter:  "postgresql",
  database: "test",
  encoding: "unicode",
  host:     "localhost",
  port:     "5432",
  password: "12345",
  username: "test")

ActiveRecord::Schema.define do
  drop_table "employees", if_exists: true
  drop_table "projects", if_exists: true

  create_table :comments, force: true do |t|
    t.integer :author_id
  end

  create_table :authors, force: true do |t|
  end
end

class Author < ActiveRecord::Base
  has_many :comments
end

class Comment < ActiveRecord::Base
  belongs_to :author
end

author = Author.create!
author.comments << [Comment.new, Comment.new]

ActiveRecord::Associations::Preloader.new(records: Author.all, associations: [:comments]).call

Error:

Traceback (most recent call last):
        8: from bug.rb:52:in `<main>'
        7: from /Users/yosi/.rvm/gems/ruby-2.7.2/gems/activerecord-7.0.2.2/lib/active_record/associations/preloader.rb:118:in `call'
        6: from /Users/yosi/.rvm/gems/ruby-2.7.2/gems/activerecord-7.0.2.2/lib/active_record/associations/preloader/batch.rb:27:in `call'
        5: from /Users/yosi/.rvm/gems/ruby-2.7.2/gems/activerecord-7.0.2.2/lib/active_record/associations/preloader/batch.rb:41:in `group_and_load_similar'
        4: from /Users/yosi/.rvm/gems/ruby-2.7.2/gems/activerecord-7.0.2.2/lib/active_record/associations/preloader/batch.rb:41:in `each_pair'
        3: from /Users/yosi/.rvm/gems/ruby-2.7.2/gems/activerecord-7.0.2.2/lib/active_record/associations/preloader/batch.rb:42:in `block in group_and_load_similar'
        2: from /Users/yosi/.rvm/gems/ruby-2.7.2/gems/activerecord-7.0.2.2/lib/active_record/associations/preloader/association.rb:32:in `load_records_in_batch'
        1: from /Users/yosi/.rvm/gems/ruby-2.7.2/gems/activerecord-7.0.2.2/lib/active_record/associations/preloader/association.rb:32:in `each'
/Users/yosi/.rvm/gems/ruby-2.7.2/gems/activerecord-7.0.2.2/lib/active_record/associations/preloader/association.rb:33:in `block in load_records_in_batch': private method `load_records' called for #<ActiveRecord::Associations::Preloader::Association:0x00000001503081e8> (NoMethodError)
Did you mean?  loaded?

When I comment out require & gem of torque it works as expected.

Did some "dirty" commit in my fork - yosiat@9c5e681 and it works with it

Array associations bug

Say we need to remove an existing association from a video_ids array column like this:

class Video < ActiveRecord::Base
  has_many :tags, array: true
end

class Tag < ActiveRecord::Base
  belongs_to_many :videos
end

video = Video.first
video2 = Video.last

tag = Tag.first
tag.video_ids = [video.id, video2.id]
tag.save!

video.tags.clear # BOOM! 💣 

video2.tags.empty? # THIS WILL BE TRUE!

That should not be the expected behavior, right?

Type casting support is missing

It would be nice to have native ranges with support for infinity with a minimum efforts.

>> rng = ActiveRecord::Base.connection.select_one("select tstzrange('2019-01-01', 'Infinity', '[)')")['tstzrange']
=> "[\"2019-01-01 00:00:00+00\",infinity)"
>> rng.class
=> String
>> rng = ActiveRecord::Base.connection.select_all("select tstzrange('2019-01-01', 'Infinity', '[)')")
=> #<ActiveRecord::Result:0x0000000012ffde00 @columns=["tstzrange"], @rows=[["[\"2019-01-01 00:00:00+00\",infinity)"]], @hash_rows=nil, @column_types={"tstzrange"=>#<ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Range:0x0000000013936850 @subtype=#<ActiveRecord::ConnectionAdapters::PostgreSQL::OID::DateTime:0x00000000139368a0 @precision=nil, @scale=nil, @limit=nil>, @type=:tstzrange>}>
>> rng.column_types['tstzrange'].cast rng.first['tstzrange']
=> 2019-01-01 00:00:00 UTC...#<Date::Infinity:0x0000000010e19f00 @d=1>

Something along these lines could be worked into a concern or something

module ActiveRecordResultCast

  extend ActiveSupport::Concern

  def cast
    if Rails::VERSION::MAJOR >= 5
      self.map{|o| o.each{|k, v| o[k] = column_types[k].cast v}}
    else
      ...
    end
  end
end

ActiveRecord::Result.send(:include, ActiveRecordResultCast)

Then

>> rng.cast
=> [{"tstzrange"=>2019-01-01 00:00:00 UTC...#<Date::Infinity:0x000000000b314600 @d=1>}]
>> rng.cast.first['tstzrange']
=> 2019-01-01 00:00:00 UTC...#<Date::Infinity:0x000000000b314600 @d=1>

I don't know whether there is a way to make it work with like select_one without explicit #cast call. Would be nice though…

Edit

I also have

module DatetimeInfinity
  extend ActiveSupport::Concern

  def infinity(options = {})
    options[:negative] ? -::DateTime::Infinity.new : ::DateTime::Infinity.new
  end
end

ActiveRecord::ConnectionAdapters::PostgreSQL::OID::DateTime.send(:include, DatetimeInfinity)

Otherwise you end up with

>> rng.cast.first['tstzrange'].last.class
=> Float

Enum and inheritance incompatibility

Enum methods are created only in the source class. To fix this, create a dynamic module for the enum methods and then include that module on the source class.

add gem to gemfile, couldent start rails server

Traceback (most recent call last):
	112: from bin/rails:4:in `<main>'
	111: from bin/rails:4:in `require'
	110: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/railties-5.2.2.1/lib/rails/commands.rb:18:in `<top (required)>'
	109: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/railties-5.2.2.1/lib/rails/command.rb:46:in `invoke'
	108: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/railties-5.2.2.1/lib/rails/command/base.rb:65:in `perform'
	107: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/thor-0.20.3/lib/thor.rb:387:in `dispatch'
	106: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/thor-0.20.3/lib/thor/invocation.rb:126:in `invoke_command'
	105: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/thor-0.20.3/lib/thor/command.rb:27:in `run'
	104: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/railties-5.2.2.1/lib/rails/commands/server/server_command.rb:142:in `perform'
	103: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/railties-5.2.2.1/lib/rails/commands/server/server_command.rb:142:in `tap'
	102: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/railties-5.2.2.1/lib/rails/commands/server/server_command.rb:147:in `block in perform'
	101: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/railties-5.2.2.1/lib/rails/commands/server/server_command.rb:51:in `start'
	100: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/railties-5.2.2.1/lib/rails/commands/server/server_command.rb:89:in `log_to_stdout'
	 99: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/rack-2.0.7/lib/rack/server.rb:354:in `wrapped_app'
	 98: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/railties-5.2.2.1/lib/rails/commands/server/server_command.rb:27:in `app'
	 97: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/rack-2.0.7/lib/rack/server.rb:219:in `app'
	 96: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/rack-2.0.7/lib/rack/server.rb:319:in `build_app_and_options_from_config'
	 95: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/rack-2.0.7/lib/rack/builder.rb:40:in `parse_file'
	 94: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/rack-2.0.7/lib/rack/builder.rb:49:in `new_from_string'
	 93: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/rack-2.0.7/lib/rack/builder.rb:49:in `eval'
	 92: from config.ru:in `<main>'
	 91: from config.ru:in `new'
	 90: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/rack-2.0.7/lib/rack/builder.rb:55:in `initialize'
	 89: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/rack-2.0.7/lib/rack/builder.rb:55:in `instance_eval'
	 88: from config.ru:3:in `block in <main>'
	 87: from config.ru:3:in `require_relative'
	 86: from /Users/liyongbo/workspace/pi-api/config/environment.rb:5:in `<top (required)>'
	 85: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/railties-5.2.2.1/lib/rails/application.rb:361:in `initialize!'
	 84: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/railties-5.2.2.1/lib/rails/initializable.rb:60:in `run_initializers'
	 83: from /Users/liyongbo/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/tsort.rb:205:in `tsort_each'
	 82: from /Users/liyongbo/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/tsort.rb:226:in `tsort_each'
	 81: from /Users/liyongbo/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/tsort.rb:347:in `each_strongly_connected_component'
	 80: from /Users/liyongbo/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/tsort.rb:347:in `call'
	 79: from /Users/liyongbo/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/tsort.rb:347:in `each'
	 78: from /Users/liyongbo/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/tsort.rb:349:in `block in each_strongly_connected_component'
	 77: from /Users/liyongbo/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/tsort.rb:415:in `each_strongly_connected_component_from'
	 76: from /Users/liyongbo/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/tsort.rb:415:in `call'
	 75: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/railties-5.2.2.1/lib/rails/initializable.rb:50:in `tsort_each_child'
	 74: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/railties-5.2.2.1/lib/rails/initializable.rb:50:in `each'
	 73: from /Users/liyongbo/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/tsort.rb:421:in `block in each_strongly_connected_component_from'
	 72: from /Users/liyongbo/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/tsort.rb:431:in `each_strongly_connected_component_from'
	 71: from /Users/liyongbo/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/tsort.rb:422:in `block (2 levels) in each_strongly_connected_component_from'
	 70: from /Users/liyongbo/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/tsort.rb:350:in `block (2 levels) in each_strongly_connected_component'
	 69: from /Users/liyongbo/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/tsort.rb:228:in `block in tsort_each'
	 68: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/railties-5.2.2.1/lib/rails/initializable.rb:61:in `block in run_initializers'
	 67: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/railties-5.2.2.1/lib/rails/initializable.rb:32:in `run'
	 66: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/railties-5.2.2.1/lib/rails/initializable.rb:32:in `instance_exec'
	 65: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/railties-5.2.2.1/lib/rails/engine.rb:613:in `block in <class:Engine>'
	 64: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/railties-5.2.2.1/lib/rails/engine.rb:613:in `each'
	 63: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/railties-5.2.2.1/lib/rails/engine.rb:614:in `block (2 levels) in <class:Engine>'
	 62: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/railties-5.2.2.1/lib/rails/engine.rb:656:in `load_config_initializer'
	 61: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/notifications.rb:170:in `instrument'
	 60: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/railties-5.2.2.1/lib/rails/engine.rb:657:in `block in load_config_initializer'
	 59: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:285:in `load'
	 58: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:257:in `load_dependency'
	 57: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:285:in `block in load'
	 56: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:285:in `load'
	 55: from /Users/liyongbo/workspace/pi-api/config/initializers/form_core.rb:25:in `<top (required)>'
	 54: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:195:in `const_missing'
	 53: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:510:in `load_missing_constant'
	 52: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:356:in `require_or_load'
	 51: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:37:in `load_interlock'
	 50: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies/interlock.rb:13:in `loading'
	 49: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/concurrency/share_lock.rb:151:in `exclusive'
	 48: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies/interlock.rb:14:in `block in loading'
	 47: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:37:in `block in load_interlock'
	 46: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:373:in `block in require_or_load'
	 45: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:475:in `load_file'
	 44: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:661:in `new_constants_in'
	 43: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:476:in `block in load_file'
	 42: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:476:in `load'
	 41: from /Users/liyongbo/workspace/pi-api/app/models/fields.rb:3:in `<top (required)>'
	 40: from /Users/liyongbo/workspace/pi-api/app/models/fields.rb:11:in `<module:Fields>'
	 39: from /Users/liyongbo/workspace/pi-api/app/models/fields.rb:11:in `each'
	 38: from /Users/liyongbo/workspace/pi-api/app/models/fields.rb:12:in `block in <module:Fields>'
	 37: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:246:in `require_dependency'
	 36: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:334:in `depend_on'
	 35: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:356:in `require_or_load'
	 34: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:37:in `load_interlock'
	 33: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies/interlock.rb:13:in `loading'
	 32: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/concurrency/share_lock.rb:151:in `exclusive'
	 31: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies/interlock.rb:14:in `block in loading'
	 30: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:37:in `block in load_interlock'
	 29: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:373:in `block in require_or_load'
	 28: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:475:in `load_file'
	 27: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:661:in `new_constants_in'
	 26: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:476:in `block in load_file'
	 25: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:476:in `load'
	 24: from /Users/liyongbo/workspace/pi-api/app/models/fields/input_field.rb:3:in `<top (required)>'
	 23: from /Users/liyongbo/workspace/pi-api/app/models/fields/input_field.rb:4:in `<module:Fields>'
	 22: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:195:in `const_missing'
	 21: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:542:in `load_missing_constant'
	 20: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:195:in `const_missing'
	 19: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:510:in `load_missing_constant'
	 18: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:356:in `require_or_load'
	 17: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:37:in `load_interlock'
	 16: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies/interlock.rb:13:in `loading'
	 15: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/concurrency/share_lock.rb:151:in `exclusive'
	 14: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies/interlock.rb:14:in `block in loading'
	 13: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:37:in `block in load_interlock'
	 12: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:373:in `block in require_or_load'
	 11: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:475:in `load_file'
	 10: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:661:in `new_constants_in'
	  9: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:476:in `block in load_file'
	  8: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/dependencies.rb:476:in `load'
	  7: from /Users/liyongbo/workspace/pi-api/app/models/optional_column.rb:1:in `<top (required)>'
	  6: from /Users/liyongbo/workspace/pi-api/app/models/optional_column.rb:5:in `<class:OptionalColumn>'
	  5: from /Users/liyongbo/workspace/pi-api/app/models/optional_column.rb:5:in `include'
	  4: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/concern.rb:122:in `append_features'
	  3: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activesupport-5.2.2.1/lib/active_support/concern.rb:122:in `class_eval'
	  2: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/form_core-0.0.14/lib/form_core/concerns/models/field.rb:23:in `block in <module:Field>'
	  1: from /Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/torque-postgresql-1.0.1/lib/torque/postgresql/attributes.rb:27:in `method_missing'
/Users/liyongbo/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.2.1/lib/active_record/dynamic_matchers.rb:22:in `method_missing': undefined method `accessibilities' for #<Class:0x00007feae1f62880> (NoMethodError)

[bug] 3.1 breaks serial ids in schemas

It modifies

create_table "my_table", id: :serial, force: :cascade do |t|

to

create_table "my_table", id: :integer, default: -> { "nextval('my_table_id_seq'::regclass)" }, force: :cascade do |t|

where the default value does not work (probably) since the table does not exist yet.

BelongsToMany: calls after_commit on the associated object

Hi!

I did the migration to torque weeks ago and now I notified different behaviour other than "has_and_belongs_to_many" from rails.

# frozen_string_literal: true
require 'torque-postgresql'

require 'byebug'
require "active_record"
require "minitest/autorun"
require "logger"

ActiveRecord::Base.establish_connection(
  adapter:  "postgresql",
  database: "test",
  encoding: "unicode",
  host:     "localhost",
  port:     "5432",
  password: "12345",
  username: "test")

ActiveRecord::Schema.define do
  drop_table "employees", if_exists: true
  drop_table "projects", if_exists: true

  create_table "employees" do |t|
    t.string "name"
    t.timestamps
  end

  create_table "projects" do |t|
    t.string "title"
    t.bigint "employees_ids", array: true
    t.timestamps
  end
end

class Employee < ActiveRecord::Base
  has_many :projects, array: true, foreign_key: :employees_ids

  after_commit :on_update, on: :update

  def on_update
    puts "Employee got updated"
  end
end

class Project < ActiveRecord::Base
  belongs_to_many :employees, foreign_key: "employees_ids"
end

employee = Employee.create!
project = Project.create!

puts "Adding employee to project"
project.employees << employee
pp project.reload.employees_ids

When doing concat, both objects get after_commit on update calls, for Project, it makes sense since its employees_ids has changed, but for Employee, it doesn't make sense since nothing is changed there.

To overcome this after_commit callback, right now I need to add a check for return if saved_changes.empty? which I prefer to avoid.

Is there anyway to stop those after_commit calls?

ArgumentError from load_additional_types

With the gem installed, every AR query generates this error:

ArgumentError: wrong number of arguments (given 0, expected 1..2)
from /home/anthony/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/torque-postgresql-0.2.7/lib/torque/postgresql/adapter/database_statements.rb:42:in `load_additional_types'

I'm using:
Rails version 5.2.0.rc2
PG version 1.0.0

support for multi range (multi_period?)

I have implemented very rudimentary suport for multi range type in my app. Some example snippets below. I start to use these types of ranges more and more, as they proved very useful.

It would be great if torque-postgresql would have support for this. I can prepare a PR, but would need some guidance with how to implement this in the current gem's architecture.

# SQL
range_agg(tstzrange(starttime, endtime, '[]')) AS multi_period
# MultiPeriod.value(self[:multi_period]) => converts column to Array of Range objects
class MultiPeriod
  extend Dry::Initializer

  param :multi_period, type: Types::Array.of(Types.Instance(Range)) | Types::Coercible::String

  def self.value(...)
    new(...).value
  end

  def value
    return multi_period if multi_period.is_a?(Array)

    json_value.map do |from, to|
      (Time.zone.parse(from).to_datetime..Time.zone.parse(to).to_datetime)
    end.sort_by(&:first)
  end

  private
    def json_value
      JSON.parse("[#{multi_period[1..-2]}]")
    end
end
scope :order_by_lower, -> (dir = :asc) { order(Arel.sql("lower(multi_period) #{dir}")) }
scope :order_by_upper, -> (dir = :asc) { order(Arel.sql("upper(multi_period) #{dir}")) }

scope :multi_period_overlapping, -> (from, to) { where("multi_period && ?", Arel.sql("{[#{from},#{to}]}")) }

Status of the record coder?

I'm interested in implementing support for storing arbitrary record types in rows.

I saw that lib/torque/postgresql/coder.rb was removed in cf1c46f (message remove unnecessary code).

Do you recall whether it was working? If I get it running, would a PR be accepted?

Values are not autoloaded when model's field was not started

It's not able the following without any instance being created before it:

User.roles

That happens because the enum methods are only created after the schema of the model is loaded. That said, whevener ActiveRecord::Base is extended and the enum.initializer is set to true, I can either load the schema of the model, or (that has more performance) only identifies those class-related methods that should be defined.

Rails 7 - "uninitialized constant ActiveRecord::Reflection::AssociationReflection::VALID_AUTOMATIC_INVERSE_MACROS"

Hi,

Not sure if this is too early to file bugs since it's a alpha 02 release of rails.

rails aborted!
NameError: uninitialized constant ActiveRecord::Reflection::AssociationReflection::VALID_AUTOMATIC_INVERSE_MACROS
/Users/yosi/.rvm/gems/ruby-2.7.2/gems/torque-postgresql-2.1.3/lib/torque/postgresql/reflection/belongs_to_many_reflection.rb:59:in `<module:Reflection>'
/Users/yosi/.rvm/gems/ruby-2.7.2/gems/torque-postgresql-2.1.3/lib/torque/postgresql/reflection/belongs_to_many_reflection.rb:5:in `<module:PostgreSQL>'
/Users/yosi/.rvm/gems/ruby-2.7.2/gems/torque-postgresql-2.1.3/lib/torque/postgresql/reflection/belongs_to_many_reflection.rb:4:in `<module:Torque>'
/Users/yosi/.rvm/gems/ruby-2.7.2/gems/torque-postgresql-2.1.3/lib/torque/postgresql/reflection/belongs_to_many_reflection.rb:3:in `<main>'

Remove here - rails/rails@173a112

Ruby 3.0.2 error: when concating to relation with default scope

Hi,

Trying to upgrade to ruby 3.0.2 and I am getting an error when I am trying to concat to has_many with default-scope when torque is installed:

Reproduce script:

# frozen_string_literal: true

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  # Activate the gem you are reporting the issue against.
  gem "activerecord"
  gem "torque-postgresql"
  gem "pg"
end

require "active_record"
require "minitest/autorun"
require "logger"

ActiveRecord::Base.establish_connection(
  adapter:  "postgresql",
  database: "test",
  encoding: "unicode",
  host:     "localhost",
  port:     "5432",
  password: "12345",
  username: "test")

ActiveRecord::Schema.define do
  drop_table "projects"
  drop_table "managers"

  create_table "projects" do |t|
    t.string "title"
    t.timestamps
  end

  create_table "managers" do |t|
    t.string "name"
    t.integer :project_id
    t.integer "position"
    t.timestamps
  end
end

class Manager < ActiveRecord::Base
  belongs_to :project
end

class Project < ActiveRecord::Base
  # If we remove the "order" here the test will pass
  has_many :managers, -> { order(position: :asc) }
end


class BugTest < Minitest::Test
  def test_concat
    project = Project.create!
    project.managers << Manager.new
    project.save
  end
end

Output (removes lines of gem install)

ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [x86_64-darwin20]
/Users/yosi/.rvm/gems/ruby-3.0.2/gems/torque-postgresql-2.2.0/lib/torque/postgresql/attributes/builder/enum.rb:119: warning: assigned but unused variable - type
/Users/yosi/.rvm/gems/ruby-3.0.2/gems/torque-postgresql-2.2.0/lib/torque/postgresql/attributes/builder/period.rb:47: warning: method redefined; discarding old threshold
-- drop_table("projects")
   -> 0.0188s
-- drop_table("managers")
   -> 0.0016s
-- create_table("projects")
   -> 0.0063s
-- create_table("managers")
   -> 0.0056s
Run options: --seed 36840

# Running:

E

Error:
BugTest#test_concat:
ArgumentError: wrong number of arguments (given 2, expected 1)
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/relation.rb:27:in `initialize'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/torque-postgresql-2.2.0/lib/torque/postgresql/relation.rb:121:in `initialize'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/relation/delegation.rb:118:in `new'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/relation/delegation.rb:118:in `create'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/reflection.rb:285:in `build_scope'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/associations/association_scope.rb:165:in `eval_scope'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/associations/association_scope.rb:129:in `block (2 levels) in add_constraints'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/associations/association_scope.rb:128:in `each'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/associations/association_scope.rb:128:in `block in add_constraints'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/associations/association_scope.rb:125:in `reverse_each'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/associations/association_scope.rb:125:in `add_constraints'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/associations/association_scope.rb:29:in `scope'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/associations/association_scope.rb:7:in `scope'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/associations/association.rb:244:in `association_scope'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/associations/collection_association.rb:282:in `add_to_target'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/associations/collection_association.rb:437:in `block in concat_records'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/associations/collection_association.rb:435:in `each'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/associations/collection_association.rb:435:in `concat_records'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/associations/has_many_association.rb:130:in `concat_records'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/associations/collection_association.rb:120:in `block in concat'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/associations/collection_association.rb:135:in `block in transaction'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/connection_adapters/abstract/database_statements.rb:320:in `block in transaction'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/connection_adapters/abstract/transaction.rb:319:in `block in within_new_transaction'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activesupport-6.1.4.1/lib/active_support/concurrency/load_interlock_aware_monitor.rb:26:in `block (2 levels) in synchronize'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activesupport-6.1.4.1/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `handle_interrupt'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activesupport-6.1.4.1/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `block in synchronize'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activesupport-6.1.4.1/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `handle_interrupt'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activesupport-6.1.4.1/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `synchronize'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/connection_adapters/abstract/transaction.rb:317:in `within_new_transaction'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/connection_adapters/abstract/database_statements.rb:320:in `transaction'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/transactions.rb:209:in `transaction'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/associations/collection_association.rb:134:in `transaction'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/associations/collection_association.rb:120:in `concat'
    /Users/yosi/.rvm/gems/ruby-3.0.2/gems/activerecord-6.1.4.1/lib/active_record/associations/collection_proxy.rb:1027:in `<<'
    repro.rb:58:in `test_concat'

Fix integration with annotate

Annotate and the version 0.2.1 doesn't work together. The problem is that there is something frozen that apparently is blocking one of the operations of it.

Prepared statement bug?

Hi. I faced with very annoying bug. Let's assume we have the following structure:

    create_table :products do |t|
      t.string :name
      t.integer :code_ids, array: true
    end

    create_table :codes do |t|
      t.string :name
    end

And simple models with simple relations:

class Product < ApplicationRecord; belongs_to_many :codes ;end
class Code < ApplicationRecord; has_many :products, array: true; end

Let's prepare some data:

code_ids = Code.pluck(:id)

# create Product with 1 Code (it's important to be first)
Product.create(name: 'one code', code_ids: code_ids.sample)

# create Product with 5 Codes
Product.create(name: 'five codes', code_ids.sample(5))

Now, let's play with collection where first Product has one Code:

Product.order(id: :asc).each do |product|
  puts product.codes.map(&:name).to_sentence
end

In the output both Products will have just one Code, but second Product should have 5:

  Product Load (2.8ms)  SELECT "products".* FROM "products" ORDER BY "products"."id" ASC
  Code Load (0.5ms)  SELECT "codes".* FROM "codes" WHERE "codes"."id" IN ($1)  [["code_ids", 169]]
F-1981
  Code Load (0.4ms)  SELECT "codes".* FROM "codes" WHERE "codes"."id" IN ($1)  [["code_ids", 856]]
Q88

Go ahead and reload our server to clear cache and do vice versa experiment :)

Product.order(id: :desc).each do |product|
  puts product.codes.map(&:name).to_sentence
end

Now output seems to be fine, but relation still uses cached prepared statement and puts NULL in sql:

Product Load (0.3ms)  SELECT "products".* FROM "products" ORDER BY "products"."id" DESC
  Code Load (0.4ms)  SELECT "codes".* FROM "codes" WHERE "codes"."id" IN ($1, $2, $3, $4, $5)  
[["code_ids", 564], ["code_ids", 947], ["code_ids", 546], ["code_ids", 888], ["code_ids", 473]]
B54, VN-9122, O-2587, N-8840, X23

Code Load (0.2ms)  SELECT "codes".* FROM "codes" WHERE "codes"."id" IN ($1, $2, $3, $4, $5)  
[["code_ids", 169], ["code_ids", nil], ["code_ids", nil], ["code_ids", nil], ["code_ids", nil]]
F-1981

Any ideas how to fix this?

—————

My Gemfile:

ruby '2.5.6'

gem 'torque-postgresql', '~> 1.1'
gem 'pg', '= 1.2.3'
gem 'rails', '= 5.2.4.3'

[Feature] Try to use tableoid in order to distinguish records on table inheritance

Since the column tableoid is available without the need of extra join or anything, it seems to be faster to use it as the source of inherited table discover.

We already use the tableoid as a join to pg_class.oid to get the table name, but maybe we can do it directly and use the number as the reference. This can also facilitate many other mappings for inheritance.

schema dumper will drop ceate_views

how to use rails default ActiveRecord::SchemaDumper instead of this gem I need to watch custom functions ,
add this gem to Gemfile AND run rails db:migrate

`
create_function :trans_to, sql_definition: <<-SQL
...
SQL

create_view "receivable_statistics", sql_definition: <<-SQL
..
SQL
`
all function are gone in schema.rb
how to recover schema

Enum - Nil should be an Enum value

Allow nil to be turn into an Enum value so it's able to use methods like:

val = Enum::SomeEnum.new(nil)
val.created? # Any '?' will return false
val.created! # It can replace the value

Table inheritance 'read_attribute' doesn't work properly with 'id' attribute on inherited table

Although the attribute can still be read, inspection or the direct execution of the following doesn't work:

Activity.first.read_attribute(:id)

This only happens because on this method, the 'id' is replaced by the 'primary_key' of the table. Since the primary_key usually comes from the base table, the inherited table doesn't have it.

There may be two approaches. Rewrite the 'read_attribute' method to still try to get the 'id' when 'primary_key' is nil, or rewrite the method that returns the 'primary_key' to identify that's a inherited table and return it from the source.

The second option may be the best one for many reasons where methods rely on 'primary_key'.

Doesn’t work with Rails 6

Just letting you know that this gem doesn’t work with Rails 6 yet, due to some major changes in the AR schema API. I’m working on updating it and will hopefully have a pull request fairly soon.

Implement I18n for Interval

Allows I18n.localize interval to be translated into its pieces 1 year, 2 months, 3 days, 4 hours, 6 minutes, and 7 seconds, use array support and :one and :more for values.

User-defined type 'earth' cannot be dumped!

For some reason, whenever we activate earthdistance on PostgreSQL, it tries to dump an additional ENUM of type 'earth'. Although all the other types are correctly dumped, a fix is required because schema.rb needs to be consistent.

Auxiliary Statements doesn't work with Inheritance

Consider

# /app/models/account.rb
class Account < Account
  auxiliary_statement :balance do |cte|
    cte.query AccountTransaction.approved.group(:account_id)
    cte.attributes col(:value).sum => :balance
    cte.join_type :left
  end
end

# /app/models/user.rb
class User < Account
end

The following doesn't work:

User.with(:balance).first.balance
# There's no 'balance' auxiliary statement defined

The current workaround is the following

Account.cast_records(User, filter: true).with(:balance).first.balance

[request] schema-aware disabling of referential integrity while using fixtures

The problem is that when using fixtures for testing, rails disables referential integrity by using table names only. This requires putting all necessary schema names into a schema_search_path in config/database.yml. This is quite a PITA to remember. It would be nice if it was done automagically.

Rails calls ActiveRecord::ConnectionAdapters::PostgreSQL::ReferentialIntegrity#disable_referential_integrity
that uses ActiveRecord::ConnectionAdapters::SchemaStatements#tables which returns table names only without schema name to be quoted in #disable_referential_integrity. I feel like something could be done by returning already enquoted (as in PG format() with %I) name within ActiveRecord::ConnectionAdapters::PostgreSQL::SchemaStatements#data_source_sql or somewhere there.

I'm not sure how doable it is or whether it is better to file a bug somewhere upstream, but I'll leave it here as an idea.

[issue] undefined method `foreign_key_in_create' for #<ActiveRecord::ConnectionAdapters::PostgreSQL::SchemaCreation:0x0000000106d2e0e0

Rails version: 7.0.4
This error is happening when there is a migration that has a foreign key constraint in it
The migration file looks this

    create_table :sample_table do |t|
      t.belongs_to :account, null: false, index: true, foreign_key: true
     end

Error log:

Caused by: NoMethodError: undefined method `foreign_key_in_create' for #ActiveRecord::ConnectionAdapters::PostgreSQL::SchemaCreation:0x0000000106d2e0e0

          foreign_key_in_create(o.name, to_table, options)
          ^^^^^^^^^^^^^^^^^^^^^

.rvm/gems/ruby-3.1.2/gems/torque-postgresql-3.0.1/lib/torque/postgresql/adapter/schema_creation.rb:23:in `block in visit_TableDefinition'

.rvm/gems/ruby-3.1.2/gems/torque-postgresql-3.0.1/lib/torque/postgresql/adapter/schema_creation.rb:22:in `map'

. rvm/gems/ruby-3.1.2/gems/torque-postgresql-3.0.1/lib/torque/postgresql/adapter/schema_creation.rb:22:in `visit_TableDefinition'

.rvm/gems/ruby-3.1.2/gems/activerecord-7.0.4/lib/active_record/connection_adapters/abstract/schema_creation.rb:13:in `accept'

.rvm/gems/ruby-3.1.2/gems/activerecord-7.0.4/lib/active_record/connection_adapters/abstract/schema_statements.rb:325:in `create_table'

.rvm/gems/ruby-3.1.2/gems/torque-postgresql-3.0.1/lib/torque/postgresql/adapter/schema_statements.rb:70:in `create_table'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.