{"id":16192214,"url":"https://github.com/cretz/pgnio","last_synced_at":"2025-09-07T01:04:05.809Z","repository":{"id":139465735,"uuid":"124201472","full_name":"cretz/pgnio","owner":"cretz","description":"Asynchronous PostgreSQL client for Java and the JVM","archived":false,"fork":false,"pushed_at":"2023-09-27T05:41:05.000Z","size":210,"stargazers_count":65,"open_issues_count":4,"forks_count":5,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-09-07T01:02:57.628Z","etag":null,"topics":["java","postgresql"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cretz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-03-07T08:08:45.000Z","updated_at":"2025-08-21T12:13:09.000Z","dependencies_parsed_at":null,"dependency_job_id":"3b1b75de-bcf4-4bc1-97fd-cd9313af4bf8","html_url":"https://github.com/cretz/pgnio","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/cretz/pgnio","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cretz%2Fpgnio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cretz%2Fpgnio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cretz%2Fpgnio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cretz%2Fpgnio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cretz","download_url":"https://codeload.github.com/cretz/pgnio/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cretz%2Fpgnio/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273983110,"owners_count":25202095,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-06T02:00:13.247Z","response_time":2576,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["java","postgresql"],"created_at":"2024-10-10T08:09:19.299Z","updated_at":"2025-09-07T01:04:05.738Z","avatar_url":"https://github.com/cretz.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PgNio\n\nPgNio is an asynchronous PostgreSQL client for Java and the JVM. It was built to solve both simple and advanced Postgres\nneeds without being too opinionated or inflexible. Since all protocol features are supported callers can take advanced\napproaches to problems.\n\n[![Javadocs](http://javadoc.io/badge/com.github.cretz.pgnio/pgnio-driver.svg)](http://javadoc.io/doc/com.github.cretz.pgnio/pgnio-driver)\n(`protected` visibility excluded)\n\n### Features/Goals\n\n* Java 8+\n* Simple and small codebase, no dependencies\n* NIO and SSL\n* Notification support\n* `COPY` support\n* Advanced prepared and bound query support including max-rows and suspension\n* Flexible server communication - control when/how data is sent/received, support all protocol features\n* Extensible - almost everything can be extended\n* Low level - can be the base of higher-level Postgres libraries (such as the upcoming async JDBC API or combined with\n  other reactive libs)\n* Extensible yet not embedded/forced parameter and row data serialization from/to Java types\n* Support for date, geom, network, money, hstore, etc data types\n\n### Install\n\nThis is deployed to Maven Central. In maven project:\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.github.cretz.pgnio\u003c/groupId\u003e\n    \u003cartifactId\u003epgnio-driver\u003c/artifactId\u003e\n    \u003cversion\u003e0.2.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nOr in Gradle:\n\n```\ncompile 'com.github.cretz.pgnio:pgnio-driver:0.2.0'\n```\n\n### How To\n\nBelow are simple examples on how to use the client library. The library makes heavy use of composition with\n`CompletableFuture` values which is why some of the code appears quite functional and non-ergonomic. All top-level\nclasses in the library are in the `pgnio` package. While there are synchronous `get` invocations in these examples, in\nnormal use developers might not want to block for a result.\n\n#### Create and use a single connection\n\nTo connect to a database, a `Config` instance is needed. It's a simple class with already-defaulted public fields that\ncan be set directly. Each field also has a corresponding builder method to set its value and return back the `Config`\ninstance. Here is a simple config:\n\n```java\nConfig conf = new Config().hostname(\"myhost\").username(\"myuser\").password(\"mypass\");\n```\n\nSee the `Config` Javadoc for more details on the values. Besides common values, developers are encouraged to set timeout\nvalues that are used for reading/writing from/to the server.\n\nA connection is first connected via the `Connection.init` static method which accepts a `Config`. It is then\nauthenticated by calling auth on the resulting opened connection:\n\n```java\nConnection.init(conf).thenCompose(conn -\u003e conn.auth()).thenCompose(conn -\u003e /* do stuff */);\n```\n\nNote, this example does not block for response or close the connection but normal code would. A shortcut for this is to\njust call `Connection.authed`.\n\nOnce the connection is done, just call `terminate` or pass a future to `terminated`:\n\n```java\nConnection.authed(conf).thenCompose(conn -\u003e conn.terminated(/* do stuff returning future */)).get();\n```\n\nThis example does block using `get` at the end. It also calls `terminated` to close the connection after stuff is done.\nHere is an example of fetching a simple string from a query:\n\n```java\nList\u003cQueryMessage.Row\u003e rows = Connection.authed(conf).thenCompose(conn -\u003e\n    conn.terminated(conn.simpleQueryRows(\"SELECT current_database() AS database_name\"))).get();\nSystem.out.println(\"Current DB: \" + RowReader.DEFAULT.get(rows.get(0), \"database_name\", String.class));\n```\n\n`RowReader` is covered later.\n\n#### Create and use a connection pool\n\nA `ConnectionPool` can be created with a `Config` like a connection and has a `withConnection` method that helps make\nsure connections can be reused:\n\n```java\nConnectionPool pool = new ConnectionPool(conf);\nList\u003cQueryMessage.Row\u003e rows = pool.withConnection(conn -\u003e\n    conn.simpleQueryRows(\"SELECT current_database() AS database_name\")).get();\nSystem.out.println(\"Current DB: \" + RowReader.DEFAULT.get(rows.get(0), \"database_name\", String.class));\n```\n\nThe `Config`'s `poolSize` determines the fixed pool size. While not set by default, developers are encouraged to set\n`Config.poolValidationQuery` to something like `SELECT 1` to make sure borrowed connections are always valid. A\n`ConnectionPool` should be closed after use. For the rest of these examples, the `pool` variable above will be reused.\n\n#### Execute simple queries\n\nTo execute a simple query and retrieve the query result connection state, use `simpleQuery`. This usage requires that\nyou mark the result `done`. There are convenience methods to do this automatically and return values. They are\n`simpleQueryExec` for discarding the result, `simpleQueryRowCount` to get the returned/affected row count, and\n`simpleQueryRows` to get the row list:\n\n```java\npool.withConnection(c -\u003e\n    c.simpleQueryExec(\"CREATE TEMP TABLE foo (bar VARCHAR(100))\").\n        // The result is just of type java.lang.Void anyways, so ignore it\n        thenCompose(__ -\u003e c.simpleQueryRowCount(\"INSERT INTO foo VALUES ('test1'), ('test2')\")).\n        // The result is an integer, so this outputs \"Rows: 2\"\n        thenAccept(rowCount -\u003e System.out.println(\"Rows: \" + rowCount)).\n        // Now select em all\n        thenCompose(__ -\u003e c.simpleQueryRows(\"SELECT * FROM foo\")).\n        // Show the strings\n        thenAccept(rows -\u003e\n            System.out.println(\"Rows: \" + rows.stream().\n                map(row -\u003e RowReader.DEFAULT.get(row, \"bar\", String.class)).collect(Collectors.joining(\", \")))\n        )\n).get();\n```\n\n#### Reading row values\n\nRows are returned as `QueryMessage.Row` objects. These objects include metadata about the columns and the two\ndimensional byte array, with a byte array for each column. Instead of putting the logic to convert from byte arrays\ninside the row class, PgNio offers a `RowReader` class for reading row data. The class may be manually instantiated\nwith custom converters, but most common uses will use the `RowReader.DEFAULT` singleton:\n\n```java\npool.withConnection(c -\u003e\n    c.simpleQueryRows(\"SELECT 'test' AS first_row, 12, '{5, 6}'::integer[]\").\n        thenAccept(rows -\u003e {\n            // Pass in the row, column name, and type to fetch\n            System.out.println(\"Col 1: \" + RowReader.DEFAULT.get(rows.get(0), \"first_row\", String.class));\n            // Can also pass in the zero-based column index\n            System.out.println(\"Col 2: \" + RowReader.DEFAULT.get(rows.get(0), 1, Integer.class));\n            // Even works with arrays\n            System.out.println(\"Col 3: \" + Arrays.toString(RowReader.DEFAULT.get(rows.get(0), 2, int[].class)));\n        })\n).get();\n```\n\nSee the Javadoc for more information on custom column value converters. See the [Data Types](#data-types) section below\nfor more information on supported data types.\n\n#### Execute queries with parameters\n\nIn the [PostgreSQL protocol](https://www.postgresql.org/docs/current/static/protocol.html), there are two ways to submit\nqueries. One is the simple query form which issues a query and gets row metadata and row data. These are the calls\nprefixed with \"simple\". The other way is the \"advanced\" or \"prepared\" approach which separates the steps to parse the\nquery, bind parameters, describe the result, and execute the query. The \"simple\" approach can be seen as just combining\nthose 4 steps together in one call on the server side. PgNio offers separate calls for each of these steps allowing the\ncaller to choose when/how they are called. There are also \"prepared\" convenience methods analogous to the \"simple\"\nconvenience methods which invoke all of these steps internally:\n\n```java\npool.withConnection(c -\u003e\n    // Ask for a series from 1 through a parameter (4 in this case)\n    c.preparedQueryRows(\"SELECT * FROM generate_series(1, $1)\", 4).\n        // Will be a count of 4\n        thenAccept(rows -\u003e System.out.println(\"Row count: \" + rows.size()))\n).get();\n```\n\nInternally, PgNio uses a `ParamWriter` instance (configured with a default via `Config.paramWriter`) to convert from\nJava types to PostgreSQL parameters. See the [Data Types](#data-types) for more information on suggested data types for\ncertain parameter types.\n\n#### Reuse prepared queries\n\nThe prepared queries above are \"unnamed\" (internally they use an empty string as the name) which means they can't easily\nbe reused. PgNio supports named prepared queries which are stored for the life of the connection or until closed. Unlike\nunnamed prepared queries, there aren't convenience methods to create a named query, but convenience methods can be used\nfor binding, executing, and retrieving rows:\n\n```java\npool.withConnection(c -\u003e\n    c.simpleQueryExec(\"CREATE TEMP TABLE foo (bar VARCHAR(100))\").\n        thenCompose(__ -\u003e c.prepareReusable(\"myquery\", \"INSERT INTO foo VALUES ($1)\")).\n        // We would use bindDescribeExecuteAndDone if this were a select\n        thenCompose(prepared -\u003e prepared.bindExecuteAndDone(\"test1\")).\n        thenCompose(result -\u003e result.done()).\n        // Count will be 1\n        thenCompose(__ -\u003e c.simpleQueryRows(\"SELECT COUNT(1) FROM foo\")).\n        thenAccept(rows -\u003e\n            System.out.println(\"Count: \" + RowReader.DEFAULT.get(rows.get(0), 0, Long.class))).\n        // Reuse the query\n        thenCompose(__ -\u003e c.reusePrepared(\"myquery\")).\n        thenCompose(prepared -\u003e prepared.bindExecuteAndDone(\"test2\")).\n        thenCompose(result -\u003e result.done()).\n        // Count will be 2\n        thenCompose(__ -\u003e c.simpleQueryRows(\"SELECT COUNT(1) FROM foo\")).\n        thenAccept(rows -\u003e\n            System.out.println(\"Count: \" + RowReader.DEFAULT.get(rows.get(0), 0, Long.class))).\n        // Try to close the statement regardless of error\n        handle((__, ex) -\u003e\n            c.reusePrepared(\"myquery\").\n                thenCompose(prepared -\u003e prepared.closeStatement()).\n                thenCompose(prepared -\u003e prepared.done()).\n                thenCompose(result -\u003e result.done()).\n                thenAccept(___ -\u003e { if (ex != null) throw new RuntimeException(ex); })).\n        thenCompose(Function.identity())\n).get();\n```\n\nNote, \"life of the connection\" means as long as the socket is open to the server. So when using a connection pool,\ndevelopers should always close their prepared statements or they will remain open as long as the connection does.\n\n#### Use transactions\n\nThe regular \"ready for query\" connection state is the `QueryReadyConnection.AutoCommit` class which automatically\ncommits everything. Running `beginTransaction` on it returns a `QueryReadyConnection.InTransaction` class which won't\nreturn back to auto commit mode until `commitTransaction` or `rollbackTransaction` is executed. Example:\n\n```java\npool.withConnection(c -\u003e\n    c.simpleQueryExec(\"CREATE TEMP TABLE foo (bar VARCHAR(100))\").\n        // Start the transaction\n        thenCompose(__ -\u003e c.beginTransaction()).\n        // Insert a value\n        thenCompose(txn -\u003e txn.simpleQueryExec(\"INSERT INTO foo VALUES ('test')\").thenApply(__ -\u003e txn)).\n        // Count should be 1\n        thenCompose(txn -\u003e\n            txn.simpleQueryRows(\"SELECT COUNT(1) FROM foo\").thenApply(rows -\u003e {\n                System.out.println(\"Count: \" + RowReader.DEFAULT.get(rows.get(0), 0, Long.class));\n                return txn;\n            })).\n        // Roll it back\n        thenCompose(txn -\u003e txn.rollbackTransaction()).\n        // Count should be 0\n        thenCompose(conn -\u003e conn.simpleQueryRows(\"SELECT COUNT(1) FROM foo\")).\n        thenAccept(rows -\u003e\n            System.out.println(\"Count: \" + RowReader.DEFAULT.get(rows.get(0), 0, Long.class)))\n).get();\n```\n\nTransactions can also be nested which is internally supported via savepoints.\n\n#### Listen for notifications\n\nPostgreSQL has `LISTEN`/`NOTIFY` support which allows pub/sub. PgNio allows subscription to these messages on a per\nconnection basis. Once subscribed to the messages, it must be read from the server side. This will happen during normal\nquery operations since a notification is sent along with other messages. But if not querying, developers need to wait\nwhile reading for a message, which can be done via `unsolicitedMessageTick` and a timeout.\n\n```java\n// Create a listener\nCompletableFuture listener = pool.withConnection(c -\u003e {\n    // Subscribe to the notification\n    c.notifications().subscribe(notification -\u003e {\n        System.out.println(\"Got: \" + notification.payload);\n        // This function requires a future result so it can continue on its way.\n        // Here we just return a completed nothing, but developers could listen for another message if they wanted.\n        return CompletableFuture.completedFuture(null);\n    });\n    // Let PostgreSQL know we're listening\n    return c.simpleQueryExec(\"LISTEN my_notifications\").\n        // Wait for 30 seconds for a single message.\n        // To listen for more messages, we'd have to call this again.\n        thenCompose(__ -\u003e c.unsolicitedMessageTick(30, TimeUnit.SECONDS));\n});\n\n// Send a notification\npool.withConnection(c -\u003e c.simpleQueryExec(\"NOTIFY my_notifications, 'test1'\")).get();\n\n// Wait for listener to end\nlistener.get();\n```\n\nIn addition to notifications, developers can also listen for notices and server parameter/option changes (e.g. time zone\nchange). Note, when a connection is returned to a pool, all of its subscriptions are cleared. Same thing when a\nconnection is terminated. Therefore, developers who want to listen to notifications for a longer period should consider\ncreating a longer lived connection or just never giving the connection back to the pool.\n\n#### Copy to a table\n\nPostgreSQL supports a fast insert mode called a [COPY](https://www.postgresql.org/docs/current/static/sql-copy.html) and\nPgNio supports it. Here's how to insert some CSV values:\n\n```java\npool.withConnection(c -\u003e\n    c.simpleQueryExec(\"CREATE TEMP TABLE foo (bar VARCHAR(100), baz integer)\").\n        // Begin copy\n        thenCompose(__ -\u003e c.simpleCopyIn(\"COPY foo FROM STDIN CSV\")).\n        thenCompose(copy -\u003e copy.sendData(\"test1,123\\n\".getBytes(StandardCharsets.UTF_8))).\n        thenCompose(copy -\u003e copy.sendData(\"test2,456\\n\".getBytes(StandardCharsets.UTF_8))).\n        thenCompose(copy -\u003e copy.done()).\n        // Count should be 2\n        thenCompose(__ -\u003e c.simpleQueryRows(\"SELECT COUNT(1) FROM foo\")).\n        thenAccept(rows -\u003e\n            System.out.println(\"Count: \" + RowReader.DEFAULT.get(rows.get(0), 0, Long.class)))\n).get();\n```\n\nThere are other formats including the default text format. `ParamWriter` can be used to help with this.\n\n#### Copy from a table\n\nCopying can also occur when reading out from a table:\n\n```java\npool.withConnection(c -\u003e\n    c.simpleQueryExec(\"CREATE TEMP TABLE foo (bar VARCHAR(100), baz integer);\" +\n            \"INSERT INTO foo VALUES ('test1', 123), ('test2', 456)\").\n        thenCompose(__ -\u003e c.simpleCopyOut(\"COPY foo TO STDOUT CSV\")).\n        thenCompose(copy -\u003e {\n            ByteArrayOutputStream bytes = new ByteArrayOutputStream();\n            return copy.\n                receiveEachData(b -\u003e {\n                    try { bytes.write(b); }\n                    catch (IOException e) { throw new RuntimeException(e); }\n                }).\n                thenAccept(__ -\u003e\n                    System.out.println(\"Got:\\n\" + new String(bytes.toByteArray(), StandardCharsets.UTF_8))).\n                thenCompose(__ -\u003e copy.done());\n        })\n).get();\n```\n\n#### Cancelling a query\n\nIn PostgreSQL, a long-running query cannot simply be cancelled within the same connection. Instead, a separate\nconnection must be created solely to cancel using the original connection's process ID and secret key:\n\n```java\n// We'll just set the process ID and secret key into an int array\nCompletableFuture\u003cint[]\u003e processIdAndSecretKey = new CompletableFuture\u003c\u003e();\n\n// Run a query for 10 seconds\nCompletableFuture longQuery = pool.withConnection(c -\u003e {\n    // Set the process ID and secret key of this connection\n    processIdAndSecretKey.complete(new int[] { c.getProcessId(), c.getSecretKey() });\n    // Wait 10 seconds\n    return c.simpleQueryExec(\"SELECT pg_sleep(10)\");\n});\n\n// Kill that query\nprocessIdAndSecretKey.thenCompose(idAndKey -\u003e\n    Connection.init(conf).thenCompose(c -\u003e c.cancelOther(idAndKey[0], idAndKey[1]))).get();\n\n// This will throw an exception since it was cancelled\nlongQuery.get();\n```\n\nNote, the newly created connection doesn't have to be explicitly closed/terminated because it is implied with\n`cancelOther`.\n\n#### More...\n\nMany more cases are not covered here but can be learned from the code or test cases including:\n\n* Advanced handling of query results including asking for one row at a time, skipping results, etc\n* Fetching a maximum bound-query row set then fetching more\n* Nested transactions\n* Fetching results from multiple queries\n* Using `flush` instead of `done` on prepared/bound queries\n* Describing prepared statements to get parameter requirements\n* Custom `Converters` for `RowReader` and/or `ParamWriter`\n* `Notice` use and subscription\n* `SSL` including use of custom `SSLContext`s to validate keys\n\n### Data types\n\nBelow is a table of PostgreSQL types and their suggested Java data type. Some Java types can be used for multiple\nPostgreSQL types and some PostgreSQL types can be represented by multiple Java types. These are listed in the order\nthey appear in the [PostgreSQL data type documentation](https://www.postgresql.org/docs/current/static/datatype.html)\n\n| PostgreSQL Type | Java Type\n| --- | --- |\n| `smallint` | `java.lang.Short` |\n| `integer` | `java.lang.Integer` |\n| `bigint` | `java.lang.Long` |\n| `decimal` | `java.lang.BigDecimal`\u003csup\u003e1\u003c/sup\u003e |\n| `numeric` | `java.lang.BigDecimal`\u003csup\u003e1\u003c/sup\u003e |\n| `real` | `java.lang.Float` |\n| `double precision` | `java.lang.Double` |\n| `smallserial` | `java.lang.Short` |\n| `serial` | `java.lang.Integer` |\n| `bigserial` | `java.lang.Long` |\n| `money` | `pgnio.DataType.Money` |\n| `varchar(n)` | `java.lang.String` |\n| `char(n)` | `java.lang.String` |\n| `text` | `java.lang.String` |\n| `bytea` | `byte[]` |\n| `timestamp without time zone` | `java.time.LocalDateTime` |\n| `timestamp with time zone` | `java.time.OffsetDateTime` |\n| `date` | `java.time.LocalDate` |\n| `time without time zone` | `java.time.LocalTime` |\n| `time with time zone` | `java.time.OffsetTime` |\n| `interval` | `pgnio.DataType.Interval` |\n| `boolean` | `java.lang.Boolean` |\n| enumerated types | `java.lang.String` |\n| `point` | `pgnio.DataType.Point` |\n| `line` | `pgnio.DataType.Line` |\n| `lseg` | `pgnio.DataType.LineSegment` |\n| `box` | `pgnio.DataType.Box` |\n| `path` | `pgnio.DataType.Path` |\n| `polygon` | `pgnio.DataType.Polygon` |\n| `circle` | `pgnio.DataType.Circle` |\n| `inet` | `pgnio.DataType.Inet` |\n| `cidr` | `pgnio.DataType.Inet` |\n| `macaddr` | `pgnio.DataType.MacAddr` |\n| `macaddr8` | `pgnio.DataType.MacAddr` |\n| `bit(n)` | `java.lang.String` |\n| `bit varying(n)` | `java.lang.String` |\n| `tsvector` | `java.lang.String` |\n| `tsquery` | `java.lang.String` |\n| `uuid` | `java.util.UUID` |\n| `xml` | `java.lang.String` |\n| `json` | `java.lang.String` |\n| `jsonb` | `java.lang.String` |\n| arrays | arrays |\n| `hstore` | `java.util.Map\u003cString, String\u003e` |\n| all other types | `java.lang.String` |\n\nNotes:\n\n1. If `decimal` or `numeric` are expected to ever be NaN or infinity, users might prefer to deserialize to `String`\n   first before converting to `BigDecimal`. Otherwise an exception occurs. For parameters that need to be NaN or\n   infinity, consider using a float or double.\n\n### FAQ\n\n#### Why was this built?\n\nMy company needs a non-blocking PostgreSQL Java driver that is simple and yet can be used for advanced items. The other\nones carry unnecessary dependencies, are opinionated on what they make visible, aren't very configurable with\nserialization, don't allow flexible use of the protocol, don't support all PostgreSQL features, and/or are\nunmaintained (I've opened issues or made PRs on some of them). Granted there is no guarantee that this one will be\nmaintained forever either.\n\nAs mentioned in the features/goals section, this library is simple, extensible, and both low-level + high-level.\nSerialization concerns are separated from protocol use. I also wanted to build this in preparation for the upcoming\nasync JDBC API and to develop a deep understanding of the PostgreSQL protocol.\n\n#### Is \"asynchronous\" or \"non-blocking\" really better?\n\nNo. Sometimes it is when you don't want to use a thread per connection though internally NIO leverages thread\ngroups/pools. Also, since PostgreSQL's protocol doesn't support multiplexing a single connection there is even less\nbenefit than there might be with other protocols. Having said that, rarely is it worse and this library could easily be\nused in a higher-level, synchronous, blocking application or library.\n\n#### Why aren't there built-in conversions for lists, sets, etc?\n\nIn order to make this library simple, only the practical converters are included. Those collections can easily be\nderived from arrays and/or custom converters can easily be written to build them.\n\n#### Why don't the conversions support `Type` lookups instead of `Class` lookups?\n\nFor the `RowReader`, the `get` accepts a `Class` instead of a `Type`. There was no need using the current converters\nto support generic types, but this may change in the future.\n\n#### Why don't the conversions look up by implemented interface instead of just superclass?\n\nFor the current set of converters, simply traversing the class hierarchy to find a suitable conversion was good enough.\nIf there is a need for a converter for an interface, this could be supported in the future.\n\n#### Why can't reading an `hstore` into a `Map` use the key and value types?\n\nThis library only supports `hstore` converting to a `Map\u003cString, String\u003e`. One might assume that, like arrays, it should\nallow map values of other types that recursively does conversions on them. But PostgreSQL doesn't tell you the value\ntypes of `hstore`. It was decided to perform the simple conversion. There is a `RowReader.get` call that accepts a\nstring if the caller wants to convert further, but it was decided that this library would not do it for them.\n\n#### What about binary formatted parameters and results?\n\nPostgreSQL has two formats in the protocol for parameters and results: binary and text. Right now, PgNio only supports\nthe text format (the default). The text format sends everything as normal strings and is portable across PostgreSQL\nversions. This is usually good enough for almost all purposes. However, as more use cases for binary formatting come\nabout, it very well might be implemented in the library. In the meantime, the library is built to be extensible enough\nthat `ParamWriter`s and `RowReader`s operate purely on bytes and anyone can write binary formatters. Also, all protocol\ncalls that support specifying text or binary format are exposed to let the caller choose if they want.\n\n### Development\n\n#### Style\n\nPgNio gladly accepts pull requests. In general the style is two-space indent, 120-char line max, and try to be clean\nwith line wrapping ideally with punctuation at the end of the line instead of the start. Since this is also a library\nthat can be used as a basis for others, we prefer to set the visibility protected instead of private or package-private\nfor anything that could have any value to anyone. We prefer fields over getters, nested classes over a bunch of files, simpler code over longer code, and clarity over confusion.\n\nThe [checker framework](https://checkerframework.org/) is used mainly to check nullability. This is preferred over\nruntime checks for this library. Sometimes the initialization constraints get in the way, so feel free to mark code\n`@SuppressWarnings(\"initialization\")`.\n\n#### Building\n\nThe project can be built with Gradle. Unlike other projects, PgNio does not bundle a Gradle wrapper script with the\nrepository. Simply download Gradle to `some/path` and run:\n\n    some/path/bin/gradle --no-daemon :driver:assemble\n\nGranted `--no-daemon` is just a choice that some choose to not keep a running Java process in the background, but it\nwill be a slower build. Also, the [checker framework](https://checkerframework.org/)'s annotation processor slows down\ncompilation quite a bit.\n\n#### Testing\n\nThe unit tests are more like integration tests in that they actually run a PostgreSQL instance as an\n[embedded PostgreSQL server](https://github.com/yandex-qatools/postgresql-embedded/). It will automatically download\nitself and create directories as needed in `~/.embedpostgresql`. To run all tests, simply:\n\n    some/path/bin/gradle --no-daemon :driver:test\n\nBy default it chooses the latest PostgreSQL version configured in the library (`10.2` as of this writing). A different\nversion can be used by setting the version number that appears in the\n[download link](https://www.enterprisedb.com/download-postgresql-binaries) as the system property\n`pgnio.postgres.version`. It is usually just the version with `-1` appended. So to test against `9.6.7`:\n\n    some/path/bin/gradle --no-daemon :driver:test -Dpgnio.postgres.version=9.6.7-1\n\nNote, on Windows sometimes the process remains open or there are other oddities. Developers may have to kill the\nprocesses themselves and/or make sure the data files at `~/.embedpostgresql/data` are actually deleted (that is\nthe `C:\\Users\\username\\.embedpostgresql\\data` directory).\n\n#### Using Latest Master\n\nFor updates that may not have been released into a numbered version, developers can use\n[JitPack](https://jitpack.io/#cretz/pgnio/master-SNAPSHOT). Essentially this means using the JitPack resolver in the\nbuild tool, and setting a dependency on the group `com.github.cretz`, name `pgnio`, and version `master-SNAPSHOT`.\n\n#### Java 9+\n\nWhen using Java 9 or newer to compile, the checker framework\n[cannot perform checks](https://github.com/typetools/checker-framework/issues/1224) so it is disabled. For this reason,\ndevelopers ar encouraged to use Java 8 when compiling the `driver` project.\n\n#### ADBA Support\n\n**NOTE: Work on ADBA support has been suspended. See [this issue](https://github.com/cretz/pgnio/issues/8) for more\ninfo.**\n\nAsynchronous database access support (a.k.a. ADBA, JDBC-Next, async JDBC, java.sql2, etc) is currently in development\nin the `adba` subproject which uses Java 9. This means that Java 9+ must be used to compile it which, as mentioned\nabove, disables checker framework checks.\n\nADBA support requires the ADBA source which is available from the\n[OpenJDK sandbox](http://hg.openjdk.java.net/jdk/sandbox/file/9d3b0eb749a9/src/jdk.incubator.adba) as of this writing.\nDevelopers have to compile it to use it; [here](https://gist.github.com/cretz/fb21718d2456fe5d581c9d536c011d99) is a\n`build.gradle` script that will build the ADBA JAR when `assemble` is run. Once the JAR is available, the full path to\nthe JAR must be set as the `adba.jar.path` system property when running the `adba` build in this project. E.g.:\n\n    some/path/bin/gradle --no-daemon :adba:assemble -Dadba.jar.path=/full/path/to/jdk.incubator.adba.jar\n\nOr if you are using an IDE such as IntelliJ this can be set as a Gradle option in the settings.\n\n### TODO\n\n* Streaming/logical replication\n* Support other authentication options","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcretz%2Fpgnio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcretz%2Fpgnio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcretz%2Fpgnio/lists"}