Home
Arjen's Journal - Storing an IP address in a database table
Open Query: MySQL, Open Source & other ponderings

Arjen Lentz
Date: 2005-10-03 08:38
Subject: Storing an IP address in a database table
Security: Public

Say you have an IP address, 192.168.0.10 and want to store that in a database table. You could of course store it in a CHAR(15) and that is in fact what many people do. But you probably want to search on this field and therefore want it indexed also. So can we do better than using a 15 byte character field? We sure can.

MySQL has two built-in functions: INET_ATON() and INET_NTOA(). They are actually based on the equivalent inet_aton() and inet_ntoa() which are C library functions present on pretty much every TCP/IP capable system. Why? These two functions are used allover the place in any TCP/IP stack implementation or even application.
The INET_ATON() function converts Internet addresses from the numbers-and-dots notation into a 32-bit unsigned integer, and INET_NTOA() does the opposite. Isn't that handy!

Let's put it to the test:

mysql> SELECT INET_ATON('192.168.0.10') AS ipn;
+------------+
| ipn        |
+------------+
| 3232235530 |
+------------+

mysql> SELECT INET_NTOA(3232235530) AS ipa;
+--------------+
| ipa          |
+--------------+
| 192.168.0.10 |
+--------------+
So you can store an IP address in an INT UNSIGNED (4 bytes) which is of course much more efficient and faster than a CHAR(15). Naturally, you can call the function while you're inserting, so something like this is fine also:
INSERT INTO tbl VALUES (..., INET_ATON('192.168.0.10'), ...)
In MySQL 5.0, you can even do this transformation inside a LOAD DATA INFILE command, without using temporary columns:
LOAD DATA INFILE 'filename'
INTO TABLE tbl
...
(col1, ..., @ipa1, ..., coln)
SET ipn = INET_ATON(@ipa);
So in the list of columns you assign this column to a server-side variable, and then assign the transformed value to the proper column in the SET clause. Quite elegant, really.

Post A Comment | 19 Comments | Add to Memories | Tell a Friend | Link



User: (Anonymous)
Date: 2006-01-28 22:46 (UTC)
Subject: (no subject)

Should ATON_ATON be replaced with INET_ATON ?
And what about 'ipn'? Where is it used?

Reply | Thread | Link



Arjen Lentz
User: [info]arjen_lentz
Date: 2006-01-29 16:28 (UTC)
Subject: (no subject)

INET_ATON() typo fixed, thanks for spotting that.

ipn is just used to show how the short form is represented (i.e., a single 32-bit unsigned integer value). Try and see what my story means, don't try to copy the statements exactly. They are just an example, you need to understand the concept and apply it to your situation.

Reply | Parent | Thread | Link



User: (Anonymous)
Date: 2006-03-27 22:41 (UTC)
Subject: PostgreSQL network address types

In PostgreSQL there is a native network address type and MAC address data type:

http://www.postgresql.org/docs/8.1/interactive/datatype-net-types.html

Then there are functions and operators for them:

http://www.postgresql.org/docs/8.1/interactive/functions-net.html

--
Joseph Scott
http://joseph.randomnetworks.com/

Reply | Thread | Link



Arjen Lentz
User: [info]arjen_lentz
Date: 2006-03-29 16:38 (UTC)
Subject: Re: PostgreSQL network address types

Thanks. But those types are mostly used for input validation, not efficient storage. Reading that documentation page, one sees that an IPv4 address is stored in 12. The above solution in MySQL uses 4 bytes. This is clearly more efficient in storage, less resource use for indexing, and increased performance for handling (lookups, etc).

Reply | Parent | Thread | Link



User: (Anonymous)
Date: 2006-05-02 07:58 (UTC)
Subject: Re: PostgreSQL network address types

They are stored in 12 because they can store IPv6 addresses as well. This is a big deal in many cases. In addition, the indexes support all sorts of fun queries, such as "inside netblock" and things like that in a pretty efficient manner.

Reply | Parent | Thread | Link



User: (Anonymous)
Date: 2007-03-02 19:20 (UTC)
Subject: Re: PostgreSQL network address types

IPv6 uses 128 bit addresses, equal to 16 octets.

I do like the idea, though, of the index supporting natural queries with regard to the data type. Currently an indexed date field would not utilize the index if one selects YEAR(datefield) and the same goes to LEFT(textfield,2).

It is possible to workaround using BETWEEN (or just > and < ) and LIKE in the two cases. But it might not seem intuitive or add to readability.

YEAR(datefield) = 2007
would be more self-explaining than
datefield BETWEEN '2007-01-01 00:00:00' AND '2007-12-31 23:59:59'

- Peter Brodersen

Reply | Parent | Thread | Link



Arjen Lentz
User: [info]arjen_lentz
Date: 2007-03-05 05:32 (UTC)
Subject: Re: PostgreSQL network address types

The latter could be internally fixed up by the optimizer, without the user having to worry about it.
Of course it would need to detect all usable cases to deal with them appropriately.

Reply | Parent | Thread | Link



User: (Anonymous)
Date: 2007-08-06 12:32 (UTC)
Subject: Re: PostgreSQL network address types

Hardly as elegant as the PostgreSQL solution though.

Reply | Parent | Thread | Link



Arjen Lentz
User: [info]arjen_lentz
Date: 2007-08-06 13:12 (UTC)
Subject: Re: PostgreSQL network address types

Elegant comes in many ways. I'll concur that PostgreSQL makes it look nice from the user perspective. But if you're only dealing with IPv4, you don't want to use more than 4 bytes for an address. The additional space costs you memory, disk, and I/O which you may not be able to afford - depending on the application.

I'm all for having specific data types for commonly used domains, and IPV4 and IPV6 (each individually) definitely fall into that category.

Reply | Parent | Thread | Link



User: (Anonymous)
Date: 2006-08-07 22:17 (UTC)
Subject: (no subject)

You just saved me untold hours of angst.
Thank you. Thank you. Thank you.

Reply | Thread | Link



User: (Anonymous)
Date: 2007-03-14 15:39 (UTC)
Subject: MySQL: Storing IP Addresses in INET format

Thanks, found you via google.

Reply | Parent | Thread | Link



User: (Anonymous)
Date: 2007-05-14 03:17 (UTC)
Subject: Using MySQL C API

Hi,

my C code goes like this...
sprintf(query, "INSERT INTO PeerInfo(IPAddress, Port, Active, Counter, Priority) VALUES('%d', '%d', '%c', '%d', '%c'", inet_aton(addr->sin_addr.s_addr), addr->sin_port, active, counter, priority);

where IPAddress is unsigned int and Port is unsigned short.I am getting this error "conversion from `in_addr_t' to non-scalar type `in_addr' requested".

can some body correct this plz.

Reply | Thread | Link



Arjen Lentz
User: [info]arjen_lentz
Date: 2007-05-14 03:28 (UTC)
Subject: Re: Using MySQL C API

You'll want to use %u and cast to a uint32 or something.

Reply | Parent | Thread | Link



User: (Anonymous)
Date: 2007-05-14 05:17 (UTC)
Subject: Re: Using MySQL C API

The Problem here is i want to store the IP address as an integer in the table.when i am trying to convert it from addr->sin_addr to integer,it is giving error.
when i give the query like this..
insert into PeerInfo values(inet_aton("211.38.137.33")); the value in the table looks like this 3542518049.

but when i try to write the same in the query,its giving an error.

Reply | Parent | Thread | Link



Arjen Lentz
User: [info]arjen_lentz
Date: 2007-05-14 05:55 (UTC)
Subject: Re: Using MySQL C API

Well, "it" would be your C compiler, this has nothing to do with MySQL or the MySQL client API. It's pure C coding stuff so that's where that problem needs to be solved, too.

And indeed, doing it in SQL is what this original posting was about. It works fine too.

Reply | Parent | Thread | Link



User: (Anonymous)
Date: 2007-08-27 08:30 (UTC)
Subject: Re: Using MySQL C API

if u can tell how to store ip addr range also.

Reply | Parent | Thread | Link



Arjen Lentz
User: [info]arjen_lentz
Date: 2008-02-20 23:21 (UTC)
Subject: Re: Using MySQL C API

Store the low and high, then the server can find it using a range scan in its index.
If you work using a mask, it may not be able to use an index, although there might be some tricks available to work around that.

Reply | Parent | Thread | Link



User: (Anonymous)
Date: 2008-07-10 23:43 (UTC)
Subject: Re: Using MySQL C API

netmask operations are easy, they are based on bitwise operations.

if you have ip4 address a.b.c.d as an integer abcd
and netmask m.n.o.p as integer mnop

the first address is written in c as (abcd & mnop) and the last as (abcd | ^ mnop )

unlike the C version of inet_aton the mysql version transforms the result to host byte order, so betweeen can be used with sensible results

so in mysql, to see if addr in in the subnet described by
a.b.c.d and m.n.o.p you can say

addr between ( abcd & mnop ) and ( abcd | ~ mnop & 0xfffffff)

and get meaningful results.

Reply | Parent | Thread | Link



Arjen Lentz
User: [info]arjen_lentz
Date: 2008-07-11 00:16 (UTC)
Subject: Re: Using MySQL C API

That's exactly the same as what the C version does. In fact MySQL calls the C function, as far as I know.

Doing the bitwise operations on the network ordered address is fine, however for searches you don't want to do that as it would make using an index lookup for the search impossible. In the best case it might use an index scan rather than a full table scan.

Reply | Parent | Thread | Link



browse
my journal
links
July 2008
High Performance MySQL (2nd ed.)
summary