Skip to content

Commit

Permalink
PostgreSQL SSL support (#1044)
Browse files Browse the repository at this point in the history
Add support for SSL in PostgreSQL client
  • Loading branch information
fare authored Nov 21, 2023
1 parent ee8beee commit 8dd0e65
Show file tree
Hide file tree
Showing 23 changed files with 430 additions and 175 deletions.
2 changes: 1 addition & 1 deletion doc/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ module.exports = {
"db/",
"db/dbi",
"db/sqlite",
"db/postgres",
"db/postgresql",
"db/conpool",
]
},
Expand Down
31 changes: 8 additions & 23 deletions doc/reference/std/db/dbi.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@

(import :std/db/dbi)


<a id="orgc142ed3"></a>

## sql-connect

First things first we must connect to a database. Though the actual function to
Expand All @@ -16,8 +13,6 @@ connection is automagically closed using [`sql-close`](#sqlClose) when garbage c
;; => #<postgresql-connection #36>


<a id="org1572080"></a>

## connection?

This predicate asks if the thing passed is in fact a connection.
Expand All @@ -26,8 +21,6 @@ This predicate asks if the thing passed is in fact a connection.
(connection? car) ;; => #f


<a id="sqlEval"></a>

## sql-eval

Often when interacting with the **DB** we do not actually need a result.
Expand All @@ -44,8 +37,6 @@ It can take arguments.
(sql-eval db "INSERT INTO foo VALUES ($1)" "yay!") ;; => #!void


<a id="sqlEvalQuery"></a>

## sql-eval-query

The **R** in **CRUD** is likely what is most often used.
Expand Down Expand Up @@ -73,18 +64,16 @@ arguments as well.
;; => ("huh?" "bar" "bar")


<a id="sqlPrepare"></a>

## sql-prepare

Often an evaluation of a query is not enough.

- There&rsquo;s the simple matter of column names as we only return a list of
- Theres the simple matter of column names as we only return a list of
results.

- We may want to pass arguments.

For that purpose there&rsquo;s a prepared statement. They are `willed to run`
For that purpose theres a prepared statement. They are `willed to run`
[`sql-finalize`](#sqlFinalize) before taking out the trash.

(def istmt (sql-prepare db "INSERT INTO foo VALUES ('bar') RETURNING foo.*;"))
Expand All @@ -95,8 +84,6 @@ For that purpose there&rsquo;s a prepared statement. They are `willed to run`
(def bind-stmt (sql-prepare db "SELECT true AS funnyColumn, * FROM foo WHERE bar = $1"))


<a id="org7432ad3"></a>

## statement?

Is this an **SQL Statement**?
Expand All @@ -105,11 +92,9 @@ Is this an **SQL Statement**?
(statement? 'foobar) ;; =? #f


<a id="orgacd84d5"></a>

## sql-columns

We&rsquo;ve got some predicative-ly confirmed prepared statements. `sql-columns` gives
Weve got some predicative-ly confirmed prepared statements. `sql-columns` gives
us the column names.

(map sql-columns [ istmt bind-istmt stmt bind-stmt ])
Expand Down Expand Up @@ -149,15 +134,15 @@ as well.
; prepared statement "stmt539" requires 1) (F . postgres.c) (L . 1665) (R .
; exec_bind_message)

That&rsquo;s because arguments need to be bound with [`sql-bind`](#sqlBind).
Thats because arguments need to be bound with [`sql-bind`](#sqlBind).


<a id="sqlQuery"></a>

## sql-query

Similar to [`sql-eval-query`](#sqlEvalQuery) `sql-query` returns the results of the
[`sql-prepare`](#sqlPrepare)&rsquo;d statement in list form.
[`sql-prepare`](#sqlPrepare)d statement in list form.

(sql-query stmt) => ("(bar)" "(bar)" "(bar)")

Expand Down Expand Up @@ -188,7 +173,7 @@ especially with [reset](#sqlReset), or even [reset/clear](#sqlResetClear).

## sql-bind

For [`sql-prepare`](#sqlPrepare)&rsquo;d statements that take arguments `sql-bind` sets them to the
For [`sql-prepare`](#sqlPrepare)d statements that take arguments `sql-bind` sets them to the
value before we run the statement.

(sql-bind bind-istmt "yay!") ;; => #!void
Expand Down Expand Up @@ -216,7 +201,7 @@ A statement can be rebound.
(sql-query stmt)
;; => ("(bar)" "(bar)" "(bar)" "(yay!)" "(yay!)" "(huh?)")

It can also be [cleared](#sqlClear), [reset](#sqlReset), or even [reset/clear](#sqlResetClear)&rsquo;d.
It can also be [cleared](#sqlClear), [reset](#sqlReset), or even [reset/clear](#sqlResetClear)d.


<a id="sqlClear"></a>
Expand Down Expand Up @@ -314,7 +299,7 @@ one is.

## sql-txn-commit

Commits a transaction if there&rsquo;s one that can be commited. It errors if the
Commits a transaction if theres one that can be commited. It errors if the
transaction cannot be commited and otherwise, if there is no transaction, does
nothing at all.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,59 @@
# PostgreSQL driver

(import :std/db/postgresql)
The `:std/db/postgresql` library provides a driver for a PostgreSQL database.

Have a look at [the postgresql-test.ss file](https://github.com/mighty-gerbils/gerbil/blob/master/src/std/db/postgresql-test.ss) to see more of how it is used with
the `:std/db/dbi`.
::: tip To use the bindings from this module:
```scheme
(import :std/db/postgresql)
```
:::


<a id="org04d26f9"></a>
Have a look at [the postgresql-test.ss file](https://github.com/mighty-gerbils/gerbil/blob/master/src/std/db/postgresql-test.ss)
to see more of how it is used with the `:std/db/dbi`.

## postgresql-connect

The first way is simply to use the function.

(postgresql-connect host: "localhost" user: "foo" passwd: "bar")
;; => #<postgresql-connection #36>

But, often, we may want to close the connection when garbage collected so we,
the developer, don&rsquo;t need to worry about hanging connections. Thus,
`std/db/dbi#sql-connect` is often the better choice as it `will`&rsquo;s the
```scheme
(postgresql-connect url
[ssl: 'try] [ssl-context: (default-client-ssl-context)] [timeout: #f])
OR
(postgresql-connect [host: "localhost"] user: u passwd: p db: d
[ssl: 'try] [ssl-context: (default-client-ssl-context)] [timeout: #f])
=> postgresql-connection
```

To connect to a database, you can simply to use the function.
Either provide a `url` as a positional argument
(defaults to `#f` which designates using the mechanism below instead),
or provide separately the `host`, `port`, `user`, `passwd`, `db`,
which all default to `#f`.
The `url` and its components roughly follow the same meaning as in `libpq`:
https://www.postgresql.org/docs/current/libpq-connect.html

A `host` value of `#f` designates the local address `"127.0.0.1"`.
A `port` value of `#f` designates port `5432`.
A `user` value of `#f` designates the value of the `USER` environment variable.
A `passwd` value of `#f` designates the empty password `""`.
A `db` value of `#f` designates the same value as `user`.

Either way, you may specify the keyword arguments
`ssl` (defaults to `'try`), which unless false will cause an SSL connection to be attempted,
though unless it is `#t` will not cause an error if SSL is unsupported by the server.
The `ssl-context` will be used for the connection, as well as the `timeout`.

Now, often, we may want to close the connection when garbage collected so we,
the developer, don’t need to worry about hanging connections. Thus,
[`std/db/dbi#sql-connect`](dbi.md#sql-connect) is often the better choice as it `will`’s the
`sql-close` into being.

(import :std/db/dbi)
(def pg (sql-connect postgresql-connect host: "localhost" user: "foo" passwd: "bar"))
;; => #<postgresql-connection #36>


<a id="defcatalog"></a>

## defcatalog, Postgresql->Gerbil->Postgresql mapping

A catalog tells us what to do with what postgresql gives us.

Here&rsquo;s the basic syntax.
Heres the basic syntax.

((_ (name mixin ...) (oids serialize deserialize) ...)

Expand All @@ -43,15 +65,12 @@ A mixin is not always needed. Here is our first version.
;; INT8OID INT2OID INT4OID FLOAT4OID FLOAT8OID NUMERICOID
((20 21 23 700 701 1700) (lambda _ "42") (lambda _ 42)))

Try it out by `parameterize`&rsquo;ing the [`current-catalog`](#currentCatalog).
Try it out by `parameterize`ing the [`current-catalog`](#currentCatalog).

(parameterize ((current-catalog my-default-catalog))
(sql-eval-query pg "SELECT 1, FALSE WHERE $1" 'hey-you))
;; => (#(42 42))


<a id="orgd307da2"></a>

## defcatalog-default

By default there is a [`default-catalog`](#defaultCatalog). If we want to declare a new type within
Expand All @@ -67,7 +86,7 @@ For example, **PostgreSQL** has a **JSON** type.

And, **Gerbil** does as well! Actually, the `:std/test/json` just turns it into a hash table.

First we see the `oid`&rsquo;s for postgres&rsquo; json types. Select them as JSON to see
First we see the `oid`s for postgres json types. Select them as JSON to see
that as well.

(import :std/text/json)
Expand All @@ -89,15 +108,15 @@ that as well.
;; => (((json . "114")) ((jsonb . "3802")) ((jsonpath . "4072"))
;; ((_json . "199")) ((_jsonb . "3807")) ((_jsonpath . "4073")))

All we need is to (de)serialize them&#x2026;
All we need is to (de)serialize them

(def (serialize-json gerbil-json)
(call-with-output-string "" (cut write-json gerbil-json <>)))

(def (deserialize-json str)
(call-with-input-string str read-json))

&#x2026; and add them to the default catalog.
and add them to the default catalog.

(defcatalog-default ((114 3802) serialize-json deserialize-json))

Expand All @@ -115,9 +134,6 @@ Even better, we can pass them to queries!
(sql-query stmt))))
;; => (("key" "value") #("bar" "baz") #("foo" "1"))


<a id="defaultCatalog"></a>

## default-catalog

What if we only want to change certain things from the default and not have them
Expand Down Expand Up @@ -147,8 +163,6 @@ Which works as expected.
;; year: 2021 zone-offset: 0> 42))


<a id="currentCatalog"></a>

## current-catalog

The `current-catalog` parameter determines which catalog is used by default.
Expand All @@ -171,7 +185,7 @@ Use it to declare a global default.

(sql-eval-query pg "SELECT 1") ;; => (42)

Don&rsquo;t forget to set it back :).
Dont forget to set it back :).

(current-catalog default-catalog)

Expand Down
Loading

0 comments on commit 8dd0e65

Please sign in to comment.