sql

liebs 2024-07-09T20:58:08.870359Z

Hi, dealing with some frustrating issues in re: next.jdbc and duckdb. I keep seeing this exception:

clojure.lang.ExceptionInfo: Unknown dbtype: , and :classname not provided.
;          db: #object[next.jdbc.connection$url_PLUS_etc$reify__29370 0x10903cbd "jdbc:duckdb:chadwick"]
;     db-spec: {:dbtype "duckdb", :dbname "chadwick"}
I'd appreciate any ideas because I have never encountered such a problem with any other databases before.

respatialized 2024-07-09T21:02:06.724149Z

This sounds like it could be a missing driver issue - do you have the DuckDB JDBC driver on the classpath?

liebs 2024-07-09T21:02:29.561389Z

I do indeed

liebs 2024-07-09T21:02:47.094499Z

org.duckdb/duckdb_jdbc {:mvn/version "1.0.0"}

seancorfield 2024-07-09T21:03:02.099369Z

Provide more of the stacktrace and also the code you think is throwing that error.

seancorfield 2024-07-09T21:03:58.894519Z

When you say you "keep seeing this exception", do you mean "it works sometimes, but other times I get this exception" or do you mean "I can't get it to work, I only ever see this exception"?

liebs 2024-07-09T21:04:52.374749Z

; 2024-07-09T20:53:08.945Z pop-os ERROR [com.slothrop.clj-baseball.backend.duckdb:40] - 
;                                       clojure.core/eval        core.clj: 3215
;                                                     ...                     
;                                          user/eval80252      REPL Input:     
;                                              user/reset        user.clj:   27
;                                                     ...                     
;                    clojure.tools.namespace.repl/refresh        repl.clj:  169
;                    clojure.tools.namespace.repl/refresh        repl.clj:  187
;                                      clojure.core/apply        core.clj:  667
;                                                     ...                     
;            clojure.tools.namespace.repl/refresh-scanned        repl.clj:  109
;            clojure.tools.namespace.repl/refresh-scanned        repl.clj:  141
;                                                     ...                     
;      
;                                         user/start        user.clj:   20
;                                                     ...                     
;                             clojure.core/alter-var-root        core.clj: 5530
;                             clojure.core/alter-var-root        core.clj: 5535
;                                                     ...                     
;               com.stuartsierra.component/eval23945/fn/G  component.cljc:    5 (repeats 2 times)
;              com.stuartsierra.component.SystemMap/start  component.cljc:  181
;                 com.stuartsierra.component/start-system  component.cljc:  163
;                 com.stuartsierra.component/start-system  component.cljc:  165
;                                                     ...                     
;                com.stuartsierra.component/update-system  component.cljc:  130
;                com.stuartsierra.component/update-system  component.cljc:  136
;                                     clojure.core/reduce        core.clj: 6885
; 
;                                                     ...                     
;             com.stuartsierra.component/update-system/fn  component.cljc:  140
;                   com.stuartsierra.component/try-action  component.cljc:  118
;                                      clojure.core/apply        core.clj:  669
;                                                     ...                     
;               com.stuartsierra.component/eval23945/fn/G  component.cljc:    5 (repeats 2 times)
; com.slothrop.clj-baseball.backend.duckdb.Database/start      duckdb.clj:   27
;    com.slothrop.clj-baseball.backend.duckdb.Database/fn      duckdb.clj:   27
;                                      next.jdbc/execute!        jdbc.clj:  254
;                      next.jdbc.protocols/eval29153/fn/G   protocols.clj:   34
;                       next.jdbc.result-set/eval30133/fn  result_set.clj: 1021
;                      next.jdbc.protocols/eval29091/fn/G   protocols.clj:   14
;                       next.jdbc.connection/eval29377/fn  connection.clj:
;   473
;                      next.jdbc.connection/spec->url+etc  connection.clj:  200
; clojure.lang.ExceptionInfo: Unknown dbtype: , and :classname not provided.
;          db: #object[next.jdbc.connection$url_PLUS_etc$reify__29370 0x10903cbd "jdbc:duckdb:chadwick"]
;     db-spec: {:dbtype "duckdb", :dbname "chadwick"}

liebs 2024-07-09T21:05:59.096409Z

I figure this has something to do with my poor understanding of component tbh, bc I can for instance run this

(jdbc/execute! (jdbc/get-datasource db-spec) ["select 42"])
and it works just fine

seancorfield 2024-07-09T21:06:33.260399Z

Well, my first comment is going to be "stop using c.t.n.repl/refresh" since that can blow away state in namespaces and not restore it...

p-himik 2024-07-09T21:06:53.012239Z

Or don't store state in namespaces. ;)

seancorfield 2024-07-09T21:09:18.000359Z

Q: when you start your Component system for the first time, does it work?

liebs 2024-07-09T21:09:33.914849Z

no

liebs 2024-07-09T21:10:20.614129Z

I mean the system starts fine, but the JDBC code does not work.

p-himik 2024-07-09T21:10:34.228889Z

Judging by the dbtype: , part, somewhere dbtype, whatever it is, is set to nil or is missing altogether.

p-himik 2024-07-09T21:11:09.614239Z

Perhaps something expects a map like {:dbtype ..., ...} but your component returns something else. And it becomes apparent only when that something else is used.

seancorfield 2024-07-09T21:11:58.158719Z

I see user.clj and user/reset in that stacktrace which is why I asked about starting your Component system from a clean REPL (and you're also using something that "sanitizes" your stacktrace -- so I'd avoid that too if you can).

liebs 2024-07-09T21:13:00.075669Z

yes given my relative lack of familiarity with Component (I started using it bc I have always found Integrant somewhat confusing tbh) I did my best to adhere to Sierra's article on the reloaded workflow, but I'm sure I've borked something

seancorfield 2024-07-09T21:13:14.554309Z

So you'll have to show us your Database record source code with its start/`stop` functions. In fact, to make things go faster -- is this code on GitHub where we can take a look at the whole thing?

seancorfield 2024-07-09T21:13:28.240379Z

Don't use the reloaded workflow 😐

p-himik 2024-07-09T21:13:30.780729Z

Since you have problems even right after starting the system, the issue has nothing to do with the reloaded workflow.

p-himik 2024-07-09T21:13:49.751269Z

(I also disagree with Sean, but we've discussed it elsewhere at length).

seancorfield 2024-07-09T21:14:35.332379Z

If you're just getting started with Component, the Reloaded workflow adds a bunch of footguns. Better to start with just manual start / stop until you understand what you're doing.

seancorfield 2024-07-09T21:15:26.424769Z

Anyways... show us source code...

seancorfield 2024-07-09T21:17:55.547509Z

You're returning the datasource instead of the component: https://github.com/bhlieberman/clj-baseball/blob/re-frame/dashboard/src/main/com/slothrop/clj_baseball/backend/duckdb.clj#L43'

seancorfield 2024-07-09T21:19:01.130479Z

Oh, wait, that isn't a datasource, it's the Component with :db added in... poor naming... OK, looking further...

liebs 2024-07-09T21:21:13.950369Z

for context, I also tried to go off your example in the usermanager app which does this

(defrecord Database [db-spec     ; configuration
                     datasource] ; state

  component/Lifecycle
  (start [this]
    (if datasource
      this ; already initialized
      (let [database (assoc this :datasource (jdbc/get-datasource db-spec))]
        ;; set up database if necessary
        (populate database (:dbtype db-spec))
        database)))
  (stop [this]
    (assoc this :datasource nil))

seancorfield 2024-07-09T21:21:14.578679Z

Where's the -main that creates the system-map and calls start?

seancorfield 2024-07-09T21:21:40.933199Z

Heh, remind me to fix the naming in that then... Oof!

liebs 2024-07-09T21:22:51.035179Z

since I'm just developing this rn I'm using this code

(defn start-system [opts]
  (let [{:keys [port]} opts]
    (component/system-map
     :server (server/web-server #'server/handler port)
     :app (app/->app {})
     :db (db/setup-db))))

(def system (start-system {:port 3000}))

(defn start []
  (alter-var-root #'system component/start))
which is in dev/user.clj in the repo

favila 2024-07-09T21:24:54.595569Z

Shouldn't this be (jdbc/execute! (datasource) ...? I.e. the value of :db entry

seancorfield 2024-07-09T21:25:55.385839Z

point_up::skin-tone-2 Yup, I think he's right.

seancorfield 2024-07-09T21:30:08.526559Z

I changed the code to call datasource (to get db) and it started up.

liebs 2024-07-09T21:31:55.241609Z

yeah I suspected this originated in my ignorance of Component. So making the component state invokable and using it like (datasource) is doing what exactly? Something like deref'ing it?

favila 2024-07-09T21:34:33.844799Z

Your Database record implements invoke as something that returns db

seancorfield 2024-07-09T21:34:36.880359Z

The component implements IFn so it can be invoked (called).

seancorfield 2024-07-09T21:34:54.614569Z

Nothing to do with Component per se.

seancorfield 2024-07-09T21:36:13.974299Z

But it is a fairly common idiom with Component, I think, for things to be invokable as a way of getting back the state they encapsulate -- or performing some operation on that state (without it "escaping")

seancorfield 2024-07-09T21:42:43.523799Z

@bhlieberman93 If you look at the usermanager code, it passes database -- the Component -- into populate, and each call to jdbc/execute! in there "calls" the db component passed in.

liebs 2024-07-09T21:45:22.763649Z

yeah, that makes sense. I think what I was tripped up on is that I was not following the same pattern inside start because I assumed that its being in the Component constructor made the situation different. I guess...who knows what I was thinking 😅

seancorfield 2024-07-09T21:46:06.987079Z

I've create an issue against usermanager to try to clarify that for future learners...