Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Não é possível selecionar onde ip=inet_pton($ip)


Primeiro a correção, que é bem simples:Se você quiser armazenar ambos, endereços IPv4 e IPv6, você deve usar VARBINARY(16) em vez de BINARY(16) .

Agora para o problema:Por que não funciona como esperado com BINARY(16) ?

Considere que temos uma tabela ips com apenas uma coluna ip BINARY(16) PRIMARY KEY .Armazenamos o endereço IPv4 local padrão com
$stmt = $db->prepare("INSERT INTO ips(ip) VALUES(?)");
$stmt->execute([inet_pton('127.0.0.1')]);

e encontre o seguinte valor no banco de dados:
0x7F000001000000000000000000000000

Como você vê - é um valor binário de 4 bytes (0x7F000001 ) preenchido à direita com zeros para caber na coluna de comprimento fixo de 16 bytes.

Quando você agora tenta encontrá-lo com
$stmt = $db->prepare("SELECT * FROM ips WHERE ip = ?");
$stmt->execute([inet_pton('127.0.0.1')]);

acontece o seguinte:PHP envia o valor 0x7F000001 como parâmetro que é comparado com o valor armazenado 0x7F000001000000000000000000000000 .Mas como dois valores binários de comprimento diferente nunca são iguais, a condição WHERE sempre retornará FALSE.Você pode tentar com
SELECT 0x00 = 0x0000

que retornará 0 (FALSO).

Nota:O comportamento é diferente para strings não binárias de comprimento fixo (CHAR(N) ).

Poderíamos usar a conversão explícita como solução alternativa:
$stmt = $db->prepare("SELECT * FROM ips WHERE ip = CAST(? as BINARY(16))");
$stmt->execute([inet_pton('127.0.0.1')]);

e ele encontrará a linha. Mas se olharmos para o que temos
var_dump(inet_ntop($stmt->fetch(PDO::FETCH_OBJ)->ip));

Vamos ver
string(8) "7f00:1::"

Mas isso não é (realmente) o que tentamos armazenar. E quando agora tentamos armazenar 7f00:1:: , receberemos um erro de chave duplicada , embora nunca tenhamos armazenado nenhum endereço IPv6 ainda.

Então, mais uma vez:Use VARBINARY(16) , e você pode manter seu código intacto. Você ainda economizará algum armazenamento, se armazenar muitos endereços IPv4.