Skip to content

Instantly share code, notes, and snippets.

@Downchuck
Created January 20, 2014 23:47
Show Gist options
  • Save Downchuck/8531692 to your computer and use it in GitHub Desktop.
Save Downchuck/8531692 to your computer and use it in GitHub Desktop.
postgresql jna
diff --git a/build.xml b/build.xml
index 2b86a36..ddd235e 100644
--- a/build.xml
+++ b/build.xml
@@ -45,6 +45,9 @@
<include name="${package}/largeobject/**" />
<include name="${package}/util/**" />
+
+ <include name="${package}/jna/**" />
+
<!--
Each jdbcN subpackage is used only if the driver supports *at least* that
revision of JDBC. That is, a JDBC2 build uses only jdbc2, a JDBC3 build
diff --git a/org/postgresql/core/PGStream.java b/org/postgresql/core/PGStream.java
index fad4c12..a7b3443 100644
--- a/org/postgresql/core/PGStream.java
+++ b/org/postgresql/core/PGStream.java
@@ -23,6 +23,8 @@ import org.postgresql.util.HostSpec;
import org.postgresql.util.PSQLState;
import org.postgresql.util.PSQLException;
+import org.postgresql.jna.UnixSocketFactory;
+
/**
* Wrapper around the raw connection to the server that implements some basic
* primitives (reading/writing formatted data, doing string encoding, etc).
@@ -56,9 +58,17 @@ public class PGStream
{
this.hostSpec = hostSpec;
- Socket socket = new Socket();
- socket.connect(new InetSocketAddress(hostSpec.getHost(), hostSpec.getPort()));
- changeSocket(socket);
+ Socket socket = new Socket();
+ if(hostSpec.getHost().startsWith("unix:")) {
+ UnixSocketFactory unixSocket = new UnixSocketFactory();
+ String unixPath = hostSpec.getHost().substring(5);
+ socket = unixSocket.createSocket(unixPath, 0);
+ }
+ else {
+ socket.connect(new InetSocketAddress(hostSpec.getHost(), hostSpec.getPort()));
+ }
+ changeSocket(socket);
+
setEncoding(Encoding.getJVMEncoding("US-ASCII"));
_int2buf = new byte[2];
diff --git a/org/postgresql/core/v3/ConnectionFactoryImpl.java b/org/postgresql/core/v3/ConnectionFactoryImpl.java
index 3c7d332..0d2b9fc 100644
--- a/org/postgresql/core/v3/ConnectionFactoryImpl.java
+++ b/org/postgresql/core/v3/ConnectionFactoryImpl.java
@@ -87,6 +87,9 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
for (int whichHost = 0; whichHost < hostSpecs.length; ++whichHost) {
HostSpec hostSpec = hostSpecs[whichHost];
+
+ String unixSocketPath = info.getProperty("unixSocket");
+ if(unixSocketPath != null) hostSpec = new HostSpec(new String("unix:" + unixSocketPath), 0);
if (logger.logDebug())
logger.debug("Trying to establish a protocol version 3 connection to " + hostSpec);
diff --git a/org/postgresql/jna/SockAddr.java b/org/postgresql/jna/SockAddr.java
new file mode 100644
index 0000000..a357c42
--- /dev/null
+++ b/org/postgresql/jna/SockAddr.java
@@ -0,0 +1,30 @@
+/*-------------------------------------------------------------------------
+ *
+ * Copyright (c) 2003-2011, PostgreSQL Global Development Group
+ *
+ *
+ * Authors: J. Yunke, C. Pritchard.
+ *-------------------------------------------------------------------------
+ */
+
+package org.postgresql.jna;
+
+import com.sun.jna.Structure;
+import java.util.List;
+import java.util.Arrays;
+
+public class SockAddr extends Structure {
+ public short sa_family = 1;
+ public byte[] sa_data;
+ public SockAddr(final String path) {
+ byte[] bytes = path.getBytes();
+ sa_data = new byte[bytes.length + 1];
+ System.arraycopy(bytes, 0, sa_data, 0, bytes.length);
+ allocateMemory();
+ }
+ @Override
+ protected List getFieldOrder() {
+ return Arrays.asList("sa_family", "sa_data");
+ }
+}
+
diff --git a/org/postgresql/jna/UnixSocket.java b/org/postgresql/jna/UnixSocket.java
new file mode 100644
index 0000000..4d63c45
--- /dev/null
+++ b/org/postgresql/jna/UnixSocket.java
@@ -0,0 +1,213 @@
+/*-------------------------------------------------------------------------
+ *
+ * Copyright (c) 2003-2011, PostgreSQL Global Development Group
+ *
+ *
+ * Authors: J. Yunke, C. Pritchard.
+ *-------------------------------------------------------------------------
+ */
+
+package org.postgresql.jna;
+
+// Windows not supported by UnixSocketFactory.
+import com.sun.jna.Platform;
+
+// Exceptions.
+import java.io.IOException;
+import java.net.SocketException;
+import com.sun.jna.LastErrorException;
+
+// Implementated interfaces.
+import java.net.Socket;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+// Utils.
+import java.nio.ByteBuffer;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.util.Arrays;
+
+// Socket implementation exposing an InputStream and OutputStream.
+class UnixSocket extends Socket {
+ UnixSocketLibrary library = null;
+ int sockfd = -1;
+ public static final int AF_UNIX = 1;
+ public static final int SOCK_STREAM = 1;
+ public static final int PROTOCOL = 0;
+
+ InputStream is = null;
+ OutputStream os = null;
+
+ UnixSocket(UnixSocketLibrary library, SockAddr sockAddr) throws SocketException {
+ this.library = library;
+ this.sockfd = socket(AF_UNIX,SOCK_STREAM,PROTOCOL);
+
+ try {
+ int i = connect(sockAddr,sockAddr.size());
+ if (i != 0) {
+ throw new SocketException("UnixSocket(..): could not connect to socket");
+ }
+ } catch (LastErrorException lee) {
+ throwSocketException("UnixSocket(..): could not connect to socket",lee);
+ }
+
+ this.is = new BufferedInputStream(new UnixSocketInputStream(this));
+ this.os = new BufferedOutputStream(new UnixSocketOutputStream(this));
+ }
+
+ protected void throwIOException(String prefixMessage, LastErrorException lee) throws IOException {
+ String strerror = library.strerror(lee.getErrorCode());
+ throw new IOException(prefixMessage + ": " + strerror);
+ }
+
+ protected void throwSocketException(String prefixMessage, LastErrorException lee) throws SocketException {
+ String strerror = library.strerror(lee.getErrorCode());
+ throw new SocketException(prefixMessage + ": " + strerror);
+ }
+
+ protected int socket(int domain, int type, int protocol) throws SocketException {
+ try {
+ int sockfd = this.library.socket(domain,type,protocol);
+ return sockfd;
+ } catch (LastErrorException lee) {
+ throwSocketException("socket(..): could not open socket",lee);
+ return -1;
+ }
+ }
+
+ public int connect(SockAddr sockaddr, int addrlen) throws SocketException {
+ try {
+ int result = this.library.connect(this.sockfd,sockaddr,addrlen);
+ return result;
+ } catch (LastErrorException lee) {
+ throwSocketException("connect(..): could not connect to socket",lee);
+ return -1;
+ }
+ }
+
+ public int read(byte[] buf, int count) throws IOException {
+ try {
+ ByteBuffer buffer = ByteBuffer.wrap(buf);
+ int length = this.library.read(this.sockfd,buffer,count);
+ return length;
+ } catch (LastErrorException lee) {
+ throwIOException("read(..): could not read from socket",lee);
+ return -1;
+ }
+ }
+
+ public int write(byte[] buf, int count) throws IOException {
+ try {
+ ByteBuffer buffer = ByteBuffer.wrap(buf);
+ int length = this.library.write(this.sockfd,buffer,count);
+ return length;
+ } catch (LastErrorException lee) {
+ throwIOException("write(..): could not write to socket",lee);
+ return -1;
+ }
+ }
+
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ return is;
+ }
+
+ @Override
+ public OutputStream getOutputStream() throws IOException {
+ return os;
+ }
+
+ @Override
+ public void shutdownInput() throws IOException {
+ is = null;
+ }
+
+ @Override
+ public void shutdownOutput() throws IOException {
+ os = null;
+ }
+
+ @Override
+ public synchronized void close() throws IOException {
+ try {
+ shutdownInput();
+ shutdownOutput();
+ this.library.close(this.sockfd);
+
+ } catch (LastErrorException lee) {
+ throwIOException("close(..): could not close socket",lee);
+ }
+ }
+}
+
+class UnixSocketInputStream extends InputStream {
+ UnixSocket unixSocket = null;
+
+ UnixSocketInputStream(UnixSocket unixSocket) {
+ this.unixSocket = unixSocket;
+ }
+
+ @Override
+ public long skip(long n) throws IOException {
+ return -1;
+ }
+
+ @Override
+ public int read(byte[] data, int offset, int length) throws IOException {
+ int readLength = 0;
+ if (offset != 0) {
+ throw new IOException("read(..): offset not implemented");
+ }
+ readLength = this.unixSocket.read(data, length);
+ return readLength;
+ }
+
+ @Override
+ public int read() throws IOException {
+ byte[] data = new byte[1];
+ int read = read(data);
+ if (read == 0) return -1;
+ if (read != 1) {
+ throw new IOException("read(..): could not read one byte");
+ }
+ return data[0] & 0xFF;
+ }
+
+ @Override
+ public int read(byte[] data) throws IOException {
+ return read(data,0,data.length);
+ }
+
+}
+
+class UnixSocketOutputStream extends OutputStream {
+ UnixSocket unixSocket = null;
+
+ UnixSocketOutputStream(UnixSocket unixSocket) {
+ this.unixSocket = unixSocket;
+ }
+
+ @Override
+ public void write(byte[] data, int offset, int length) throws IOException {
+ if (offset != 0) data = Arrays.copyOfRange(data, offset, offset + length);
+ if (offset == 0) {
+ int writtenLength = this.unixSocket.write(data, length);
+ if (writtenLength != length) {
+ throw new IOException("write incomplete: length is " + length + " but only wrote " + writtenLength);
+ }
+ }
+ }
+
+ @Override
+ public void write(int data) throws IOException {
+ write(new byte[] { (byte) data },0,1);
+ }
+
+ @Override
+ public void write(byte[] data) throws IOException {
+ write(data,0,data.length);
+ }
+
+}
diff --git a/org/postgresql/jna/UnixSocketFactory.java b/org/postgresql/jna/UnixSocketFactory.java
new file mode 100644
index 0000000..4bea018
--- /dev/null
+++ b/org/postgresql/jna/UnixSocketFactory.java
@@ -0,0 +1,90 @@
+/*-------------------------------------------------------------------------
+ *
+ * Copyright (c) 2003-2011, PostgreSQL Global Development Group
+ *
+ *
+ * Authors: J. Yunke, C. Pritchard.
+ *-------------------------------------------------------------------------
+ */
+
+package org.postgresql.jna;
+
+// Using JNA to access native sockets.
+import com.sun.jna.LastErrorException;
+import com.sun.jna.Library;
+import com.sun.jna.Native;
+import com.sun.jna.Structure;
+
+// Windows not supported by UnixSocketFactory.
+import com.sun.jna.Platform;
+
+// Exceptions.
+import java.io.IOException;
+import java.net.SocketException;
+
+// Unix domain sockets have file entries.
+import java.io.File;
+
+// Implementated interfaces.
+import javax.net.SocketFactory;
+import java.net.Socket;
+
+import java.net.InetAddress;
+
+public class UnixSocketFactory extends SocketFactory {
+ public static final String DEFAULT_LOCATION="/tmp/.s.PGSQL.5432";
+
+// JNA instance.
+ protected static boolean hasInstance = false;
+ protected static UnixSocketLibrary instance = null;
+ protected synchronized static void loadInstance() throws SocketException {
+ if (!hasInstance) {
+ if (Platform.isWindows() || Platform.isWindowsCE()) {
+ throw new SocketException("loadInstance(): Windows support for named sockets is not implemented.");
+ }
+ instance = (UnixSocketLibrary) Native.loadLibrary("c", UnixSocketLibrary.class);
+ hasInstance = true;
+ }
+ }
+
+ @Override
+ public Socket createSocket(String host, int portNumber) throws SocketException, IOException {
+ if(!hasInstance) {
+ loadInstance();
+ }
+
+ String unixSocketPath = host;
+ if (unixSocketPath != null && !"".equals(unixSocketPath.trim())) {
+ File unixSocketFile = new File(unixSocketPath);
+ if (!unixSocketFile.exists()) {
+ throw new SocketException("connect(..): could not find Unix socket at \"" + unixSocketPath + "\"");
+ }
+ } else {
+ unixSocketPath = DEFAULT_LOCATION;
+ File unixSocketFile = new File(unixSocketPath);
+ if (!unixSocketFile.exists()) {
+ throw new SocketException("connect(..): could not find Unix socket in the default location \"" + DEFAULT_LOCATION + "\"");
+ }
+ }
+
+ SockAddr sockAddr = new SockAddr(unixSocketPath);
+ Socket socket = new UnixSocket(instance,sockAddr);
+ return socket;
+ }
+
+ @Override
+ public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
+ return createSocket("", 0);
+ }
+
+ @Override
+ public Socket createSocket(InetAddress host, int port, InetAddress localHost, int localPort) throws IOException {
+ return createSocket("", 0);
+ }
+
+ @Override
+ public Socket createSocket(InetAddress host, int port) throws IOException {
+ return createSocket("", 0);
+ }
+}
+
diff --git a/org/postgresql/jna/UnixSocketLibrary.java b/org/postgresql/jna/UnixSocketLibrary.java
new file mode 100644
index 0000000..60852de
--- /dev/null
+++ b/org/postgresql/jna/UnixSocketLibrary.java
@@ -0,0 +1,23 @@
+/*-------------------------------------------------------------------------
+ *
+ * Copyright (c) 2003-2011, PostgreSQL Global Development Group
+ *
+ *
+ * Authors: J. Yunke, C. Pritchard.
+ *-------------------------------------------------------------------------
+ */
+
+package org.postgresql.jna;
+
+import com.sun.jna.Library;
+import java.nio.ByteBuffer;
+
+public interface UnixSocketLibrary extends Library {
+ public int socket(int domain, int type, int protocol);
+ public int connect(int sockfd, SockAddr sockaddr, int addrlen);
+ public int read(int fd, ByteBuffer buffer, int count);
+ public int write(int fd, ByteBuffer buffer, int count);
+ public int close(int fd);
+ public String strerror(int errno);
+}
+
@kofemann
Copy link

cool! I have updated it to work as jdbc4 driver and on our test system works as expected.

https://github.com/kofemann/pgjdbc/tree/unix-domain-socket

If it's OK for you I will send a pool request to upstream developers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment