postgresql - 如何使用 Eloquent 和 PostgreSQL 将 IP 地址保存为二进制文件?

标签 postgresql laravel laravel-4 eloquent

首先,这是我获得信息的 SO 问答 - laravel 4 saving ip address to model .

所以我的表可能有数百万行,因此为了保持低存储,我选择了选项 2 - 使用架构构建器的 binary() 列并在 Eloquents 的访问器/修改器的帮助下将 IP 转换/存储为二进制。

这是我的 table :

Schema::create('logs', function ( Blueprint $table ) {
    $table->increments('id');
    $table->binary('ip_address'); // postgresql reports this column as BYTEA
    $table->text('route');
    $table->text('user_agent');
    $table->timestamp('created_at');
});

我遇到的第一个问题是保存 IP 地址。我在我的模型上设置了一个访问器/修改器,使用 inet_pton()inet_ntop() 将 IP 字符串转换为二进制。示例:

public function getIpAddressAttribute( $ip )
{
    return inet_ntop( $ip );
}

public function setIpAddressAttribute( $ip )
{
    $this->attributes['ip_address'] = inet_pton( $ip );
}

尝试保存 IP 地址导致整个请求失败 - nginx 只会返回 502 bad gateway 错误。

好的。所以我想一定是 Eloquent/PostgreSQL 在传递二进制数据时不能很好地协同工作。

我做了一些搜索,找到了 pg_escape_bytea()pg_unescape_bytea() 函数。我按如下方式更新了我的模型:

public function getIpAddressAttribute( $ip )
{
    return inet_ntop(pg_unescape_bytea( $ip ));
}

public function setIpAddressAttribute( $ip )
{
    $this->attributes['ip_address'] = pg_escape_bytea(inet_pton( $ip ));
}

现在,我可以毫不费力地保存 IP 地址(至少,它不会抛出任何错误)。

我遇到的新问题是当我尝试检索和显示 IP 时。 pg_unescape_bytea() 失败,pg_unescape_bytea() expects parameter 1 to be string, resource given

奇怪。所以我在访问器中dd() $ip,结果是resource(4, stream)。这是预期的吗?或者 Eloquent 在处理列类型时遇到问题?

我做了更多搜索,发现 pg_unescape_bytea() 可能没有正确地转义数据 - https://bugs.php.net/bug.php?id=45964 .

经过多次反复讨论后,很明显我可能从错误的方向处理这个问题,需要一些新的视角。

那么,我做错了什么?我是否应该通过更改列类型来使用 Postgres 的 BIT VARYING 而不是 BYTEA --

DB::statement("ALTER TABLE logs ALTER COLUMN ip_address TYPE BIT VARYING(16) USING CAST(ip_address AS BIT VARYING(16))");`

-- 或者我只是误用了 pg_escape_bytea/pg_unescape_bytea

感谢所有帮助!

最佳答案

就像在对您的问题的评论中已经说过的那样:在您的特定情况下,您应该使用相应的 PostgreSQL 数据类型,处理起来会容易得多。与 MySQL 相比,PostgreSQL 中还有很多其他类型(如 JSON),有一个 PostgreSQL data type overview page以供进一步引用。

也就是说,其他人可能会偶然发现 bytea 字段的类似问题。你得到 Resource 而不是 string 的原因是 PostgreSQL 将 bytea 字段视为流。一种非常幼稚的方法是首先获取流然后返回数据:

public function getDataAttribute($value)
{
    // This will kill your server under high load with large values.
    $data =  fgets($value);

    return pg_unescape_bytea($data);
}

您可以想象,如果多个人试图获取大文件(目前为数百 MiB 或几 GiB),而大型数据对象需要服务器上的大量内存(这甚至可能导致没有交换的移动设备上的问题)。在这种情况下,您应该在服务器和客户端上使用流,并只在您真正需要的客户端上获取数据。

关于postgresql - 如何使用 Eloquent 和 PostgreSQL 将 IP 地址保存为二进制文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29245221/

相关文章:

postgresql - 基准 Amazon Redshift JSON_EXTRACT_PATH_TEXT

php - 对相关模型进行分页

php - 如何让我在 Heroku 上的 Laravel 项目使用 Heroku Postgres?

php - 为什么 laravel 查询不返回正确的结果?

php - Laravel Eager Loading - 只加载特定的列

php - Laravel 4 如何加载延迟提供者?

mysql - 将 drupal 数据库从 mysql 转换为 postgresql?

python - 使用 psycopg2 更新 JSONB

json - PostgreSQL:比较 json

php - Laravel 5.2 facade getFacadeAccessor 返回的内容