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.
@dominicm sounds like a situation where you might want to use save points to get more control?
I'll take a look and see if they would help. Never encountered those!
They weren't well-supported in clojure.java.jdbc
but there are examples in the next.jdbc
docs @dominicm
Here’s a perhaps offbeat question: I have a need to query SQL Server 2005. Is that even doable with modern Clojure libs?
@darrell As long as there's a compatible JDBC driver, yes.
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).
But if you can find a compatible JDBC driver then both clojure.java.jdbc
and next.jdbc
should work with it just fine.
https://www.microsoft.com/en-us/download/details.aspx?id=2505 is the download page for the old drivers.
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
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\“.”
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.
Thanks! I’ll explore and see what I can get working.
@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
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
putting an explicit require seemed to fix the issue
@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?
lein uberjar
i've hopped into the docker image and looked at the jar in emacs and the .class
and .clj
files exist
Ah, I bet that doesn't put c-s-k on the classpath when it builds the uberjar...
it's a kafka streams app, so there may be some oddities thrown in there
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?
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.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
I've managed to repro... I'll create a GH issue for this...
oh neat
what's the problem?
👍
thanks
I think I have a fix for it. I'll try to cut a new next.jdbc
release later today.
speedy boi
ty ty
Ha! I'm precisely in that area right now - doing some work with resultsets and csk!
@ddouglass OK, change your dependency to 1.1.613 for seancorfield/next.jdbc
and it should be fixed.
Sorry about that 😞
not a problem, was funky when it died in the cluster but not in my repl
though i am confused on why requiring-resolve
wasn't doing the trick?
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.
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.
gotcha
I think I may have missed something, did the next jdbc channel disappear?
@dharrigan There never was one.
All the next.jdbc
chatter tends to happen here.
waaaaaah! I must be going loopy in this lockdown!
Join the club 🙂
I could have sworn there was a dedicated next jdbc channel
I have a question about next jdbc and plan then 🙂
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
thus, if I have a column such as tenant_id
, then I need to access it like (:tenant_id row)
however!
(and let me formulate this...a moment)
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}
when I do (log/info row)
, it spits out {:tenant-id "foobar"}
and this confused me.
since, it appears that something is converting the columns to csk, when I log (which I suppose realises the row)
but if I try to then do (tenant-id row)
I get nil, but (tenant_id row)
returns a value
it feels like there are two representations of the data
.
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
Did you mean to have keywords there?
oops, sorry, yes (:tenant-id row)
and (:tenant_id row)
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).
ah right, so the builder is not invoked until the reduction happens
No, the builder is only invoked if you realize a row.
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.
right, thank you. that clarified it
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
😐
Another question, if I may...
You may indeed 🙂
Would a [{:keys [tenant-id]} row]
(i.e., a keys
) realize a row?
That's a good question 🙂
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!,
does that suggest it does?
What i was trying to do is pass the (yet unrealized) row into a function
but destructure it
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.
Yes, just proved it
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.
so, the destructuring doesn't do the realization, i.e., thus not invoking the builder fn
Map destructuring uses get
so no seq
/`keys`/`vals` operation occurs.
right, so it's the keys
function, not the :keys
syntatic sugar
can I realize a row, thus having the builder function fire, without doing anything else (except return the result of that builder fn?)
Call rs/datafiable-row
on the row.
right, just came across that
cool
We talked ages and ages ago on plan
I've come back to revisit it (as the code is becoming more important)
In recent versions of next.jdbc
, you can pass nil
for the connectable and opts in datafiable-row
(which makes it easier to use when you don't care about datafy/nav stuff)
works like a charm
(rs/datafiable-row funkylicious-row nil nil)
invokes the builder
perfecto! thanks sean!
Remember that building a row is the expensive part you're normally trying to avoid when working with plan
🙂
Yup, but I'm at that level where I need the data out of the row now 🙂
gotta get at it - eventually! 🙂