Mysql
 sql >> Database >  >> RDS >> Mysql

Impossibile selezionare dove ip=inet_pton($ip)

Prima la correzione, che è abbastanza semplice:se vuoi memorizzare entrambi gli indirizzi IPv4 e IPv6, dovresti usare VARBINARY(16) invece di BINARY(16) .

Ora il problema:perché non funziona come previsto con BINARY(16) ?

Considera che abbiamo una tabella ips con una sola colonna ip BINARY(16) PRIMARY KEY .Memorizziamo l'indirizzo IPv4 locale predefinito con

$stmt = $db->prepare("INSERT INTO ips(ip) VALUES(?)");
$stmt->execute([inet_pton('127.0.0.1')]);

e trova il seguente valore nel database:

0x7F000001000000000000000000000000

Come vedi:è un valore binario di 4 byte (0x7F000001 )composto a destra con zeri per adattarsi alla colonna a lunghezza fissa di 16 byte.

Quando ora provi a trovarlo con

$stmt = $db->prepare("SELECT * FROM ips WHERE ip = ?");
$stmt->execute([inet_pton('127.0.0.1')]);

accade quanto segue:PHP invia il valore 0x7F000001 come parametro che viene poi confrontato con il valore memorizzato 0x7F000001000000000000000000000000 .Ma poiché due valori binari di lunghezza diversa non sono mai uguali, la condizione WHERE restituirà sempre FALSE. Puoi provare con

SELECT 0x00 = 0x0000

che restituirà 0 (FALSO).

Nota:il comportamento è diverso per le stringhe non binarie di lunghezza fissa (CHAR(N) ).

Potremmo utilizzare il casting esplicito come soluzione alternativa:

$stmt = $db->prepare("SELECT * FROM ips WHERE ip = CAST(? as BINARY(16))");
$stmt->execute([inet_pton('127.0.0.1')]);

e troverà la riga. Ma se guardiamo cosa otteniamo

var_dump(inet_ntop($stmt->fetch(PDO::FETCH_OBJ)->ip));

vedremo

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

Ma non è (realmente) ciò che abbiamo cercato di memorizzare. E quando ora proviamo a memorizzare 7f00:1:: , verrà visualizzato un errore chiave duplicata , anche se non abbiamo ancora memorizzato alcun indirizzo IPv6.

Quindi ancora una volta:usa VARBINARY(16) e puoi mantenere intatto il tuo codice. Risparmierai persino spazio di archiviazione, se memorizzi molti indirizzi IPv4.