diff -ruN postgresql-jdbc-8.1dev-403.src/org/postgresql/jdbc2/AbstractJdbc2Connection.java postgresql-8.1dev-403-inet-src/org/postgresql/jdbc2/AbstractJdbc2Connection.java --- postgresql-jdbc-8.1dev-403.src/org/postgresql/jdbc2/AbstractJdbc2Connection.java Mon Aug 1 02:54:14 2005 +++ postgresql-8.1dev-403-inet-src/org/postgresql/jdbc2/AbstractJdbc2Connection.java Wed Nov 2 21:08:07 2005 @@ -443,6 +443,9 @@ addDataType("polygon", org.postgresql.geometric.PGpolygon.class); addDataType("money", org.postgresql.util.PGmoney.class); addDataType("interval", org.postgresql.util.PGInterval.class); + addDataType("cidr", org.postgresql.net.PGcidr.class); + addDataType("inet", org.postgresql.net.PGinet.class); + addDataType("macaddr", org.postgresql.net.PGmacaddr.class); for (Enumeration e = info.propertyNames(); e.hasMoreElements(); ) { diff -ruN postgresql-jdbc-8.1dev-403.src/org/postgresql/net/PGNetworkBase.java postgresql-8.1dev-403-inet-src/org/postgresql/net/PGNetworkBase.java --- postgresql-jdbc-8.1dev-403.src/org/postgresql/net/PGNetworkBase.java Wed Dec 31 19:00:00 1969 +++ postgresql-8.1dev-403-inet-src/org/postgresql/net/PGNetworkBase.java Wed Nov 2 21:08:07 2005 @@ -0,0 +1,400 @@ +package org.postgresql.net; + +import java.io.Serializable; +import java.sql.SQLException; +import org.postgresql.util.GT; +import org.postgresql.util.PGobject; +import org.postgresql.util.PGtokenizer; +import org.postgresql.util.PSQLException; +import org.postgresql.util.PSQLState; + + +class PGNetworkBase extends PGobject implements Serializable, Cloneable +{ + protected byte[] addr; + protected int netmask; + protected boolean embedded_ipv4 = false; + + protected String addrString = null; + protected Integer hashCode = null; + + /** + * This will read an IPv4 address string in to the instance + * variable addr. + * + *

If there is a syntax error in the format of the host string + * false will be returned. + * + * @param host A host parameter in IPv4 dotted quad notation. + * @param allowShortNotation Whether we should accept addresses + * in the [a[.b[.c[.d]]] format or a.b.c.d format. + * @return true if this is an IPv4 host, false if not. + * If true is returned, this.addr will contain the valid + * bytes which make up this IPv4 address. + */ + protected boolean isIPv4Address( String host, boolean allowShortNotation ) + { + if( host == null ) + { + return( false ); + } + + PGtokenizer t = new PGtokenizer( host, '.' ); + int size = t.getSize(); + if( + ( ( allowShortNotation == false ) && ( size != 4 ) ) || + ( ( allowShortNotation == true ) && ( size < 1 || size > 4 ) ) ) + { + return( false ); + } + + // create space for the new ipv4 address and zero it out. + byte[] address = new byte[ 4 ]; + + try + { + for( int i = 0; i < size; ++i ) + { + String chunk = t.getToken( i ); + if( chunk.length() == 0 ) + { + return( false ); + } + + int v = Integer.parseInt( chunk ); + if( ( v < 0 ) || ( v > 255 ) ) + { + return( false ); + } + + address[i] = (byte)(v & 0xFF); + } + } + catch( NumberFormatException e ) + { + return( false ); + } + + // this is an ipv4 address. + this.addr = address; + this.hashCode = null; + this.addrString = null; + return( true ); + } + + /** + * This method will return true if this is a valid syntactically correct + * IPv6 address. It will also fill in the byte array member variable + * with the 16 byte values which make up the address. + * + * @param host The host portion of the address with the netmask stripped off. + * @return true if this is a valid IPv6 address, false if not. + */ + protected boolean isIPv6Address( String host ) + { + // Lets get some easy checks out of the way. + if( host == null ) + { + return( false ); + } + + // All IPv6 Addresses contain ':' + int index = host.indexOf( ":" ); + if( index == -1 ) + { + return( false ); + } + + // Lets see if we use the shorthand legally '::' can be used once and + // only once. + int shorthandIndex = host.indexOf( "::", index ); + if( shorthandIndex != -1 ) + { + // Ok, we use the IPv6 shorthand notation at least once. + // Lets look for another usage or ":::" which isn't valid. + if( host.indexOf( "::", shorthandIndex + 1 ) != -1 ) + { + // we either have ":::" or multiple uses of the shorthand, + // which isn't allowed. + return( false ); + } + } + + // ensure that if we start with a ':' there are two of them + // ensure that if we end with a ':', again, there are two of them. + if( ( host.endsWith( ":" ) && !host.endsWith( "::" ) ) || + ( host.startsWith( ":" ) && !host.startsWith( "::" ) ) ) + { + return( false ); + } + + // + // 0 1 2 3 4 5 1011 1213 1415 + // XXXX:XXXX:XXXX::YYYY:YYYY:YYYY + // + // i = 5, j = 10, n = 15 + // + // If the host uses shorthand notation, we will split host around "::" + // We will use the front-end (head) to fill elements of 0 -> i of address. + // We will use the tail-end (tail) to fill the elements j -> n on address. + // + + String head = host; + String tail = ""; + if( shorthandIndex != -1 ) + { + head = host.substring( 0, shorthandIndex ); + tail = host.substring( shorthandIndex + 2 ); + } + + // Ok, the host has passed all of our tests sofar, lets parse + // the host into tokens. + PGtokenizer headTokenizer = new PGtokenizer( head, ':' ); + int headSize = headTokenizer.getSize(); + PGtokenizer tailTokenizer = new PGtokenizer( tail, ':' ); + int tailSize = tailTokenizer.getSize(); + + // Lets allocate space for this potential ipv6 address. + byte[] address = new byte[ 16 ]; + + try + { + // Process the head. + int i = 0; + int k = 0; + for(;k < headSize; ++k ) + { + String chunk = headTokenizer.getToken( k ); + if( chunk.length() > 0 ) + { + // if i == 12, we may have an ipv4 address embedded as the last + // 32 bits of the address. + if( ( i == 12 ) && ( chunk.indexOf( '.' ) != -1 ) ) + { + if( isIPv4Address( chunk, false ) ) + { + System.arraycopy( this.addr, 0, address, 12, 4 ); + this.addr = null; + this.embedded_ipv4 = true; + } + } + else + { + if( i > 14 ) + return( false ); + int chunkValue = Integer.parseInt( chunk, 16 ); + address[ i++ ] = (byte)((chunkValue >> 8) & 0xFF ); + address[ i++ ] = (byte)(chunkValue & 0xFF); + } + } + } + + // Process the tail. + int j = 15; + k = 0; + for(;k < tailSize; ++k ) + { + String chunk = tailTokenizer.getToken( k ); + if( chunk.length() > 0 ) + { + if( ( j == 15 ) && ( chunk.indexOf( '.' ) != -1 ) ) + { + if( isIPv4Address( chunk, false ) ) + { + System.arraycopy( this.addr, 0, address, 12, 4 ); + this.addr = null; + this.embedded_ipv4 = true; + j = 11; + } + } + else + { + if( j < 1 ) + return( false ); + int chunkValue = Integer.parseInt( chunk, 16 ); + address[ j-- ] = (byte)(chunkValue & 0xFF ); + address[ j-- ] = (byte)((chunkValue >> 8) & 0xFF ); + } + } + } + + if( i - j > 1 ) + { + // Too many chunks in the host address + return( false ); + } + } + catch( NumberFormatException e ) + { + return( false ); + } + + this.addr = address; + this.hashCode = null; + this.addrString = null; + return( true ); + } + + /** + * This parameter takes a string representation of an inet address with + * optional netmask and returns the host portion without the netmask + * as a string. + * + * @param v The full network address with optional /netmask. addr/netmask + * @return The addr portion of the string with the optional netmask removed. + * @exception PSQLException This is thrown if the string v is not a valid + * network address. + */ + protected String getHostPortion( String v ) + throws PSQLException + { + PGtokenizer t = new PGtokenizer( v, '/' ); + + int size = t.getSize(); + if( ( size != 1 ) && ( size != 2 ) ) + { + throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.", + new Object[]{ type, v } ), PSQLState.DATA_TYPE_MISMATCH ) ); + } + + return( t.getToken( 0 ) ); + } + + /** + */ + protected int getNetmaskPortion( String v, int def ) + throws PSQLException + { + PGtokenizer t = new PGtokenizer( v, '/' ); + + int size = t.getSize(); + if( ( size != 1 ) && ( size != 2 ) ) + { + throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.", + new Object[]{ type, v } ), PSQLState.DATA_TYPE_MISMATCH ) ); + } + + int mask = def; + + if( size == 2 ) + { + try + { + // we have a netmask to read + mask = Integer.valueOf( t.getToken( 1 ) ).intValue(); + } + catch( NumberFormatException e ) + { + throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.", + new Object[]{ type, v } ), PSQLState.DATA_TYPE_MISMATCH, e ) ); + } + } + + return( mask ); + } + + /** + * Get the hash code for this network address. + * + * @return The hash value for this object. + */ + public int hashCode() + { + if( this.hashCode == null ) + { + // Seems like it will generate a decent hash? + int hashValue = this.netmask | (~this.netmask << 8) | ( this.netmask << 16) << (~this.netmask << 24); + for( int i = 0; i < this.addr.length; ++i ) + { + hashValue ^= this.addr[i] << ((i % 4) << 3); + } + this.hashCode = new Integer( hashValue ); + } + + return( this.hashCode.intValue() ); + } + + /** + * Returns the inet address in literal format. + * + * @return A string value of the inet address in literal format. + */ + public String getValue() + { + if( this.addrString == null ) + { + StringBuffer s = new StringBuffer(); + boolean isIPv4 = (this.addr.length == 4) ? true : false; + boolean isIPv6 = (this.addr.length == 16) ? true : false; + + for( int i = 0; i < this.addr.length; ++i ) + { + if( isIPv4 || ( ( i >= 12 ) && ( embedded_ipv4 == true ) ) ) + { + String digit = Integer.toString( ((int)this.addr[i]) & 0xFF ); + s.append( digit ); + if( ( isIPv4 && ( i < 3 ) ) || ( isIPv6 && ( i < 15 ) ) ) + { + s.append( "." ); + } + } + else + { + String hexDigit = Integer.toHexString( ((int)this.addr[i]) & 0xFF ); + if( hexDigit.length() == 1 ) + { + s.append( "0" ); + } + s.append( hexDigit ); + + if( ( (i % 2) == 1 ) && ( i != 15 ) ) + { + s.append( ":" ); + } + } + } + + if( ( ( this.addr.length == 16 ) && ( this.netmask < 128 ) ) || + ( ( this.addr.length == 4 ) && ( this.netmask < 32 ) ) ) + { + s.append( "/" + this.netmask ); + } + this.addrString = s.toString(); + } + + return( this.addrString ); + } + + /** + * This will return the netmask of the current network address. + * + * @return The netmask of the PGinet object. + */ + public int getNetmask() + { + return( this.netmask ); + } + + public boolean equals( Object obj ) + { + if( obj instanceof PGNetworkBase ) + { + PGNetworkBase inet = (PGNetworkBase)obj; + if( inet.netmask == this.netmask ) + { + if( inet.addr.length == this.addr.length ) + { + for( int i = 0; i < this.addr.length; ++i ) + { + if( inet.addr[ i ] != this.addr[ i ] ) + { + return( false ); + } + } + return( true ); + } + } + } + return( false ); + } +} diff -ruN postgresql-jdbc-8.1dev-403.src/org/postgresql/net/PGcidr.java postgresql-8.1dev-403-inet-src/org/postgresql/net/PGcidr.java --- postgresql-jdbc-8.1dev-403.src/org/postgresql/net/PGcidr.java Wed Dec 31 19:00:00 1969 +++ postgresql-8.1dev-403-inet-src/org/postgresql/net/PGcidr.java Wed Nov 2 21:08:07 2005 @@ -0,0 +1,280 @@ +/*------------------------------------------------------------------------- +* +* Copyright (C) 2005, PostgreSQL Global Development Group +* +*-------------------------------------------------------------------------- +*/ +package org.postgresql.net; + +import java.io.Serializable; +import java.sql.SQLException; +import org.postgresql.util.GT; +import org.postgresql.util.PGobject; +import org.postgresql.util.PGtokenizer; +import org.postgresql.util.PSQLException; +import org.postgresql.util.PSQLState; + +/** + * This represents org.postgresql's cidr datatype, which is + * used to hold both IPV4 & IPV6 network addresses. + * + * @author Russell Francis < rfrancis@ev.net > + */ +public class PGcidr extends PGNetworkBase implements Serializable, Cloneable +{ + /** + * This method will accept an IPv4 or IPv6 network address + * in any of the following formats. + * + *

+ * + * @param s The representation of the cidr as a string. + * @exception SQLException if the string is not in the proper format. + */ + public PGcidr( String s ) + throws SQLException + { + this(); + setValue( s ); + } + + public PGcidr() + { + setType( "cidr" ); + } + + /** + * Compare two PGcidr objects for equality. + * + *

This will return true if the parameter obj is of type PGcidr + * and represents the same network as this. + * + * @param obj The object which we wish to compare. + * @return true if it represents the same network as this, false otherwise. + */ + public boolean equals( Object obj ) + { + if( obj instanceof PGcidr ) + { + return( super.equals( obj ) ); + } + return( false ); + } + + /** + * This will make a duplicate of the current PGcidr object. + * + * @return null on failure, or a new PGcidr object which + * represents the same network address as the invoking + * object. + */ + public Object clone() + { + try + { + return( new PGcidr( this.getValue() ) ); + } + catch( SQLException e ) + { + return( null ); + } + } + + /** + * Set the value of this CIDR. + * + *

For IPv4 network addresses, this method will accept strings in the + * following format a[.b[.c[.d]]][/netmask] where a, b, c, d are + * integers in the range of 0 to 255 and netmask can be an integer + * from 0 to 32. + * + *

IPv6 networks must be entered as a complete IP address as + * defined in [RFC 2373] or [RFC 2732] optionally followed by + * a netmask which can be in the range of 0 - 128. + * + *

For either IPv4 or IPv6, it is illegal for the host portion + * of the network address to contain anything but zero values. + * + * @param v The string representation of this network address. + * @exception SQLException If it is not in a valid cidr format. + */ + public void setValue( String v ) + throws SQLException + { + if( v == null ) + { + throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.", + new Object[]{ type, v } ), PSQLState.DATA_TYPE_MISMATCH ) ); + } + + String host = this.getHostPortion( v ); + + if( isIPv6Address( host ) ) + { + this.netmask = this.getMinimalNetmask( v ); + if( ( this.netmask < 0 ) || ( this.netmask > 128 ) ) + { + this.netmask = 0; + this.addr = null; + throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.", + new Object[]{ type, v } ), PSQLState.DATA_TYPE_MISMATCH ) ); + } + } + else if( isIPv4Address( host, true ) ) // allow shortened ipv4 notation + { + this.netmask = this.getMinimalNetmask( v ); + if( ( this.netmask < 0 ) || ( this.netmask > 32 ) ) + { + this.netmask = 0; + this.addr = null; + throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.", + new Object[]{ type, v } ), PSQLState.DATA_TYPE_MISMATCH ) ); + } + } + else + { + throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.", + new Object[]{ type, v } ), PSQLState.DATA_TYPE_MISMATCH ) ); + } + + ensureHostBitsAreZero(); + } + + /** + * This method will get the netmask for a given address. + * + * @param v is the full address passed to setValue we will extract the netmask + * if it exists, if not, we will return a good guess. + */ + protected int getMinimalNetmask( String v ) + throws PSQLException + { + PGtokenizer t = new PGtokenizer( v, '/' ); + int size = t.getSize(); + if( ( size != 1 ) && ( size != 2 ) ) + { + throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.", + new Object[]{ type, v } ), PSQLState.DATA_TYPE_MISMATCH ) ); + } + + int mask; + + if( size == 2 ) + { + // If a netmask is specified, read and use it. + try + { + // we have a netmask to read + mask = Integer.valueOf( t.getToken( 1 ) ).intValue(); + } + catch( NumberFormatException e ) + { + throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.", + new Object[]{ type, v } ), PSQLState.DATA_TYPE_MISMATCH, e ) ); + } + } + else + { + mask = this.addr.length * 8; + if( mask == 32 ) // IPv4, IPv6 addresses use 128 if none is specified. + { + // If no netmask is specified, we use the smallest netmask that + // will include all of the non-zero bits of the address in 8 bit + // blocks. + int a = this.addr[0] & 0xFF; // cast byte to int and force range 0 - 255. + int b = this.addr[1] & 0xFF; + int c = this.addr[2] & 0xFF; + int d = this.addr[3] & 0xFF; + if( ( a >= 0 ) && ( a <= 127 ) ) + { + if( b == 0 && c == 0 && d == 0 ) + { + mask = 8; + } + else if( c == 0 && d == 0 ) + { + mask = 16; + } + else if( d == 0 ) + { + mask = 24; + } + } + else if( ( a >= 128 ) && ( a <= 191 ) ) + { + if( c == 0 && d == 0 ) + { + mask = 16; + } + else if( d == 0 ) + { + mask = 24; + } + } + else if( ( a >= 192 ) && ( a <= 223 ) ) + { + if( d == 0 ) + { + mask = 24; + } + } + } + } + + return( mask ); + } + + /** + * A given IP address and netmask specified with a cidr cannot have any + * bits which specify the host as non-zero within the address. + * + *

This method ensures that this constraint is met.

+ * + *

If the constraint is not met, an exception is thrown.

+ */ + protected void ensureHostBitsAreZero() + throws PSQLException + { + // this represents the number of full bytes in the network portion + // of the address. + int network_bytes = this.netmask / 8 ; + + // This represents the number of bits in the network portion + // of the address within the boundary byte, + // if the netmask is 24, this is 0 + // if the netmask is 23, this is 7 + // if the netmask is 25, this is 1 ... + int network_bits = this.netmask % 8; + + // Test the boundary byte to ensure no bits are set in the host + // portion. + if( network_bytes < this.addr.length ) + { + if( ( this.addr[network_bytes] & ( 0xFF >> network_bits ) ) != 0 ) + { + // bits to the right of the network portion of the + // address, this is not allowed. + throw( new PSQLException( GT.tr( "Conversion to type {0} failed: " + + "host bits not all zero.", + new Object[]{ type } ), PSQLState.DATA_TYPE_MISMATCH ) ); + } + } + + // test each byte after the boundary byte and ensure that they are zero. + for( int i = network_bytes + 1; i < this.addr.length; ++i ) + { + if( this.addr[i] != 0 ) + { + // bits to the right of the network portion of the + // address, this is not allowed. + throw( new PSQLException( GT.tr( "Conversion to type {0} failed: " + + "host bits not all zero.", + new Object[]{ type } ), PSQLState.DATA_TYPE_MISMATCH ) ); + } + } + } +} diff -ruN postgresql-jdbc-8.1dev-403.src/org/postgresql/net/PGinet.java postgresql-8.1dev-403-inet-src/org/postgresql/net/PGinet.java --- postgresql-jdbc-8.1dev-403.src/org/postgresql/net/PGinet.java Wed Dec 31 19:00:00 1969 +++ postgresql-8.1dev-403-inet-src/org/postgresql/net/PGinet.java Wed Nov 2 21:08:07 2005 @@ -0,0 +1,135 @@ +/*------------------------------------------------------------------------- +* +* Copyright (C) 2005, PostgreSQL Global Development Group +* +*-------------------------------------------------------------------------- +*/ +package org.postgresql.net; + +import java.io.Serializable; +import java.sql.SQLException; +import org.postgresql.util.GT; +import org.postgresql.util.PGobject; +import org.postgresql.util.PGtokenizer; +import org.postgresql.util.PSQLException; +import org.postgresql.util.PSQLState; + +/** + * This represents org.postgresql's inet datatype, which is used + * to hold IPv4 and IPv6 network addresses and ip addresses. + * + *

This class wraps the postgresql specific INET datatype. It supports + * values in the following format.

+ * + *

IPv4 a.b.c.d[/netmask]

+ *

IPv6 x:x:x:x:x:x:x:x[/netmask]

+ *

IPv6 x:x:x:x:x:x:a.b.c.d[/netmask] - IPv4 embedded in IPv6

+ *

The use of :: is also allowed in IPv6 addresses.

+ *

See [RFC 2373] and [RFC 2732] + * for the complete description of how to specify an IPv6 address.

+ * + * @author Russell Francis < rfrancis@ev.net > + */ +public class PGinet extends PGNetworkBase implements Serializable, Cloneable +{ + /** + * This constructor takes a string in a valid literal format + * for either an IPv4 or IPv6 address and creates a new PGinet to + * represent it. + * + * @param s The string representation of the inet value. + * @exception SQLException If the string is invalid. + */ + public PGinet( String s ) + throws SQLException + { + this(); + this.setValue( s ); + } + + public PGinet() + { + setType( "inet" ); + } + + /** + * Compare two PGinet's for equality. + * + * @param obj The object which we wish to compare. + * @return true if it represents the same network or ip address + * as this PGinet, false otherwise. + */ + public boolean equals( Object obj ) + { + if( obj instanceof PGinet ) + { + return( super.equals( obj ) ); + } + + return( false ); + } + + /** + * Make a duplicate of this PGinet object. + * + * @return null on failure, or a new PGinet address + * which is equal to this object. + */ + public Object clone() + { + try + { + return( new PGinet( this.getValue() ) ); + } + catch( SQLException e ) + { + return( null ); + } + } + + /** + * This method sets the value of this PGinet object. + * + * @param v A string representation of an inet address a.b.c.d[/netmask] + * @exception SQLException If the parameter is not a valid inet address. + */ + public void setValue( String v ) + throws SQLException + { + if( v == null ) + { + throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.", + new Object[]{ type, v } ), PSQLState.DATA_TYPE_MISMATCH ) ); + } + + String host = this.getHostPortion( v ); + + if( isIPv6Address( host ) ) + { + this.netmask = this.getNetmaskPortion( v, 128 ); + if( ( this.netmask < 0 ) || ( this.netmask > 128 ) ) + { + this.netmask = 0; + this.addr = null; + throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.", + new Object[]{ type, v } ), PSQLState.DATA_TYPE_MISMATCH ) ); + } + } + else if( isIPv4Address( host, false ) ) // don't allow shortened ipv4 notation. + { + this.netmask = this.getNetmaskPortion( v, 32 ); + if( ( this.netmask < 0 ) || ( this.netmask > 32 ) ) + { + this.netmask = 0; + this.addr = null; + throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.", + new Object[]{ type, v } ), PSQLState.DATA_TYPE_MISMATCH ) ); + } + } + else + { + throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.", + new Object[]{ type, v } ), PSQLState.DATA_TYPE_MISMATCH ) ); + } + } +} diff -ruN postgresql-jdbc-8.1dev-403.src/org/postgresql/net/PGmacaddr.java postgresql-8.1dev-403-inet-src/org/postgresql/net/PGmacaddr.java --- postgresql-jdbc-8.1dev-403.src/org/postgresql/net/PGmacaddr.java Wed Dec 31 19:00:00 1969 +++ postgresql-8.1dev-403-inet-src/org/postgresql/net/PGmacaddr.java Wed Nov 2 21:08:07 2005 @@ -0,0 +1,254 @@ +/*------------------------------------------------------------------------- +* +* Copyright (C) 2005, PostgreSQL Global Development Group +* +*-------------------------------------------------------------------------- +*/ +package org.postgresql.net; + +import java.io.Serializable; +import java.sql.SQLException; +import org.postgresql.util.GT; +import org.postgresql.util.PGobject; +import org.postgresql.util.PGtokenizer; +import org.postgresql.util.PSQLException; +import org.postgresql.util.PSQLState; + +/** + * This represents org.postgresql's macaddr datatype, which is used + * to store NIC mac addresses. + * + *

There are 5 possible valid forms which we will accept + * for a mac address which is a sextuple of hex digits.

+ * + *

+ *

+ *

+ * + * @author Russell Francis < rfrancis@ev.net > + */ +public class PGmacaddr extends PGobject implements Serializable, Cloneable +{ + byte[] macaddress = null; + Integer hashCode = null; + String strMac = null; + + /** + * Construct a new mac address from a string in one of the formats + * specified above. + * + * @param s A macaddress represented as a string. + * @exception SQLException This is thrown if the string is not a + * valid macaddress. + */ + public PGmacaddr( String s ) + throws SQLException + { + this(); + this.setValue( s ); + } + + public PGmacaddr() + { + setType( "macaddr" ); + } + + /** + * This method will get an array of bytes which make up the mac address. + * + * @return An array of bytes which make up the mac address or null, if + * no address has been assigned. + */ + public byte[] getBytes() + { + return( this.macaddress ); + } + + /** + * Set the value of the mac address. + * + * There are 5 possible valid forms which we will accept + * for a mac address which is a sextuple of hex digits + * + * 1. xxxx.xxxx.xxxx + * 2. xxxxxx:xxxxxx + * 3. xx:xx:xx:xx:xx:xx + * 4. xx-xx-xx-xx-xx-xx + * 5. xxxxxx-xxxxxx + * + * @param v A mac address in one of the formats listed above. + */ + public void setValue( String v ) + throws SQLException + { + if( v == null ) + { + throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.", + new Object[]{ type, v } ), PSQLState.DATA_TYPE_MISMATCH ) ); + } + + if( v.startsWith( ":" ) || v.startsWith( "." ) || v.startsWith( "-" ) || + v.endsWith( ":" ) || v.endsWith( "." ) || v.endsWith( "-" ) ) + { + throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.", + new Object[]{ type, v } ), PSQLState.DATA_TYPE_MISMATCH ) ); + } + + String strippedMacAddr = null; + + // this is the format we output macaddr's in + // so we will check it first. + // the case xx:xx:xx:xx:xx:xx or xxxxxx:xxxxxx + PGtokenizer t = new PGtokenizer( v, ':' ); + if( ( t.getSize() == 6 ) || ( t.getSize() == 2 ) ) + { + strippedMacAddr = PGtokenizer.strip( v, ':' ); + } + + if( strippedMacAddr == null ) + { + // lets try case xxxxxx-xxxxxx + // or xx-xx-xx-xx-xx-xx + t = new PGtokenizer( v, '-' ); + if( ( t.getSize() == 2 ) || ( t.getSize() == 6 ) ) + { + strippedMacAddr = PGtokenizer.strip( v, '-' ); + } + } + + if( strippedMacAddr == null ) + { + // try the case where they are separated by dots. + // xxxx.xxxx.xxxx + t = new PGtokenizer( v, '.' ); + if( t.getSize() == 3 ) + { + strippedMacAddr = PGtokenizer.strip( v, '.' ); + } + } + + // If we don't have a stripped mac address then we fail here. + if( strippedMacAddr == null ) + { + throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.", + new Object[]{ type, v } ), PSQLState.DATA_TYPE_MISMATCH ) ); + } + + // There should be 6 pairs of hexadecimal digits. This + // will make any valid macaddress 12 characters long. + if( strippedMacAddr.length() != 12 ) + { + throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.", + new Object[]{ type, v } ), PSQLState.DATA_TYPE_MISMATCH ) ); + } + + byte[] mac; + try + { + mac = new byte[ 6 ]; + for( int i = 0; i < 6; ++i ) + { + int twoi = i * 2; + mac[i] = (byte)(Integer.valueOf( + strippedMacAddr.substring( twoi, twoi + 2 ), 16 ).intValue() & 0xFF); + } + } + catch( NumberFormatException e ) + { + throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.", + new Object[]{ type, v } ), PSQLState.DATA_TYPE_MISMATCH ) ); + } + + this.macaddress = mac; + this.hashCode = null; + this.strMac = null; + } + + /** + * This will compare two mac addresses for equality. + * + * @param obj Another PGmacaddr object to compare with this one. + * @return true if they are equivalent, false if not. + */ + public boolean equals( Object obj ) + { + if( obj instanceof PGmacaddr ) + { + PGmacaddr addr = (PGmacaddr)obj; + byte[] thatBytes = addr.getBytes(); + if( ( this.macaddress == null ) || ( thatBytes == null ) ) + { + return( false ); + } + + if( thatBytes.length == this.macaddress.length ) + { + if( this.macaddress.length == 6 ) + { + for( int i = 0; i < 6; ++i ) + { + if( thatBytes[i] != this.macaddress[i] ) + { + return( false ); + } + } + return( true ); + } + } + } + return( false ); + } + + /** + * Get a hash code for this mac address. + * + * @return a hash value for this object. + */ + public int hashCode() + { + if( this.hashCode == null ) + { + int hashValue = this.macaddress[4] | (this.macaddress[5] << 8) | + (this.macaddress[4] << 16) | (this.macaddress[5] << 24); + for( int i = 0; i < 4; ++i ) + { + hashValue ^= this.macaddress[i] << (i << 3); + } + this.hashCode = new Integer( hashValue ); + } + + return( this.hashCode.intValue() ); + } + + /** + * This method will print the value of this mac address as a string. + * + * @return A string representation of this mac address in the + * xx:xx:xx:xx:xx:xx format. + */ + public String getValue() + { + if( this.strMac == null ) + { + StringBuffer val = new StringBuffer(); + + for( int i = 0; i < this.macaddress.length; ++i ) + { + if( i > 0 ) + { + val.append( ":" ); + } + val.append( Integer.toHexString( ((int)this.macaddress[i]) & 0xFF ) ); + } + this.strMac = val.toString(); + } + + return( this.strMac ); + } +} diff -ruN postgresql-jdbc-8.1dev-403.src/org/postgresql/test/jdbc2/Jdbc2TestSuite.java postgresql-8.1dev-403-inet-src/org/postgresql/test/jdbc2/Jdbc2TestSuite.java --- postgresql-jdbc-8.1dev-403.src/org/postgresql/test/jdbc2/Jdbc2TestSuite.java Mon Aug 1 02:54:15 2005 +++ postgresql-8.1dev-403-inet-src/org/postgresql/test/jdbc2/Jdbc2TestSuite.java Wed Nov 2 21:08:30 2005 @@ -23,7 +23,6 @@ public static TestSuite suite() { TestSuite suite = new TestSuite(); - // // Add one line per class in our test cases. These should be in order of // complexity. @@ -86,6 +85,11 @@ suite.addTestSuite(GeometricTest.class); suite.addTestSuite(LoginTimeoutTest.class); + + // tests for the PGcidr, PGinet and PGmacaddr types + suite.addTestSuite(org.postgresql.test.net.PGcidrTest.class); + suite.addTestSuite(org.postgresql.test.net.PGinetTest.class); + suite.addTestSuite(org.postgresql.test.net.PGmacaddrTest.class); // That's all folks return suite; diff -ruN postgresql-jdbc-8.1dev-403.src/org/postgresql/test/net/PGcidrTest.java postgresql-8.1dev-403-inet-src/org/postgresql/test/net/PGcidrTest.java --- postgresql-jdbc-8.1dev-403.src/org/postgresql/test/net/PGcidrTest.java Wed Dec 31 19:00:00 1969 +++ postgresql-8.1dev-403-inet-src/org/postgresql/test/net/PGcidrTest.java Wed Nov 2 21:08:30 2005 @@ -0,0 +1,241 @@ +/*------------------------------------------------------------------------- +* +* Copyright (C) 2005, PostgreSQL Global Development Group +* +*-------------------------------------------------------------------------- +*/ +package org.postgresql.test.net; + +import org.postgresql.test.TestUtil; +import org.postgresql.net.PGcidr; +import junit.framework.TestCase; +import java.sql.*; +import java.net.*; + +/** + * Unit tests for the PGcidr data type. + * + * @author Russell Francis < rfrancis@ev.net > + */ +public class PGcidrTest extends TestCase +{ + private Connection dbConn; + private static final String tableName = "testpgcidr"; + + public PGcidrTest( String name ) + { + super( name ); + } + + protected void setUp() throws Exception + { + dbConn = TestUtil.openDB(); + TestUtil.createTable( dbConn, this.tableName, "network cidr" ); + } + + protected void tearDown() throws Exception + { + TestUtil.dropTable( dbConn, this.tableName ); + } + + /** + * This method will test that the PGcidr type refuses to create + * objects when passed invalid IPv4 based network addresses. + */ + public void testPGcidrIPv4InvalidNetworks() + throws SQLException + { + String[] invalidNetworks = { + "240.0.0.1/31", // bits to the right of the netmask + "255.255.1/23", // bits to the right of the netmask + "255.255.128/16", // bits to the right of the netmask + "255.1/15", // bits to the right of the netmask + "255.128/8", // bits to the right of the netmask + "1/7", // bits to the right of the netmask + "128/0", // bits to the right of the netmask + "255.255.255.255/ab", // non-numeric netmask + "255.255.255.255/255/255", // junk + "300.0.0.1", // a is out of range. + "19.1000.50", // b is out of range. + "1.2.301.4/31", // c is out of range. + "1.2.3.4/35", // netmask is too big. + "1.2.3.4/-10", // netmask is too small. + "1.2.3.4/-1", // netmask is too small. + "1.2.3.4.5/32" // not dotted quad. + }; + + // Test that we can create networks + for( int i = 0; i < invalidNetworks.length; ++i ) + { + PGcidr network = this.makePGcidr( invalidNetworks[i] ); + assertNull( "An invalid network was turned into a PGcidr object: " + + invalidNetworks[i] + "' failed.", network ); + + } + } + + /** + * This method will test that the PGcidr type creates + * PGcidr objects when passed valid IPv4 based network addresses. + * + *

It will also ensure that they can be inserted into Postgres + * and the they can be read from Postgres and that they maintain + * equality after the ordeal.

+ */ + public void testPGcidrIPv4ValidNetworks() throws SQLException + { + // Each network should be unique in the list or this test + // will fail. + String[] validNetworks = { + "0", + "0/32", + "1", + "1.5", + "10.10", + "10.10/15", + "20.20.20", + "20.20.20/25", + "20.20.20/22", + "220.200.200", + "220.128.1/25", + "220.200.200.4", + "192.168.1.10/32" + }; + + this.ensureValidNetworks( validNetworks ); + } + + /** + * This method will test that the PGcidr type refuses to create + * objects when passed invalid IPv6 based network addresses. + */ + public void testPGcidrIPv6InvalidNetworks() throws SQLException + { + String[] invalidNetworks = { + "1234:1234:1234:1234:1234:1234:1234:1234:", // superfluous trailing ':' + "1234:1234:1234:1234:1234:1234:1234:123F/127", // bits to right of netmask + "1234:1234:1234::/31", // bits to right of netmask + "1234:1234:1234::/32", // bits to right of netmask + "1234:1234:1234::/34", // bits to right of netmask + "::/-1", // netmask out of range. + "::/129", // netmask out of range. + "1234:5678:9abc:defg::/128", // bogus hex value 'g'. + "1234:5678:9abc::/0f", // non numeric netmask. + "1234:5678:9abc:deff:ffde:cba9:8765:4321:1234" // to many hex sequences. + }; + + // Test that we can create networks + for( int i = 0; i < invalidNetworks.length; ++i ) + { + PGcidr network = this.makePGcidr( invalidNetworks[i] ); + assertNull( "An invalid network was turned into a PGcidr object: " + + invalidNetworks[i] + "' failed.", network ); + + } + } + + + /** + * This method will test that the PGcidr type creates + * PGcidr objects when passed valid IPv6 based network addresses. + * + *

It will also ensure that they can be inserted into Postgres + * and the they can be read from Postgres and that they maintain + * equality after the ordeal.

+ */ + public void testPGcidrIPv6ValidNetworks() throws SQLException + { + // Each network should be unique in the list or this test + // will fail. + String[] validNetworks = { + "::", + "::/32", + "abcd:eff0::/28", + "abcd:efef::/32", + "4bc:ab:1234::bcda/127", + "4bc:ab:1234::bcda/128", + "1234::1234", + "1234:1234:1234:1234:1234:1234:1234:123E/127", + "1234:1234:1234:1234:1234:1234:1234:123F/128" + }; + + this.ensureValidNetworks( validNetworks ); + } + + /** + * This method will take an array of valid network strings and + * ensure that PGcidr objects can be created from them, these + * objects are inserted into the database and each one is then fetched + * from the database and their values are compared for equality. + * + *

We also ensure that the hashCode for the original object + * matches that of the fetched object.

+ * + * @param validNetworks an array of valid network strings. + */ + private void ensureValidNetworks( String[] validNetworks ) + throws SQLException + { + // Insert each of the networks into the table. + PreparedStatement s = dbConn.prepareStatement( TestUtil.insertSQL( tableName, "?" ) ); + for( int i = 0;i < validNetworks.length; ++i ) + { + PGcidr network = this.makePGcidr( validNetworks[i] ); + assertNotNull( "A valid network was unable to be converted into a PGcidr object: '" + + validNetworks[i] + "' failed.", network ); + + s.setObject( 1, network ); + assertEquals( 1, s.executeUpdate() ); + } + + s = dbConn.prepareStatement( "SELECT * FROM " + tableName + " WHERE network = ?" ); + // Retrieve each of the networks from the table. + for( int i = 0; i < validNetworks.length; ++i ) + { + PGcidr network = this.makePGcidr( validNetworks[i] ); + assertNotNull( "A valid network was unable to be converted into a PGcidr object: '" + + validNetworks[i] + "' failed.", network ); + + s.setObject( 1, network ); + ResultSet rs = s.executeQuery(); + int count = 0; + while( rs.next() ) + { + PGcidr fetchedNetwork = (PGcidr)rs.getObject( 1 ); + assertNotNull( "Unable to fetch inserted network from the database: '" + validNetworks[i] + + "' failed.", network ); + + assertEquals( "The retrieved network and the inserted network are not equal!", + fetchedNetwork, network ); + + assertEquals( "The retrieved network and the inserted network have different hashCodes!", + fetchedNetwork.hashCode(), network.hashCode() ); + ++count; + } + + assertEquals( "Selected more than 1 row!", 1, count ); + } + } + + /** + * This is a local utility method which will take a string + * and attempt to create a new PGcidr object. + * + *

If the object cannot be created, null will be returned.

+ * + * @param value The textual representation of a network address. + * @return null on failure or a PGcidr object which represents + * the value parameter. + */ + private PGcidr makePGcidr( String value ) + { + try + { + return( new PGcidr( value ) ); + } + catch( Exception e ) + { + return( null ); + } + } +} diff -ruN postgresql-jdbc-8.1dev-403.src/org/postgresql/test/net/PGinetTest.java postgresql-8.1dev-403-inet-src/org/postgresql/test/net/PGinetTest.java --- postgresql-jdbc-8.1dev-403.src/org/postgresql/test/net/PGinetTest.java Wed Dec 31 19:00:00 1969 +++ postgresql-8.1dev-403-inet-src/org/postgresql/test/net/PGinetTest.java Wed Nov 2 21:08:30 2005 @@ -0,0 +1,230 @@ +/*------------------------------------------------------------------------- +* +* Copyright (C) 2005, PostgreSQL Global Development Group +* +*-------------------------------------------------------------------------- +*/ +package org.postgresql.test.net; + +import org.postgresql.test.TestUtil; +import org.postgresql.net.PGinet; +import junit.framework.TestCase; +import java.sql.*; +import java.net.*; + +/** + * Unit tests for the PGinet data type. + * + * @author Russell Francis < rfrancis@ev.net > + */ +public class PGinetTest extends TestCase +{ + private Connection dbConn; + private static final String tableName = "testpginet"; + + public PGinetTest( String name ) + { + super( name ); + } + + protected void setUp() throws Exception + { + dbConn = TestUtil.openDB(); + TestUtil.createTable( dbConn, this.tableName, "address inet" ); + } + + protected void tearDown() throws Exception + { + TestUtil.dropTable( dbConn, this.tableName ); + } + + /** + * This method will test that the PGinet type refuses to create + * objects when passed invalid IPv4 based addresses. + */ + public void testPGinetIPv4InvalidAddresses() + throws SQLException + { + String[] invalidAddresses = { + "255.255.1/23", // too short + "255.255.128/16", // too short + "255.1/15", // too short + "255.128/8", // too short + "1/7", // too short + "128/0", // too short + "255.255.255.255/ab", // non-numeric netmask + "255.255.255.255/255/255", // junk + "300.0.0.1", // a is out of range. + "19.1000.50", // b is out of range. + "1.2.301.4/31", // c is out of range. + "1.2.3.4/35", // netmask is too big. + "1.2.3.4/-10", // netmask is too small. + "1.2.3.4/-1", // netmask is too small. + "1.2.3.4.5/32", // not dotted quad. + "a.b.c.d/32" // hex digits not valid. + }; + + // Test that we can create networks + for( int i = 0; i < invalidAddresses.length; ++i ) + { + PGinet address = this.makePGinet( invalidAddresses[i] ); + assertNull( "An invalid address was turned into a PGinet object: " + + invalidAddresses[i] + "' failed.", address ); + } + } + + /** + * This method will test that the PGinet type creates + * PGinet objects when passed valid IPv4 based addresses. + * + *

It will also ensure that they can be inserted into Postgres + * and the they can be read from Postgres and that they maintain + * equality after the ordeal.

+ */ + public void testPGinetIPv4ValidAddresses() throws SQLException + { + // Each address should be unique in the list or this test + // will fail. + String[] validAddresses = { + "0.0.0.0", + "192.168.1.10/32", + "192.168.1.10/20", + "10.10.10.120/8", + "132.235.215.243", + "200.46.204.71" + }; + + this.ensureValidAddresses( validAddresses ); + } + + /** + * This method will test that the PGinet type refuses to create + * objects when passed invalid IPv6 based addresses. + */ + public void testPGinetIPv6InvalidAddresses() throws SQLException + { + String[] invalidAddresses = { + "1234:1234:1234:1234:1234:1234:1234:1234:", // superfluous trailing ':' + ":1234:1234:1234:1234:1234:1234:1234:1234", // superfluous trailing ':' + "::/-1", // netmask out of range. + "::/129", // netmask out of range. + "1234:5678:9abc:defg::/128", // bogus hex value 'g'. + "1234:5678:9abc::/0f", // non numeric netmask. + "1234:5678:9abc:deff:ffde:cba9:8765:4321:1234" // to many hex sequences. + }; + + // Test that we cannot create the addresses + for( int i = 0; i < invalidAddresses.length; ++i ) + { + PGinet address = this.makePGinet( invalidAddresses[i] ); + assertNull( "An invalid address was turned into a PGinet object: " + + invalidAddresses[i] + "' failed.", address ); + } + } + + /** + * This method will test that the PGinet type creates + * PGinet objects when passed valid IPv6 based address. + * + *

It will also ensure that they can be inserted into Postgres + * and the they can be read from Postgres and that they maintain + * equality after the ordeal.

+ */ + public void testPGinetIPv6ValidAddresses() throws SQLException + { + // Each address should be unique in the list or this test + // will fail. + String[] validAddresses = { + "::", + "::/32", + "abcd:eff0::/28", + "abcd:efef::/32", + "4bc:ab:1234::bcda/127", + "4bc:ab:1234::bcda/128", + "1234::1234", + "1234:1234:1234:1234:1234:1234:1234:123E/127", + "1234:1234:1234:1234:1234:1234:1234:123F/127", + "1234:1234:1234:1234:1234:1234:1234:123F/128", + "::192.168.1.1" + }; + + this.ensureValidAddresses( validAddresses ); + } + + /** + * This method will take an array of valid address strings and + * ensure that PGinet object can be created from them, these + * objects are inserted into the database and each one is then fetched + * from the database and their values are compared for equality. + * + *

We also ensure that the hashCode for the original object + * matches that of the fetched object.

+ * + * @param validAddresses an array of valid inet addresses. + */ + private void ensureValidAddresses( String[] validAddresses ) + throws SQLException + { + // Insert each of the networks into the table. + PreparedStatement s = dbConn.prepareStatement( TestUtil.insertSQL( tableName, "?" ) ); + for( int i = 0;i < validAddresses.length; ++i ) + { + PGinet address = this.makePGinet( validAddresses[i] ); + assertNotNull( "A valid address was unable to be converted into a PGinet object: '" + + validAddresses[i] + "' failed.", address ); + + s.setObject( 1, address ); + assertEquals( 1, s.executeUpdate() ); + } + + s = dbConn.prepareStatement( "SELECT * FROM " + tableName + " WHERE address = ?" ); + // Retrieve each of the networks from the table. + for( int i = 0; i < validAddresses.length; ++i ) + { + PGinet address = this.makePGinet( validAddresses[i] ); + assertNotNull( "A valid address was unable to be converted into a PGinet object: '" + + validAddresses[i] + "' failed.", address ); + + s.setObject( 1, address ); + ResultSet rs = s.executeQuery(); + int count = 0; + while( rs.next() ) + { + PGinet fetchedAddress = (PGinet)rs.getObject( 1 ); + assertNotNull( "Unable to fetch inserted network from the database: '" + validAddresses[i] + + "' failed.", address ); + + assertEquals( "The retrieved address and the inserted address are not equal!", + fetchedAddress, address ); + + assertEquals( "The retrieved address and the inserted address have different hashCodes!", + fetchedAddress.hashCode(), address.hashCode() ); + ++count; + } + + assertEquals( "Selected more than 1 row!", 1, count ); + } + } + + /** + * This is a local utility method which will take a string + * and attempt to create a new PGinet object. + * + *

If the object cannot be created, null will be returned.

+ * + * @param value The textual representation of a network address. + * @return null on failure or a PGinet object which represents + * the value parameter. + */ + private PGinet makePGinet( String value ) + { + try + { + return( new PGinet( value ) ); + } + catch( Exception e ) + { + return( null ); + } + } +} diff -ruN postgresql-jdbc-8.1dev-403.src/org/postgresql/test/net/PGmacaddrTest.java postgresql-8.1dev-403-inet-src/org/postgresql/test/net/PGmacaddrTest.java --- postgresql-jdbc-8.1dev-403.src/org/postgresql/test/net/PGmacaddrTest.java Wed Dec 31 19:00:00 1969 +++ postgresql-8.1dev-403-inet-src/org/postgresql/test/net/PGmacaddrTest.java Wed Nov 2 21:08:30 2005 @@ -0,0 +1,149 @@ +/*------------------------------------------------------------------------- +* +* Copyright (C) 2005, PostgreSQL Global Development Group +* +*-------------------------------------------------------------------------- +*/ +package org.postgresql.test.net; + +import org.postgresql.test.TestUtil; +import org.postgresql.net.PGmacaddr; +import junit.framework.TestCase; +import java.sql.*; +import java.net.*; + +/** + * Unit tests for the PGmacaddr data type. + * + * @author Russell Francis < rfrancis@ev.net > + */ +public class PGmacaddrTest extends TestCase +{ + private Connection dbConn; + private static final String tableName = "testpgmacaddr"; + + public PGmacaddrTest( String name ) + { + super( name ); + } + + protected void setUp() throws Exception + { + dbConn = TestUtil.openDB(); + TestUtil.createTable( dbConn, this.tableName, "mac macaddr" ); + } + + protected void tearDown() throws Exception + { + TestUtil.dropTable( dbConn, this.tableName ); + } + + /** + * This method will test that the PGmacaddr type refuses to create + * objects when passed invalid mac addresses. + */ + public void testPGmacaddrInvalid() + throws SQLException + { + String[] invalidMacAddresses = { + "ab:ab:ab:ab:ab:ab:ab", // to many pairs. + "ab:abc:ab:ab:ab:ab", // pair two is wrong length. + "ab:ab-ab:ab-ab:ab", // not all the same delimiter + "fg:ab:cd:ef:01:23", // invalid hex character g + ":af:af:af:af:af:af", // starts with a ':' + "12:34:56:78:9a:bc:" // ends with a ':' + }; + + // Test that we can create networks + for( int i = 0; i < invalidMacAddresses.length; ++i ) + { + PGmacaddr address = this.makePGmacaddr( invalidMacAddresses[i] ); + assertNull( "An invalid mac address was turned into a PGmacaddr object: " + + invalidMacAddresses[i] + "' failed.", address ); + } + } + + /** + * This method will test that the PGmacaddr type creates + * PGmacaddr objects when passed valid mac addresses. + * + *

It will also ensure that they can be inserted into Postgres + * and the they can be read from Postgres and that they maintain + * equality after the ordeal.

+ */ + public void testPGmacaddrValid() throws SQLException + { + // Each address should be unique in the list or this test + // will fail. + String[] validMacAddresses = { + "12:34:56:65:43:21", + "123457:754321", + "abcdee-eedcba", + "ab-cd-ef-fe-dc-ba", + "1234.5678.9abc" + }; + + // Insert each of the networks into the table. + PreparedStatement s = dbConn.prepareStatement( TestUtil.insertSQL( tableName, "?" ) ); + for( int i = 0;i < validMacAddresses.length; ++i ) + { + PGmacaddr address = this.makePGmacaddr( validMacAddresses[i] ); + assertNotNull( "A valid mac address was unable to be converted into a PGmacaddr object: '" + + validMacAddresses[i] + "' failed.", address ); + + s.setObject( 1, address ); + assertEquals( 1, s.executeUpdate() ); + } + + s = dbConn.prepareStatement( "SELECT * FROM " + tableName + " WHERE mac = ?" ); + // Retrieve each of the networks from the table. + for( int i = 0; i < validMacAddresses.length; ++i ) + { + PGmacaddr address = this.makePGmacaddr( validMacAddresses[i] ); + assertNotNull( "A valid address was unable to be converted into a PGmacaddr object: '" + + validMacAddresses[i] + "' failed.", address ); + + s.setObject( 1, address ); + ResultSet rs = s.executeQuery(); + int count = 0; + while( rs.next() ) + { + PGmacaddr fetchedAddress = (PGmacaddr)rs.getObject( 1 ); + assertNotNull( "Unable to fetch inserted mac address from the database: '" + + validMacAddresses[i] + "' failed.", address ); + + assertEquals( "The retrieved address and the inserted address are not equal!", + fetchedAddress, address ); + + assertEquals( "The retrieved address and the inserted address have " + + "different hashCodes!", fetchedAddress.hashCode(), address.hashCode() ); + + ++count; + } + + assertEquals( "Selected more than 1 row!", 1, count ); + } + } + + /** + * This is a local utility method which will take a string + * and attempt to create a new PGmacaddr object. + * + *

If the object cannot be created, null will be returned.

+ * + * @param value The textual representation of a macaddress. + * @return null on failure or a PGmacaddr object which represents + * the value parameter. + */ + private PGmacaddr makePGmacaddr( String value ) + { + try + { + return( new PGmacaddr( value ) ); + } + catch( Exception e ) + { + return( null ); + } + } +} diff -ruN postgresql-jdbc-8.1dev-403.src/org/postgresql/util/PGtokenizer.java postgresql-8.1dev-403-inet-src/org/postgresql/util/PGtokenizer.java --- postgresql-jdbc-8.1dev-403.src/org/postgresql/util/PGtokenizer.java Tue Jan 11 03:25:49 2005 +++ postgresql-8.1dev-403-inet-src/org/postgresql/util/PGtokenizer.java Wed Nov 2 21:08:53 2005 @@ -22,6 +22,9 @@ * @see org.postgresql.geometric.PGpath * @see org.postgresql.geometric.PGpoint * @see org.postgresql.geometric.PGpolygon + * @see org.postgresql.net.PGcidr + * @see org.postgresql.net.PGinet + * @see org.postgresql.net.PGmacaddr */ public class PGtokenizer { @@ -83,9 +86,6 @@ } // Don't forget the last token ;-) - - - if (s < string.length()) tokens.addElement(string.substring(s)); @@ -123,6 +123,29 @@ { return new PGtokenizer(getToken(n), delim); } + + /** + * This method will remove all occurences of char + * from a string. + * + * @param s The string to strip. + * @param ch The character to strip from s. + * @return A new string without any occurences of + * ch. + */ + public static String strip( String s, char ch ) + { + StringBuffer str = new StringBuffer(); + for( int i = 0; i < s.length(); ++i ) + { + char c = s.charAt( i ); + if( c != ch ) + { + str.append( c ); + } + } + return( str.toString() ); + } /* * This removes the lead/trailing strings from a string