Fork me on GitHub
#sql
<
2020-11-05
>
dominicm12:11:43

Yeah, we discovered that. I was just curious to know how others are handling exceptions in these cases, and relating them to the particular function calls.

seancorfield15:11:23

@dominicm sounds like a situation where you might want to use save points to get more control?

dominicm15:11:27

I'll take a look and see if they would help. Never encountered those!

seancorfield15:11:09

They weren't well-supported in clojure.java.jdbc but there are examples in the next.jdbc docs @dominicm

Darrell17:11:48

Here’s a perhaps offbeat question: I have a need to query SQL Server 2005. Is that even doable with modern Clojure libs?

👀 3
seancorfield17:11:34

@darrell As long as there's a compatible JDBC driver, yes.

seancorfield17:11:46

I don't know whether the current Microsoft SQL Server JDBC driver will work with it (i.e., the versions published to Maven) but you can probably download an older JDBC driver from Microsoft's site somewhere that would work. I can't remember the oldest version of SQL Server that I tested clojure.java.jdbc against but I used to run it on a Windows XP VM so it was old (and I definitely had to download the driver manually back then).

seancorfield17:11:06

But if you can find a compatible JDBC driver then both clojure.java.jdbc and next.jdbc should work with it just fine.

seancorfield17:11:31

I would try the latest JDBC driver (from Maven) first tho'. That would be [com.microsoft.sqlserver/mssql-jdbc "8.4.1.jre8"] for project.clj or com.microsoft.sqlserver/mssql-jdbc {:mvn/version "8.4.1.jre8"} for deps.edn

Darrell17:11:01

Thanks @seancorfield. I did try the latest but it didn’t work. It’s complaining about “The driver could not establish a secure connection to SQL Server by using Secure Sockets Layer (SSL) encryption. Error: \“Unexpected rethrowing\“.”

seancorfield17:11:37

Sounds like you'll need to download the older driver then. If you're using CLI/`deps.edn` you can just depend on the JAR via :local/root. lein is a bit more involved I think.

Darrell17:11:13

Thanks! I’ll explore and see what I can get working.

Darin Douglass19:11:08

@seancorfield is an explicit require of camel-snake-kebab.core required to use next.jdbc/snake-kebab-opts? looking at source it doesn't feel like it

Darin Douglass19:11:27

i ask b/c i'm seeing an unbound fn error for #'camel-snake-kebab.core/->snake_case when running my uberjar'd app. it does not happen in-repl

Darin Douglass19:11:45

putting an explicit require seemed to fix the issue

seancorfield20:11:27

@ddouglass If you are requiring next.jdbc, and you have the CSK library on the path (in the JAR, in your case), then it should automatically require c-s-k.core for you. How did you build your uberjar?

Darin Douglass20:11:15

i've hopped into the docker image and looked at the jar in emacs and the .class and .clj files exist

seancorfield20:11:24

Ah, I bet that doesn't put c-s-k on the classpath when it builds the uberjar...

Darin Douglass20:11:28

it's a kafka streams app, so there may be some oddities thrown in there

seancorfield20:11:24

Hmm, I haven't used lein for years. Can you try to create a small, self-contained repro case and put it up on github for me to look at?

Darin Douglass20:11:52

via the jar

-rw-rw-rw-      1713   5-Nov-2020  19:49:22  camel_snake_kebab/core$__GT_snake_case_symbol.class
  -rw-rw-rw-      1744   5-Nov-2020  19:49:22  camel_snake_kebab/core$__GT_kebab_case.class
  -rw-rw-rw-      1715   5-Nov-2020  19:49:22  camel_snake_kebab/core$__GT_kebab_case_keyword.class
the app runs via java -jar , so it should be there.

Darin Douglass20:11:31

i'll poke around a bit at it, i've a workaround in place anyway (explicit (require 'camel-snake-kebab.core)) so it's not too pressing

seancorfield20:11:36

I've managed to repro... I'll create a GH issue for this...

Darin Douglass20:11:24

what's the problem?

seancorfield20:11:07

I think I have a fix for it. I'll try to cut a new next.jdbc release later today.

dharrigan20:11:52

Ha! I'm precisely in that area right now - doing some work with resultsets and csk!

seancorfield20:11:57

@ddouglass OK, change your dependency to 1.1.613 for seancorfield/next.jdbc and it should be fixed.

seancorfield20:11:58

Sorry about that 😞

Darin Douglass20:11:43

not a problem, was funky when it died in the cluster but not in my repl

Darin Douglass20:11:55

though i am confused on why requiring-resolve wasn't doing the trick?

seancorfield20:11:34

It was requiring at macro expansion time (to determine whether the library was present), and in a REPL context that carries over to the evaluation time context.

seancorfield20:11:16

But in an uberjar context, it does not. So I shifted the requiring-resolve into the expansion and just use require at expansion time -- which is what I should have done in the first place.

dharrigan21:11:22

I think I may have missed something, did the next jdbc channel disappear?

seancorfield21:11:58

@dharrigan There never was one.

seancorfield21:11:08

All the next.jdbc chatter tends to happen here.

dharrigan21:11:16

waaaaaah! I must be going loopy in this lockdown!

seancorfield21:11:24

Join the club 🙂

dharrigan21:11:37

I could have sworn there was a dedicated next jdbc channel

dharrigan21:11:53

I have a question about next jdbc and plan then 🙂

dharrigan21:11:31

So, as I've found out (and as you point out in the documentation) this is the case The row in the reduction is an abstraction over the underlying (mutable) ResultSet object -- it is not a Clojure data structure. Because of that, you can simply access the columns via their SQL labels as shown

dharrigan21:11:15

thus, if I have a column such as tenant_id, then I need to access it like (:tenant_id row)

dharrigan21:11:40

(and let me formulate this...a moment)

dharrigan21:11:59

I was (am!) experimenting with some camel-snake-kebab and I'm passing this into the opts of the plan: {:builder-fn rs/as-unqualified-kebab-maps}

dharrigan21:11:21

when I do (log/info row), it spits out {:tenant-id "foobar"}

dharrigan21:11:24

and this confused me.

dharrigan21:11:54

since, it appears that something is converting the columns to csk, when I log (which I suppose realises the row)

dharrigan21:11:21

but if I try to then do (tenant-id row) I get nil, but (tenant_id row) returns a value

dharrigan21:11:37

it feels like there are two representations of the data

seancorfield22:11:20

Not quite sure what you're saying here?

but if I try to then do (tenant-id row) I get nil, but (tenant_id row) returns a value

seancorfield22:11:35

Did you mean to have keywords there?

dharrigan22:11:18

oops, sorry, yes (:tenant-id row) and (:tenant_id row)

seancorfield22:11:33

Inside the reducing function over plan, you do not have a realized result set -- the builder has not been called on the data -- you are interacting "directly" with the underlying ResultSet which has column labels (technically as strings).

dharrigan22:11:55

ah right, so the builder is not invoked until the reduction happens

seancorfield22:11:50

No, the builder is only invoked if you realize a row.

seancorfield22:11:06

From the options page: * :builder-fn -- a function that implements the RowBuilder and ResultSetBuilder protocols; strictly speaking, plan and execute-one! only need RowBuilder to be implemented (and plan only needs that if it actually has to realize a row) but most generation functions will implement both for ease of use.

dharrigan22:11:07

right, thank you. that clarified it

seancorfield22:11:29

I'll see if I can make it clearer in the Getting Started guide section for plan. This seems to trip a lot of people up, if they don't have a good mental model of what happens with plan 😐

dharrigan22:11:34

Another question, if I may...

seancorfield22:11:41

You may indeed 🙂

dharrigan22:11:34

Would a [{:keys [tenant-id]} row] (i.e., a keys) realize a row?

seancorfield22:11:03

That's a good question 🙂

dharrigan22:11:06

Any operation that would require a Clojure hash map, such as assoc or anything that invokes seq (keys, vals), will cause the full row to be expanded into a hash map, such as produced by execute! or execute-one!,

dharrigan22:11:12

does that suggest it does?

dharrigan22:11:45

What i was trying to do is pass the (yet unrealized) row into a function

dharrigan22:11:48

but destructure it

seancorfield22:11:14

Macroexpanding it suggests that it will not realize a row and therefore you would need to use tenant_id because it's a column label.

dharrigan22:11:08

Yes, just proved it

seancorfield22:11:13

You can tell because if you do (let [{:keys [tenant_id tenant-id]} row] ...) if tenant_id has the value, no builder was involved. If tenant-id has the value, it was run through the builder.

dharrigan22:11:33

so, the destructuring doesn't do the realization, i.e., thus not invoking the builder fn

seancorfield22:11:52

Map destructuring uses get so no seq/`keys`/`vals` operation occurs.

dharrigan22:11:18

right, so it's the keys function, not the :keys syntatic sugar

dharrigan22:11:38

can I realize a row, thus having the builder function fire, without doing anything else (except return the result of that builder fn?)

seancorfield22:11:52

Call rs/datafiable-row on the row.

dharrigan22:11:20

right, just came across that

dharrigan22:11:43

We talked ages and ages ago on plan

dharrigan22:11:57

I've come back to revisit it (as the code is becoming more important)

seancorfield22:11:49

In recent versions of next.jdbc, you can pass nil for the connectable and opts in datafiable-row

seancorfield22:11:11

(which makes it easier to use when you don't care about datafy/nav stuff)

dharrigan22:11:08

works like a charm

dharrigan22:11:33

(rs/datafiable-row funkylicious-row nil nil)

dharrigan22:11:40

invokes the builder

dharrigan22:11:15

perfecto! thanks sean!

seancorfield22:11:35

Remember that building a row is the expensive part you're normally trying to avoid when working with plan 🙂

👀 3
dharrigan22:11:18

Yup, but I'm at that level where I need the data out of the row now 🙂

dharrigan22:11:06

gotta get at it - eventually! 🙂