PostgresClientKit logs internal events of interest. To change the log verbosity, set the log level of the Postgres.logger
in your Swift code. For example:
Postgres.logger.level = .all
By default, the log is written to stdout
. Change this by setting the log handler of the Postgres.logger
. For details, see the API documentation for the LogHandler
protocol.
In troubleshooting, it can also be useful for your Swift application to receive log messages from the Postgres server. Enable this by setting the Postgres configuration parameter named client_min_messages
(for example, in the postgresql.conf
file for your Postgres server). For example:
client_min_messages = debug5
Then, do one or both of the following:
-
Increase the
Postgres.logger
level to.finer
or higher, as described above. The log messages received from the Postgres server will be logged by PostgresClientKit. -
Create a class that conforms to the
ConnectionDelegate
protocol and implements theconnection(_:didReceiveNotice:)
method. Set thedelegate
property of theConnection
to an instance of this class. (Note thatdelegate
is aweak
property ofConnection
, so your code must also hold its own reference to the delegate.)
Confirm you can connect using psql
, explicitly specifying the host, port, database, and username. For example:
psql --host 127.0.0.1 --port 5432 --dbname example --username bob
In the postgresql.conf
file for your Postgres server, confirm that:
ssl = on
Review the pg_hba.conf
file for your Postgres server. PostgresClientKit supports the trust
, password
, md5
, and scram-sha-256
options for auth-method
.
The deinitializers of Connection
, Statement
, and Cursor
call close()
on instances of those types. This is normally a convenient way to ensure Postgres resources are released, but can have unexpected side effects, particularly for Statement
. For example:
// Perform a first SQL command.
var statement = try connection.prepareStatement(text: ...)
var cursor = try statement.execute()
...
// Perform a second SQL command, re-using the same variables.
statement = try connection.prepareStatement(text: ...) // closes first cursor
cursor = try statement.execute() // triggers deinit of first cursor & statement
assert(!cursor.isClosed) // fails!
...
In this code fragment, the second assignment to the cursor
variable releases the only reference to the first Cursor
instance, allowing it to be deinitialized and then deallocated. This, in turn, releases the only reference to the first Statement
instance, and it too is deinitialized and deallocated. The deinitializer for Statement
calls close()
which, as a side effect, closes any open cursor for the connection, in this case the just-created second Cursor
instance. Doh!
You can prevent this in several ways:
-
Explicitly
close()
the firstStatement
before preparing the second. -
Assign the second
Statement
instance to a different variable than the first. -
Perform each SQL command within its own
do { }
block, so that the firstStatement
is deinitialized and deallocated before starting the second SQL command.