clj-r2dbc.impl.sql.row

Hot-path row-to-map conversion for clj-r2dbc.

Two-phase row conversion pipeline:
1. Build a RowMetadataCache once per result set using build-metadata-cache.
   This is an O(n-columns) operation that pre-computes keyword keys and Java
   types so the hot loop can use array access without per-row SPI calls.
2. Call row->map on every row in the hot loop using the pre-built cache.
   Uses unchecked arithmetic, direct array access, and transient maps.

Provides:
  RowMetadataCache     - deftype caching pre-computed keys and Java types.
  qualify-column       - column name to keyword, with qualifier mode support.
  build-metadata-cache - builds a RowMetadataCache from RowMetadata (once per result set).
  row->map             - converts a Row to a persistent map using a pre-built cache.
  make-row-fn          - convenience: returns a (Row, RowMetadata) -> map function.

This namespace is an implementation detail; do not use from application code.

build-metadata-cache

(build-metadata-cache rmd col-qualify-fn)
Build a RowMetadataCache from a RowMetadata, called once per result set.

Args:
  rmd           - the RowMetadata to read column info from.
  col-qualify-fn - 2-arity (String col-name, ColumnMetadata cm) -> keyword
                   function that produces the map key for each column.

Java type defaults to Object when ColumnMetadata.getJavaType() returns nil.

Example:
  (build-metadata-cache rmd (fn [col-name _cm] (keyword col-name)))

ensure-qualifier

(ensure-qualifier opts)
Validate that opts contains a non-nil :qualifier key.

Args:
  opts - options map to check.

Returns the :qualifier value.

Throws (synchronously):
  ex-info :clj-r2dbc/error-type :invalid-argument when :qualifier is absent.

make-row-fn

(make-row-fn)(make-row-fn {:keys [qualifier], :or {qualifier :unqualified-kebab}})
Return a (Row, RowMetadata) -> map function for use with result processing.

Caches the RowMetadataCache across invocations using RowMetadata identity
comparison. The cache is built once per result set (when RowMetadata changes)
and reused for all subsequent rows in the same result set.

Args:
  opts - (optional) options map; relevant keys:
           :qualifier - :unqualified, :unqualified-kebab (default), or
                        :qualified-kebab.

Example:
  (make-row-fn)
  ((make-row-fn) row rmd) ;;=> {:first-name "Alice"}

make-vector-fn

(make-vector-fn)
Return a (Row, RowMetadata) -> vector builder function.

Builds a RowMetadataCache once per result set (keyed by RowMetadata identity)
and reuses it for all subsequent rows. Each row is emitted as a persistent
vector of column values in result-set order.

Example:
  (make-vector-fn)
  ((make-vector-fn) row rmd) ;;=> [1 "Alice" true]

map-builder-fn

(map-builder-fn col->key-fn)
Return a (Row, RowMetadata) -> map builder function using the supplied column key function.

Caches the RowMetadataCache across invocations using RowMetadata identity
comparison. The cache is built once per result set and reused for all subsequent rows.

Args:
  col->key-fn - 2-arity (String col-name, ColumnMetadata cm) -> keyword function.

Example:
  (map-builder-fn (fn [col _cm] (keyword col)))

qualifier-value->ns

(qualifier-value->ns qv)
Convert a qualifier value to a namespace string.

Args:
  qv - qualifier value; accepts String (used as-is), keyword (namespace or
       name), or symbol (name).

Returns a namespace string, or nil for any other type.

qualify-column

(qualify-column col-name table-name mode)
Map a column name and table name to a keyword using the given mode.

Args:
  col-name   - the column name string.
  table-name - the table name string; may be nil.
  mode       - qualification mode keyword:
                 :unqualified       - (keyword col-name), no transformation.
                 :unqualified-kebab - (keyword (->kebab-case col-name)).
                 :qualified-kebab   - (keyword table-name (->kebab-case col-name));
                                      falls back to unqualified-kebab when
                                      table-name is nil or empty.

Example:
  (qualify-column "first_name" "users" :qualified-kebab) ;;=> :users/first-name
  (qualify-column "first_name" nil       :qualified-kebab) ;;=> :first-name

qualify-with-explicit

(qualify-with-explicit qualifier col-name col-meta col-xf)
Apply an explicit qualifier to col-name, producing a qualified keyword.

Args:
  qualifier - String, keyword, or symbol (static namespace); or a function
              of [col-name col-meta] -> namespace-value (1-arity accepted
              as fallback when the 2-arity call throws ArityException).
  col-name  - the column name string.
  col-meta  - the ColumnMetadata passed to qualifier when it is a function.
  col-xf    - (String -> String) case transformer applied to col-name before
              building the keyword (e.g., csk/->kebab-case-string or identity).

Returns a qualified keyword.

Throws (synchronously):
  ex-info :clj-r2dbc/error-type :invalid-argument when the resolved namespace
  is nil or blank.

row->map

(row->map row cache)
Convert an R2DBC Row to a persistent Clojure map using a pre-built cache.

This is the hot loop: uses primitive int, unchecked arithmetic, array access,
and transient map construction for maximum throughput.

Args:
  row   - the R2DBC Row to convert.
  cache - RowMetadataCache built with build-metadata-cache.

Example:
  (row->map row cache) ;;=> {:id 1 :name "Alice"}