如何在 Rails 中的数据库连接上执行查询?

发布于 2024-08-26 18:49:48 字数 1517 浏览 2 评论 0原文

我有一些初始化函数,用于在 PostgreSQL 的数据库服务器端(即不是 Rails)设置审核日志记录。在将数据插入或更新任何审核表之前,至少必须发出一个命令(设置当前用户),否则整个查询将彻底失败。

每次在代码中运行任何保存操作之前我都可以轻松地调用这些函数,但 DRY 让我觉得我应该在尽可能少的地方重复代码,特别是因为这与数据库不可知论的理想有很大的不同。目前,我正在尝试在初始值设定项中覆盖 ActiveRecord::Base.assessment_connection 来设置它,以便在自动连接后立即运行查询,但它的行为并不符合我的预期。这是初始化程序中的代码:

class ActiveRecord::Base
  # extend the class methods, not the instance methods
  class << self
    alias :old_establish_connection :establish_connection # hide the default

    def establish_connection(*args)
      ret = old_establish_connection(*args) # call the default

      # set up necessary session variables for audit logging
      # call these after calling default, to make sure conn is established 1st
      db = self.class.connection
      db.execute("SELECT SV.set('current_user', 'test@localhost')")
      db.execute("SELECT SV.set('audit_notes', NULL)") # end "empty variable" err

      ret # return the default's original value
    end
  end
end

puts "Loaded custom establish_connection into ActiveRecord::Base"
sycobuny:~/rails$ ruby script/server 
=> Booting WEBrick
=> Rails 2.3.5 application starting on http://0.0.0.0:3000
Loaded custom establish_connection into ActiveRecord::Base

这不会给我任何错误,不幸的是我无法检查该方法在内部是什么样子(我使用的是 ActiveRecord::Base.method(:assessment_connection),但显然这创建了一个每次调用时都会创建 new Method 对象,这似乎毫无价值,因为我无法检查 object_id 是否有任何有价值的信息,而且我也无法反转编译)。

但是,该代码似乎从未被调用,因为正如我之前预测的那样,对数据库对象运行保存或更新的任何尝试都会失败。如果这不是在连接到数据库时立即执行代码的正确方法,那么什么是呢?

I have certain initializing functions that I use to set up audit logging on the DB server side (ie, not rails) in PostgreSQL. At least one has to be issued (setting the current user) before inserting data into or updating any of the audited tables, or else the whole query will fail spectacularly.

I can easily call these every time before running any save operation in the code, but DRY makes me think I should have the code repeated in as few places as possible, particularly since this diverges greatly from the ideal of database agnosticism. Currently I'm attempting to override ActiveRecord::Base.establish_connection in an initializer to set it up so that the queries are run as soon as I connect automatically, but it doesn't behave as I expect it to. Here is the code in the initializer:

class ActiveRecord::Base
  # extend the class methods, not the instance methods
  class << self
    alias :old_establish_connection :establish_connection # hide the default

    def establish_connection(*args)
      ret = old_establish_connection(*args) # call the default

      # set up necessary session variables for audit logging
      # call these after calling default, to make sure conn is established 1st
      db = self.class.connection
      db.execute("SELECT SV.set('current_user', 'test@localhost')")
      db.execute("SELECT SV.set('audit_notes', NULL)") # end "empty variable" err

      ret # return the default's original value
    end
  end
end

puts "Loaded custom establish_connection into ActiveRecord::Base"
sycobuny:~/rails$ ruby script/server 
=> Booting WEBrick
=> Rails 2.3.5 application starting on http://0.0.0.0:3000
Loaded custom establish_connection into ActiveRecord::Base

This doesn't give me any errors, and unfortunately I can't check what the method looks like internally (I was using ActiveRecord::Base.method(:establish_connection), but apparently that creates a new Method object each time it's called, which is seemingly worthless cause I can't check object_id for any worthwhile information and I also can't reverse the compilation).

However, the code never seems to get called, because any attempt to run a save or an update on a database object fails as I predicted earlier. If this isn't a proper way to execute code immediately on connection to the database, then what is?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

情感失落者 2024-09-02 18:49:48

Rails 使用连接池,因此最好的选择是对 ActiveRecord::ConnectionAdapters::ConnectionPool#new_connection 使用 alias_method_chain

module ActiveRecord
  Module ConnectionAdapters
    class ConnectionPool
      alias_method_chain :new_connection, :my_stuff
      private
      def new_connection_with_my_stuff
        c = new_connection_without_my_stuff
        # Do your stuff with 
        # c.exec(<your sql command>)
        c
      end
    end
  end
end

Rails uses connection pooling, so your best bet is to use an alias_method_chain for ActiveRecord::ConnectionAdapters::ConnectionPool#new_connection

module ActiveRecord
  Module ConnectionAdapters
    class ConnectionPool
      alias_method_chain :new_connection, :my_stuff
      private
      def new_connection_with_my_stuff
        c = new_connection_without_my_stuff
        # Do your stuff with 
        # c.exec(<your sql command>)
        c
      end
    end
  end
end
旧伤慢歌 2024-09-02 18:49:48

您是否知道在建立数据库连接之前正在调用您的初始化程序?

如果没有关于可能出现问题的权威信息,我会利用 ActiveRecord 源位于我的硬盘上这一事实。

深入研究基类并进行一些调试,以查看相对于调用初始化程序的时间,ActiveRecord建立连接的调用时间。要么打开额外的详细调试。

Do you know that your initializer is being called before the database connection is established?

Without authoritative information on what could be wrong, I'd take advantage of the fact that the source to ActiveRecord is on my hard drive.

Dig into the base class and do a little puts debugging to see when ActiveRecord establish_connection is being called relative to when your initializer is called. Either that or just turn on extra verbose debugging.

眼角的笑意。 2024-09-02 18:49:48

在编写代码来挂钩 Rails 数据库调用和初始化方面,您可能需要使用 db-charmer 中的代码 作为模板。它演示了许多与 ActiveRecord 交互的有趣方法,其中一种可能可以满足您的需求。

In terms of writing code to hook rails database calls and initializations, you might want to use the code from db-charmer as a template. It demonstrates a number of interesting ways of interacting with ActiveRecord and one of them will probably do what you want.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文