sql

All things SQL and JDBC...
Arjaz 2021-06-23T08:38:38.252100Z

Hi! I'm using next.jdbc, how can I do something like that? (delete! ds :users ["email IN ?" a-set-of-mine]) How do I use placeholders with the IN keyword?

indy 2021-06-23T11:49:14.252200Z

(delete! ds :users ["email IN (?, ?)" "a@com" "b@com"])

indy 2021-06-23T11:50:29.252400Z

The code to programmatically build such a query from a collection can be prone to injection. I would highly recommend using honeysql. It supports adding in clauses that take a collection as the parameter and builds the expected query for you

emccue 2021-06-23T13:30:26.252800Z

Also depends on your database driver

emccue 2021-06-23T13:31:12.253Z

like, I think the postgres driver lets you use "IN" with a primitive array

seancorfield 2021-06-23T15:44:39.253200Z

Yes, there’s even an example of that last suggestion in the docs.

unbalanced 2021-06-23T16:52:09.254500Z

I'm probably overlooking something obvious, is there a way to create a database with jdbc-next?

(with-open [conn (pg-autoconn/conn! :postgres-config)]
    (jdbc/execute-one! conn [create-db-cmd] ))
results in
1. Unhandled org.postgresql.util.PSQLException
   ERROR: CREATE DATABASE cannot run inside a transaction block

unbalanced 2021-06-23T17:02:13.254800Z

(jdbc/execute-one!
   (doto (jdbc/get-connection (pg-autoconn/auto-conn-creds! "postgres")
                              {:auto-commit true, :read-only false})
     (.setCatalog "postgres"))
   [create-db-cmd])
that did it

unbalanced 2021-06-23T17:02:36.255200Z

it was :auto-commit false and the with-open, I think, that was messing me up

seancorfield 2021-06-23T17:04:57.256300Z

@goomba Unlike clojure.java.jdbc, next.jdbc does not create any implicit transactions. However, different databases have different rules about how DDL interacts with the auto-commit status on a connection.

seancorfield 2021-06-23T17:05:23.256800Z

In your second piece of code, you are leaking a connection so you should use with-open.

seancorfield 2021-06-23T17:06:24.257900Z

(with-open [con (jdbc/get-connection ..)]
  (.setCatalog con "postgres")
  (jdbc/execute-one! con [create-db-cmd]))

seancorfield 2021-06-23T17:07:10.258800Z

I can’t speak to what options you may or may not need to pass to get-connection for this. For MySQL, we do not need to pass any options for CREATE DATABASE.

unbalanced 2021-06-23T17:07:26.259100Z

aha! great catch

unbalanced 2021-06-23T17:07:55.259400Z

works beautifully!

seancorfield 2021-06-23T17:10:14.260400Z

Some databases do not allow DDL inside transactions — but I don’t see a transaction in your first code block so that’s a bit puzzling. But, hey, SQL! amirite?

unbalanced 2021-06-23T17:16:57.262200Z

hahha. the pg-autoconn/auto-conn! calls down to

(doto (jdbc/get-connection datasource {:auto-commit false, :read-only false})
    (.setCatalog "postgres"))
, and I believe it was the :auto-commit false along with Postgres quirks that was causing the issue in the first code block

unbalanced 2021-06-23T17:17:49.263100Z

I infer that the .close of the with-open triggered a transaction, perhaps? I have no idea. It works now though. SQL! :party-corgi:

indy 2021-06-23T17:19:23.264Z

This is from https://docs.oracle.com/javase/tutorial/jdbc/basics/transactions.html#disable_auto_commit When a connection is created, it is in auto-commit mode. This means that each individual SQL statement is treated as a transaction and is automatically committed right after it is executed. (To be more precise, the default is for a SQL statement to be committed when it is completed, not when it is executed. A statement is completed when all of its result sets and update counts have been retrieved. In almost all cases, however, a statement is completed, and therefore committed, right after it is executed.) The way to allow two or more statements to be grouped into a transaction is to disable the auto-commit mode. This is demonstrated in the following code, where con is an active connection: con.setAutoCommit(false); ... After the auto-commit mode is disabled, no SQL statements are committed until you call the method `commit` explicitly.

unbalanced 2021-06-23T17:22:24.265700Z

make sense. What I'm curious about is how a commit relates to a transaction

indy 2021-06-23T17:22:25.265800Z

So maybe this might work from the first block,

(with-open [conn (pg-autoconn/conn! :postgres-config)]
    (jdbc/execute-one! conn [create-db-cmd] )
    (.commit conn)

unbalanced 2021-06-23T17:22:52.266300Z

Reason being, in postgres, you can't create a database inside of a transaction. That was the stumbling block

✅ 2
unbalanced 2021-06-23T17:23:06.266600Z

So I had to not trigger a transaction, somehow

seancorfield 2021-06-23T17:41:19.268700Z

@kslvsunil The issue here is DDL vs SQL: some databases do not let you run DDL inside a transaction, some databases auto-commit DDL regardless of the settings on the connection — so how SQL interacts with transactions can be different than how DDL interacts with them, unfortunately.

1
seancorfield 2021-06-23T17:42:48.269900Z

@goomba I would say you don’t need {:auto-commit true :read-only false} if you use jdbc/get-connection since those should be the default. Where is that pg-autoconn/auto-conn! coming from?

unbalanced 2021-06-24T14:43:12.273Z

It's just a credentials lookup "automagically". Was orthogonal to the autocommit... terrible naming choice, in retrospect

seancorfield 2021-06-23T17:43:24.270600Z

(calling it autoconn/`auto-conn!` when it seems to set the connection not auto-commit seems like terrible naming to me!)

💯 1
unbalanced 2021-06-24T14:42:18.272800Z

oh yeah guilty hahaha. I suck at naming things. Autoconn has a property of looking up and decrypting the required DB credentials, I didn't even think about the autocommit attribute

1