xtdb

jussi 2025-05-28T09:01:05.482509Z

(XTDB v1) A bit naive question maybe but while building some data quality tooling I wanted to build a simple query to check if arbitrary documents reference a certain root-document. Eg, I have multiple documents that store a back-reference to the root doc

:xt/id root-id
...
:settings/root root-id
:api-key/root root-id
:some-other/root root-id
My first approach to get a simple count or "it exists" type of information was along the lines of
(xt/q (xt/db node)
      '{:find [(count c) (count a) (count o)] 
        :where [[c :settings/root-id id]
                [a :api-key/root-id id]
                [o :some-other/root-id id]]
        :in [id]}
      id)
But it didn't work as I naively expected. If there exists settings (c) and api-key (a) that reference root id but not some-other referencingf root id, an empty set is returned. There a n documents that can refer to the root doc, but referencing docs are optional and there may be none. How should one implement this kind of query where the idea is to find the referencing optional docs in a lenient way?

jussi 2025-05-28T09:01:53.956169Z

I could easily query each referencing doc type individually but would like to know if there is a way to do this within a single query?

Oliver Marshall 2025-05-28T09:37:01.127729Z

It’s been a while since I’ve written a v1 query, but my instinct is something like (or [e :settings/root-id id] [e :api-key/root-id id] [e :some-other/root-id id])

πŸ™ 1
jussi 2025-05-28T09:47:33.323419Z

Ah, indeed. Optional matches over same logic variable.

jussi 2025-05-28T09:47:40.450589Z

Thanks, that works for my simple needs!

πŸ™Œ 1
seancorfield 2025-05-28T16:35:41.554589Z

I'm seeing some very puzzling behavior with next.jdbc's tests for XTDB. Locally, all the tests pass (using ./run-tests.clj xtdb with JDK 24 locally and it selects Clojure 1.12, XTDB api 2.0.0-beta8, 2.0.0-beta8.1 all pass). However, CI fails on one test -- details in 🧡

jarohen 2025-06-04T10:50:21.068159Z

hey @seancorfield πŸ‘‹ I've tried running these against 8.4, I'm getting one failure: database-metadata-datafy-tests - XT is missing :SQLKeywords - but can no longer see the error above - could you give that another shot?

jarohen 2025-06-04T10:52:22.510559Z

next.jdbc.datafy-test
  database-metadata-datafy-tests

database metadata datafication
Expected: (= reference-keys (set/intersection reference-keys data))
Actual: false
Evaluated arguments: #{...}
Only in first argument:
#{:SQLKeywords}
Only in second argument:
nil
The same in both:
#{... everything else ...}

in next/jdbc/datafy_test.clj:115

seancorfield 2025-06-04T13:49:21.501449Z

@jarohen will update next.jdbc to beta8.4 (api) and try again. Why has SQLKeywords disappeared from the metadata between beta8.1 and 8.3?

jarohen 2025-06-04T13:50:09.657409Z

I don't think I've come across SQLKeywords tbh πŸ™‚ any idea where that would have been before it disappeared?

jarohen 2025-06-04T13:50:39.246099Z

think we bumped pgjdbc in that time, so maybe Postgres has removed it...?

jarohen 2025-06-04T13:50:49.831299Z

but that was only a single minor version bump

seancorfield 2025-06-04T13:51:02.544199Z

It would be a getSQLKeywords() method on the DatabaseMetadata - and it used to either return nil or something, but now throws an exception?

πŸ€” 1
jarohen 2025-06-04T13:52:12.962349Z

we did put a delegate in for the PgConnection, possibly in that time window - but I didn't come across SQLKeywords at the time, so I'd guess it was one of the ones that was delegated by default

jarohen 2025-06-04T13:54:11.474009Z

aha, so if we delegate through to the pgjdbc connection impl that then makes a query of the backend - I wonder whether that query regressed

seancorfield 2025-06-04T13:56:35.776879Z

I'm seeing an NPE:

xtdb-1       | Caused by: java.lang.NullPointerException: Cannot invoke "java.lang.Character.charValue()" because "x" is null
xtdb-1       |  at clojure.lang.RT.intCast(RT.java:1241)
xtdb-1       |  at $fn__27079.invokeStatic(io.clj:227)
xtdb-1       |  at $fn__27079.invoke(io.clj:224)
xtdb-1       |  at $io_record$write_record__27103.invoke(io.clj:296)
xtdb-1       |  at $io_list$fn__27086$fn__27087.invoke(io.clj:245)
xtdb-1       |  at clojure.core$run_BANG_$fn__8922.invoke(core.clj:7907)
xtdb-1       |  at clojure.lang.PersistentVector.reduce(PersistentVector.java:418)
xtdb-1       |  at clojure.core$reduce.invokeStatic(core.clj:6964)
xtdb-1       |  at clojure.core$run_BANG_.invokeStatic(core.clj:7902)
xtdb-1       |  at clojure.core$run_BANG_.invoke(core.clj:7902)
xtdb-1       |  at $io_list$fn__27086.invoke(io.clj:245)
xtdb-1       |  at $io_record$write_record__27103.invoke(io.clj:296)
xtdb-1       |  at xtdb.pgwire.io.SocketFrontend.send_client_msg_BANG_(io.clj:131)
xtdb-1       |  at $cmd_write_msg.invokeStatic(io.clj:79)
xtdb-1       |  at $cmd_write_msg.invoke(io.clj:73)
xtdb-1       |  at xtdb.pgwire$cmd_send_row_description.invokeStatic(pgwire.clj:520)
xtdb-1       |  at xtdb.pgwire$cmd_send_row_description.invoke(pgwire.clj:501)
xtdb-1       |  at xtdb.pgwire$cmd_describe_canned_response.invokeStatic(pgwire.clj:539)
xtdb-1       |  at xtdb.pgwire$cmd_describe_canned_response.invoke(pgwire.clj:537)
xtdb-1       |  at xtdb.pgwire$eval27726$fn__27729.invoke(pgwire.clj:558)
xtdb-1       |  at clojure.lang.MultiFn.invoke(MultiFn.java:234)
xtdb-1       |  at xtdb.pgwire$handle_msg.invokeStatic(pgwire.clj:1392)

jarohen 2025-06-04T13:57:02.679889Z

this on 8.4 still?

seancorfield 2025-06-04T13:57:24.464569Z

Yes. I just updated to 8.4 so I can repro what you're seeing.

jarohen 2025-06-04T13:57:57.367919Z

ah, gotcha, repro'd here too

jarohen 2025-06-04T13:58:02.061549Z

(t/deftest test-database-metadata
  (with-open [conn (jdbc-conn)]
    (.getSQLKeywords (.getMetaData conn))))

jarohen 2025-06-04T13:58:24.664909Z

right, let's see what's going on...

seancorfield 2025-06-04T13:59:42.904379Z

Detail: ["~#xt/error",["~:fault","~:xtdb.error/unknown",null,["^ "]]]
   :at [org.postgresql.core.v3.QueryExecutorImpl receiveErrorResponse QueryExecutorImpl.java 2733]}]
 :trace
 [[org.postgresql.core.v3.QueryExecutorImpl receiveErrorResponse QueryExecutorImpl.java 2733]
  [org.postgresql.core.v3.QueryExecutorImpl processResults QueryExecutorImpl.java 2420]
  [org.postgresql.core.v3.QueryExecutorImpl execute QueryExecutorImpl.java 372]
  [org.postgresql.jdbc.PgStatement executeInternal PgStatement.java 517]
  [org.postgresql.jdbc.PgStatement execute PgStatement.java 434]
  [org.postgresql.jdbc.PgStatement executeWithFlags PgStatement.java 356]
  [org.postgresql.jdbc.PgStatement executeCachedSql PgStatement.java 341]
  [org.postgresql.jdbc.PgStatement executeWithFlags PgStatement.java 317]
  [org.postgresql.jdbc.PgStatement executeQuery PgStatement.java 266]
  [org.postgresql.jdbc.PgDatabaseMetaData getSQLKeywords PgDatabaseMetaData.java 354]
  [jdk.internal.reflect.DirectMethodHandleAccessor invoke DirectMethodHandleAccessor.java 104]

seancorfield 2025-06-04T13:59:56.372949Z

I suspect that's not terribly helpful πŸ™‚

jarohen 2025-06-04T14:12:57.700179Z

cool, think I have a fix - try docker tag edge?

seancorfield 2025-06-04T14:34:11.784989Z

Just a sec...

seancorfield 2025-06-04T14:35:13.308269Z

f96046d?

πŸ™ 1
seancorfield 2025-06-04T14:35:49.675829Z

(and I assume this bug is also in the newly-released beta9?)

seancorfield 2025-06-04T14:37:01.425849Z

Yup, that passes locally! getSQLKeywords() no longer throws.

jarohen 2025-06-04T16:11:24.276429Z

sweet πŸ™Œ and yep, still affects beta9

jarohen 2025-06-04T16:12:09.791039Z

it'll be in the dated nightly as of ~2200Z tonight - nightly-20250604

seancorfield 2025-06-04T16:27:02.933999Z

I'll wait for beta10 -- I've been burned by relying on latest etc in CI for next.jdbc πŸ˜›

jarohen 2025-06-04T16:27:39.161479Z

heh, yeah, understandable πŸ™‚

jarohen 2025-06-04T16:27:49.239459Z

(although that dated nightly is immutable)

seancorfield 2025-06-04T22:28:33.035199Z

OK, next.jdbc CI updated to depend on that nightly for now. It all passes.

πŸ™Œ 1
seancorfield 2025-05-28T16:36:08.870389Z

Test failure:

Testing against xtdb
      √ Anonymous test case

next.jdbc.datafy-test
  database-metadata-datafy-tests
    Anonymous test case:

database metadata datafication
Expected: (= reference-keys (set/intersection reference-keys data))
and the failure is:
Only in first argument:
#{:SQLKeywords}
Only in second argument:
nil
So, for some reason, XTDB is not returning SQLKeywords on CI but it is returning it locally. Both are using the latest Docker version:
xtdb:
    image: 
    pull_policy: always
    ports:
      - "5432:5432"
Any ideas?

seancorfield 2025-05-28T16:36:37.381249Z

Obvs, I could change the test to allow :SQLKeywords to be optional but that seems... hacky...

seancorfield 2025-05-28T16:38:50.998279Z

That test, for all databases, starts here: https://github.com/seancorfield/next-jdbc/blob/develop/test/next/jdbc/datafy_test.clj#L49

seancorfield 2025-05-28T16:44:27.413209Z

The test output shows that calling the getter for SQLKeywords throws an exception in CI (only):

xtdb :db-meta    (:maxLogicalLobSize :SQLKeywords/exception)

seancorfield 2025-05-28T16:50:21.785259Z

Here's more information about the exception:

SQLKeywords exception: #error {
 :cause ERROR: Cannot invoke "java.lang.Character.charValue()" because "x" is null
  Detail: ["~#xt/error",["~:fault","~:xtdb.error/unknown","Cannot invoke \"java.lang.Character.charValue()\" because \"x\" is null",["^ "]]]
 :via
 [{:type java.lang.reflect.InvocationTargetException
   :message nil
   :at [jdk.internal.reflect.DirectMethodHandleAccessor invoke DirectMethodHandleAccessor.java 118]}
  {:type org.postgresql.util.PSQLException
   :message ERROR: Cannot invoke "java.lang.Character.charValue()" because "x" is null
  Detail: ["~#xt/error",["~:fault","~:xtdb.error/unknown","Cannot invoke \"java.lang.Character.charValue()\" because \"x\" is null",["^ "]]]
   :at [org.postgresql.core.v3.QueryExecutorImpl receiveErrorResponse QueryExecutorImpl.java 2733]}]
 :trace
 [[org.postgresql.core.v3.QueryExecutorImpl receiveErrorResponse QueryExecutorImpl.java 2733]
  [org.postgresql.core.v3.QueryExecutorImpl processResults QueryExecutorImpl.java 2420]
  [org.postgresql.core.v3.QueryExecutorImpl execute QueryExecutorImpl.java 372]
  [org.postgresql.jdbc.PgStatement executeInternal PgStatement.java 517]
  [org.postgresql.jdbc.PgStatement execute PgStatement.java 434]
  [org.postgresql.jdbc.PgStatement executeWithFlags PgStatement.java 356]
  [org.postgresql.jdbc.PgStatement executeCachedSql PgStatement.java 341]
  [org.postgresql.jdbc.PgStatement executeWithFlags PgStatement.java 317]
  [org.postgresql.jdbc.PgStatement executeQuery PgStatement.java 266]
  [org.postgresql.jdbc.PgDatabaseMetaData getSQLKeywords PgDatabaseMetaData.java 354]
  [jdk.internal.reflect.DirectMethodHandleAccessor invoke DirectMethodHandleAccessor.java 103]
  [java.lang.reflect.Method invoke Method.java 580]
  [clojure.java.data$make_shallow_getter_fn$fn__6772 invoke data.clj 105]

jarohen 2025-05-28T16:52:57.613089Z

that may be an 8.3 error πŸ€”

seancorfield 2025-05-28T16:53:41.281339Z

I tried xtdb-api 2.0.0-beta8.3 since I saw there was a core release but that api version is not on Maven.

jarohen 2025-05-28T16:53:54.181579Z

right, let's get that up onto Maven πŸ™‚

jarohen 2025-05-28T16:54:02.677469Z

the stacktrace there looks like an 8.3 stacktrace

seancorfield 2025-05-28T16:54:04.918959Z

(and why does it only fail in CI, not locally?)

jarohen 2025-05-28T16:54:24.700909Z

pass. potential for a version difference?

jarohen 2025-05-28T16:55:01.015679Z

could you run with XTDB_LOGGING_LEVEL_PGWIRE=debug on CI? that'll give us the server-side stacktrace rather than the client-side one

seancorfield 2025-05-28T17:07:35.895879Z

just pushing that up to CI

seancorfield 2025-05-28T17:12:07.930089Z

Hmm, that seemed to produce the same stacktrace:

database-metadata-datafy-tests
  xtdb :db-meta    (:maxLogicalLobSize :SQLKeywords/exception)
SQLKeywords exception: #error {
 :cause ERROR: Cannot invoke "java.lang.Character.charValue()" because "x" is null
  Detail: ["~#xt/error",["~:fault","~:xtdb.error/unknown","Cannot invoke \"java.lang.Character.charValue()\" because \"x\" is null",["^ "]]]
 :via
 [{:type java.lang.reflect.InvocationTargetException
   :message nil
   :at [jdk.internal.reflect.DirectMethodHandleAccessor invoke DirectMethodHandleAccessor.java 118]}
  {:type org.postgresql.util.PSQLException
   :message ERROR: Cannot invoke "java.lang.Character.charValue()" because "x" is null
  Detail: ["~#xt/error",["~:fault","~:xtdb.error/unknown","Cannot invoke \"java.lang.Character.charValue()\" because \"x\" is null",["^ "]]]
   :at [org.postgresql.core.v3.QueryExecutorImpl receiveErrorResponse QueryExecutorImpl.java 2733]}]
 :trace
 [[org.postgresql.core.v3.QueryExecutorImpl receiveErrorResponse QueryExecutorImpl.java 2733]
  [org.postgresql.core.v3.QueryExecutorImpl processResults QueryExecutorImpl.java 2420]
  [org.postgresql.core.v3.QueryExecutorImpl execute QueryExecutorImpl.java 372]
  [org.postgresql.jdbc.PgStatement executeInternal PgStatement.java 517]
  [org.postgresql.jdbc.PgStatement execute PgStatement.java 434]
  [org.postgresql.jdbc.PgStatement executeWithFlags PgStatement.java 356]
  [org.postgresql.jdbc.PgStatement executeCachedSql PgStatement.java 341]
  [org.postgresql.jdbc.PgStatement executeWithFlags PgStatement.java 317]
  [org.postgresql.jdbc.PgStatement executeQuery PgStatement.java 266]
  [org.postgresql.jdbc.PgDatabaseMetaData getSQLKeywords PgDatabaseMetaData.java 354]
  [jdk.internal.reflect.DirectMethodHandleAccessor invoke DirectMethodHandleAccessor.java 103]
  [java.lang.reflect.Method invoke Method.java 580]
  [clojure.java.data$make_shallow_getter_fn$fn__6772 invoke data.clj 105]

seancorfield 2025-05-28T17:14:00.018049Z

Here's the CI command details:

clojure -T:build ci :snapshot true
  shell: /usr/bin/bash -e {0}
  env:
    JAVA_HOME: /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/21.0.7-6/x64
    JAVA_HOME_21_X64: /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/21.0.7-6/x64
    CLOJURE_INSTALL_DIR: /tmp/usr/local/opt/ClojureTools
    MYSQL_ROOT_PASSWORD: testing
    NEXT_JDBC_TEST_MYSQL: yes
    NEXT_JDBC_TEST_XTDB: yes
    XTDB_LOGGING_LEVEL_PGWIRE: debug
    NEXT_JDBC_TEST_MSSQL: yes
    MSSQL_SA_PASSWORD: Str0ngP4ssw0rd
(that's all public on GH BTW)

seancorfield 2025-05-28T20:55:16.615159Z

I pinned the Docker image to 2.0.0-beta8.1 (to match the xtdb-api version) and it all passed on CI, so I'll make an effort to keep them in lock step in future.