sql

All things SQL and JDBC...
aratare 2021-04-03T07:52:55.073600Z

Hi there. Iโ€™m working on some database tests and I came across this website https://purelyfunctional.tv/mini-guide/clojure-database-test-faster/. In the article, the author has this block of code:

(defn clear
    (sql/with-db-transaction [db db]
        (sql/db-set-rollback-only! db)
        (binding [db db] ;; rebind dynamic var db, used in tests
          (test))))
which essentially allows test to do whatever it wants and then everything will be rolled back at the end. This is quite neat but apparently db-set-rollback-only! is only available in clojure.java.jdbc. Is there an equivalent of such function in next.jdbc? If not, is there a preferred way of doing database testing in Clojure? Thanks in advance ๐Ÿ™‚

dharrigan 2021-04-03T08:20:00.075800Z

You may want to consider something like clj-test-containers which spins up a real instance of your database inside a docker container. Thus you can exercise your tests against a real instance, thus your tests should be more accurate (in the sense of not mocking out tests, or using another in-memory data structure to mimic a database).

dharrigan 2021-04-03T08:20:19.076300Z

<https://github.com/javahippie/clj-test-containers>

dharrigan 2021-04-03T08:21:06.076700Z

Nice thing is that when your tests are done, the instance is destroyed ๐Ÿ™‚

aratare 2021-04-03T08:26:57.077300Z

Ooooh that is AWESOME! Thank you so much ๐Ÿ™‚

aratare 2021-04-03T08:29:03.079100Z

One question: is this mainly for the entire test suite, i.e. you spin it up once before running the tests and tear it down afterward? Because I imagine spinning a container up for every test seems like suboptimal.

dharrigan 2021-04-03T08:29:13.079300Z

the former

aratare 2021-04-03T08:29:57.080Z

Is there a way to effectively reset the db after each test instead? Iโ€™m currently using Migratus to reset the db after each test at the moment.

aratare 2021-04-03T08:30:11.080500Z

It works, but I have a feeling Iโ€™m doing it wrong ๐Ÿ˜…

dharrigan 2021-04-03T08:31:22.081200Z

It sounds like, if that's what you want to do, is to wrap each of your tests in a transaction, then roll that back

dharrigan 2021-04-03T08:31:48.081600Z

i.e., using use-fixtures

dharrigan 2021-04-03T08:31:56.081800Z

<https://clojure.github.io/clojure/clojure.test-api.html#clojure.test/use-fixtures>

dharrigan 2021-04-03T08:32:25.082Z

<https://clojuredocs.org/clojure.test/use-fixtures>

aratare 2021-04-03T08:33:07.082500Z

Yep, and hence my original question if thereโ€™s anything like db-set-rollback-only! in next.jdbc.

dharrigan 2021-04-03T08:33:51.083300Z

There is the rollback-only

aratare 2021-04-03T08:34:14.083700Z

where is that sorry?

dharrigan 2021-04-03T08:34:20.083900Z

let me dig that out for you

aratare 2021-04-03T08:34:35.084200Z

Lovely. Thanks ๐Ÿ™‚

dharrigan 2021-04-03T08:35:40.085800Z

This may give you some hints: <https://github.com/seancorfield/next-jdbc/blob/7e13e719ce2df78050c6efb2704e771c56a25035/test/next/jdbc_test.clj#L209>

jumar 2021-04-03T08:36:00.086400Z

This is possible if you test the dB layer but what if you want to test higher levels such as ring handlers that create their own transactions.? I assume then you need to reset db state after every test

dharrigan 2021-04-03T08:36:32.086600Z

Seems like that's not testing in isolation

dharrigan 2021-04-03T08:36:39.086900Z

I would only test the layer that I'm interested in

dharrigan 2021-04-03T08:36:45.087200Z

and mock out above/below

aratare 2021-04-03T08:36:48.087400Z

jdbc/with-transaction [t (ds) {:rollback-only true}] seems like the way to do it

aratare 2021-04-03T08:37:28.087500Z

itโ€™s still quite useful if you want to do end-to-end testing I reckon

dharrigan 2021-04-03T08:37:41.088Z

The question becomes, where do you stop?

dharrigan 2021-04-03T08:37:52.088500Z

If you're testing ring handling, do you go up the stack and test your network call to the ring?

jumar 2021-04-03T08:37:59.088800Z

Yes. Mocking is quite brittle and not that useful imho

๐Ÿ‘† 1
dharrigan 2021-04-03T08:38:08.089Z

for it's not truly end-to-end testing, if you're not going all the way ๐Ÿ™‚

aratare 2021-04-03T08:38:30.089200Z

well, by end2end I mean something akin to selenium

dharrigan 2021-04-03T08:38:41.089400Z

you're looking at more at integration test rather than a "simple" unit test

jumar 2021-04-03T08:39:34.090600Z

Oh definitely- I think anything involving a db isnโ€™t a unit test anymore

aratare 2021-04-03T08:40:14.090800Z

Yep I agree.

dharrigan 2021-04-03T08:40:45.091Z

shock

dharrigan 2021-04-03T08:40:54.091200Z

agreement on slack, on a saturday morning!

aratare 2021-04-03T08:40:59.091400Z

haha

dharrigan 2021-04-03T08:41:06.091600Z

I fall off my chair

aratare 2021-04-03T08:41:26.091800Z

too bad Iโ€™m not feeling up to wage war on a saturday morning ๐Ÿ˜…

dharrigan 2021-04-03T08:41:34.092Z

๐Ÿ™‚

aratare 2021-04-03T08:41:43.092200Z

still, thanks a lot for the help @dharrigan

aratare 2021-04-03T08:41:48.092400Z

really appreciate it ๐Ÿ™‚

dharrigan 2021-04-03T08:41:56.092600Z

You're most welcome! Enjoy!

Gleb Posobin 2021-04-03T16:37:02.094300Z

(not related to clojure, but I am assuming this is ok in this channel since it is says "all things sql", and don't know a better place to ask and can't find anything about this) I am trying to do the following in postgres: I want to do a dynamic LIKE prefix query on a column that is indexed with the text_pattern_ops index. The query looks like WITH prefixes (...) SELECT url FROM urls, prefixes WHERE urls.url LIKE prefixes.prefix || '%'; This doesn't use the index because prefixes.prefix might contain a bunch of wildcards for all postgres knows. How do I escape wildcards in prefixes.prefix and more importantly tell postgres to use the index on the url column when pattern-matching?

orestis 2021-04-03T16:53:57.096200Z

Does it use the index without the string manipulation? What happens if you introduce another WITH where you do the string concat and use that for the like?

Gleb Posobin 2021-04-03T16:55:56.097300Z

It uses the index if I specify the string 'http://google.com%' myself. Doesn't use the index if the whole string comes from the with, without concatenation.

Gleb Posobin 2021-04-03T16:56:24.097900Z

I just found there is starts_with, but it doesn't seem to use the index at all even if I specify the string manually.

Gleb Posobin 2021-04-03T17:09:33.098500Z

Hmm, looks like there is a message in the postgres mailing list about this problem with no replies: https://www.postgresql.org/message-id/flat/F5AA20A6-F2BC-4A60-A58A-0EF5220FD077%40ya.ru

orestis 2021-04-03T17:16:47.099600Z

Iโ€™ve had good results asking things in the Postgres Reddit. Perhaps you could be lucky and get an answer.

orestis 2021-04-03T17:17:22.100400Z

Is the prefixes static ? Perhaps you could end up doing some fancy sql generation

Gleb Posobin 2021-04-03T17:19:25.100600Z

It comes from the client, so no.

Gleb Posobin 2021-04-03T17:19:46.101Z

I'll try asking, thank you! Also will ask in their mailing list, this seems to be the main place of activity for them.

Gleb Posobin 2021-04-03T17:57:36.104400Z

Actually it is simpler, I just replaced the the LIKE part with url.url ~&gt;=~ prefix AND url.url ~&lt;~ prefix || '~', this does use the index, and actually does the same thing under the hood as a prefix LIKE (well, except the prefix LIKE replaces the last character of the prefix string with the next one in ~< part to be completely correct, but I don't need to worry about non-ascii characters in my case and has the largest number among ascii characters).

orestis 2021-04-03T17:58:58.105200Z

I havenโ€™t seen this operator before! Is this a regex? Can you point to the docs?

Gleb Posobin 2021-04-03T18:07:02.106Z

I am not sure what operator that is and didn't find it in the docs, I just saw that it in explain analyze of the LIKE query.

Gleb Posobin 2021-04-03T18:10:05.106500Z

Found this: https://stackoverflow.com/questions/35807872/operator-in-postgres