【问题标题】:Insert a database dump with ruby-pg使用 ruby​​-pg 插入数据库转储
【发布时间】:2012-07-12 17:03:44
【问题描述】:

我有一个包含各种插入语句和偶尔的“\connect”命令的数据库转储文件

问题是PG::Connection.exec() 不接受psql 的\-commands,如\i\c 等。

有没有办法使用 ruby​​-pg(或者可能是另一个 pg-gem)来完成这项工作?

由于数据库位于不同的主机上,我无法使用 Unix 套接字,因此无法在没有密码的情况下以用户 postgres 的身份连接。

【问题讨论】:

  • 我的回答是直接针对这个问题,但我必须问一个更哲学的问题:如果您无法连接到数据库以加载脚本并且显然无法安排由可以完成的人来完成, 应该你一开始就这样做。如果您无法在本地登录并且您不知道密码,我认为您不应该这样做。也许这是您的 DBA 的工作(或者您需要获得更好的 DBA)。请不要误认为这对我来说很困难,但是应该使用 psql 加载脚本。为什么你不能这样做是管理问题,而不是技术问题。
  • 有点不同。我是 dba 和 unix 管理员。事情是我必须为那些没有经验的客户提供工具。我喜欢用 shell 编写的一百万种工具,它们让我的生活更轻松。包括用于故障转移和恢复一大堆 unix 系统和数据库的整个套件。所以我希望我认为我知道我在做什么;)我目前正在将大部分 shell 内容迁移到 ruby​​,因为我希望它可测试并且我希望它更易于维护。我在 shell 中做了一些曲折,我希望在 ruby​​ 中不必这样做。包括重度嵌套的 cmd。
  • 另一件事,请不要误会我的意思。我觉得我必须在这里解释一下自己。自从机器首次进入数据中心以来,我一直在维护这个系统(现在有 20 个,正在进行一些漂亮的复制工作)。我是对系统了如指掌的人,我只是想让我的生活更轻松。是的,它是一种技术。问题。我只是太懒了,我的生命太短了,不能每天手工做所有的事情。信不信由你,我可以在 8 小时后下班,因为这些东西是自动化的并且可以正常工作。为尽可能有效地做事而提出愚蠢的问题而感到抱歉

标签: ruby postgresql dump psql


【解决方案1】:

使用像pg_restore这样的psql命令行工具可能更容易:

pg_restore -d newdb db.dump

如果您对托管原始数据库的框具有 ssh 访问权限并且知道需要转储哪个表,则可以 ssh 进入该框并将pg_dump 输出直接通过管道传输到您的本地数据库。比如:

ssh user@original_database pg_dump -U remote_user_name -T schema_name.table_name mydb | psql -U local_user_name -d local_database

【讨论】:

  • 是的,但我希望避免这种情况。如上所述,我已经使用了在我迁移 ruby​​ 的 shell 脚本套件中难以维护的大量嵌套命令,原因有很多。我有点想过(作为最后的手段)使用 Rye 和管道通过那里或 Open3 的混合物。但我希望它会比这更容易。我希望有一个能够做到这一点的 pg gem ^^
【解决方案2】:

答案是“否”,因为反斜杠命令不是 Postgres 命令。它们是 psql 命令,是默认的命令行客户端。没有第三方工具会复制这些命令,因为它们不是 libpq API 的一部分。

现在,也就是说,\connect 是文件中唯一的反斜杠命令吗?如果是这样,那么您应该能够通过从 \connect 语句中解析出数据库名称来实现这一点,然后从每个 \connect 语句之后的第一行开始将转储文件拆分为块。此时,只需针对该块的 \connect 语句中给定的数据库连接正常运行每个块。

据我所知,如果不创建新连接,就无法重新连接到不同的数据库,因为必须重新验证用户的凭据。这就是 psql 在幕后的工作方式。

还有另一种想法,如果只有一个 \connect 语句,并且它紧接在 CREATE DATABASE 语句之后,紧接在 CREATE ROLE 等语句之后,那么如果您将数据库创建为单独的步骤并删除 CREATE DATABASE 和 \连接文件中的语句,您可以在不分块的情况下执行此操作。请记住,您仍然需要两个连接,因为在创建数据库之前您无法连接到数据库,并且在连接到新数据库的过程中仍然无法更改。

【讨论】:

  • 这一切都让人不满意。但是将 sql 分成两部分并这样做会更容易和更干净。建立两个(或更多)sql 连接不会有问题。它是(如上所述)在系统关闭时执行的回滚脚本的一部分。好吧,我永远不必使用我正在迁移到 ruby​​ 的 shell,也永远不会希望在 ruby​​ 迁移之后必须使用它。但最好为那一刻做好准备。我会考虑这个解决方案,而不是那些太“shellscripty”并且我已经使用过的解决方案。好的,他们工作得很好。但是维护起来很麻烦。
【解决方案3】:

在尝试了很多东西之后,包括在 \connect 行拆分文件等,我最终暂时使用“老式 shell 脚本方式”......这是我想要避免的方式。但我必须去某个地方

我现在正在做的事情如下:

  insert_command = "cat #{dump_file_path} | " +
        "ssh -p #{port} -i #{key} #{ssh_user}@#{db_host} " +
        "\"su - postgres -c 'cat > /tmp/rb_dump.sql && psql -f /tmp/rb_dump.sql ; rm /tmp/rb_dump.sql'\""

    Open3.popen3 insert_command do |stdin, stdout, stderr, t|
      unless t.value.to_s.include? "exit 0"
        <handle error>
      end
    end

在我看来这完全是 hacky,但它确实有效 ^^

我的另一个解决方案是下一个代码,它有一个大问题(如下所述)

 def insert_database_dump(dump_file_handle, host, user, password, database = nil)
    begin
      pg = PG.connect(host: host, user: user, password: password, dbname: database)
      until dump_file_handle.eof?
        line = dump_file_handle.readline(";")
        if line =~ /connect\s(\w*)/
          line.gsub!("\\connect", "").strip!
          insert_database_dump(dump_file_handle, host, user, password, line)
        else
          pg.exec(line)
        end
      end
    ensure
      pg.finish unless pg.finished?
    end
  end

代码执行以下操作(仅重要部分):

  • 从 sql 中读取一行,直到 ";" (查询被分成几行,所以在换行之前阅读是行不通的
  • 执行查询
  • 如果该行看起来像“connect word”,则选择“word”作为数据库名称,并使用“word”作为数据库名称再次调用该方法,并且当前文件句柄指向正确的行我想继续。

尽管方法可能有更优雅的方式来调用自身,但除非 sql 转储中存在 cmets,否则此方法有效。 问题是 cmets 看起来像这样:

-- foo: bar; bam: baz;

所以当用 ";" 分割时,它会执行 "-- foo: bar;",因为它是一个注释,但由于 "bam: baz;" 而失败

我目前没有简单的解决方案来即时删除注释行。所以 Open3 解决方案将不得不做,直到我发现为止。

干杯, 斯蒂芬

【讨论】:

    猜你喜欢
    • 2012-06-02
    • 1970-01-01
    • 2015-05-01
    • 2017-11-22
    • 2014-10-12
    • 2017-02-10
    • 2011-10-25
    • 1970-01-01
    • 2021-04-07
    相关资源
    最近更新 更多