Method impls for [[metabase.driver.sql-jdbc.actions]] for | (ns metabase.driver.h2.actions (:require [clojure.java.jdbc :as jdbc] [clojure.string :as str] [metabase.actions.core :as actions] [metabase.driver.sql-jdbc.actions :as sql-jdbc.actions] [metabase.driver.sql-jdbc.connection :as sql-jdbc.conn] [metabase.util :as u] [metabase.util.i18n :refer [tru deferred-trun]] [metabase.util.log :as log])) |
(defmethod sql-jdbc.actions/base-type->sql-type-map :h2
[_driver]
{:type/BigInteger "BIGINT"
:type/Boolean "BOOL"
:type/Date "DATE"
:type/DateTime "DATETIME"
:type/DateTimeWithTZ "TIMESTAMP WITH TIME ZONE"
:type/Decimal "DECIMAL"
:type/Float "FLOAT"
:type/Integer "INTEGER"
:type/Text "VARCHAR"
:type/Time "TIME"}) | |
H2 doesn't need to do anything special with nested transactions; the original transaction can proceed even if some specific statement errored. | (defmethod sql-jdbc.actions/do-nested-transaction :h2 [_driver _conn thunk] (thunk)) |
Get the name of identifier from JDBC error message. An identifier can contains quote and full schema, database, table , etc. This formats so that we get only the identifer name with quote removed. (db-identifier->name "PUBLIC.TABLE" ) => "TABLE" | (defn- db-identifier->name
[s]
(-> s
(str/replace #"\"" "")
(str/split #"\.")
last)) |
Given a constraint with | (defn- constraint->column-names
[database table-name constraint-name]
(let [jdbc-spec (sql-jdbc.conn/db->pooled-connection-spec (u/the-id database))
sql-args ["SELECT C.TABLE_CATALOG, C.TABLE_SCHEMA, K.COLUMN_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS C
JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE K ON C.CONSTRAINT_NAME = K.CONSTRAINT_NAME
WHERE C.INDEX_NAME = ? AND C.TABLE_NAME = ?"
constraint-name table-name]]
(first
(reduce
(fn [[columns catalog schema] {:keys [table_catalog table_schema column_name]}]
(if (and (or (nil? catalog) (= table_catalog catalog))
(or (nil? schema) (= table_schema schema)))
[(conj columns column_name) table_catalog table_schema]
(do (log/warnf "Ambiguous catalog/schema for constraint %s in table %s"
constraint-name table-name)
(reduced nil))))
[[] nil nil]
(jdbc/reducible-query jdbc-spec sql-args {:identifers identity, :transaction? false}))))) |
(defmethod sql-jdbc.actions/maybe-parse-sql-error [:h2 actions/violate-not-null-constraint]
[_driver error-type _database _action-type error-message]
(when-let [[_ column]
(re-find #"NULL not allowed for column \"([^\"]+)\"" error-message)]
{:type error-type
:message (tru "{0} must have values." (str/capitalize column))
:errors {column (tru "You must provide a value.")}})) | |
(defmethod sql-jdbc.actions/maybe-parse-sql-error [:h2 actions/violate-unique-constraint]
[_driver error-type database _action-type error-message]
(when-let [[_match constraint-name table]
(re-find #"Unique index or primary key violation: \"[^.]+.(.+?) ON [^.]+.\"\"(.+?)\"\"" error-message)]
(let [columns (constraint->column-names database table constraint-name)]
{:type error-type
:message (tru "{0} already {1}." (u/build-sentence (map str/capitalize columns) :stop? false) (deferred-trun "exists" "exist" (count columns)))
:errors (reduce (fn [acc col]
(assoc acc col (tru "This {0} value already exists." (str/capitalize col))))
{}
columns)}))) | |
(defmethod sql-jdbc.actions/maybe-parse-sql-error [:h2 actions/violate-foreign-key-constraint]
[_driver error-type _database action-type error-message]
(when-let [[_match column]
(re-find #"Referential integrity constraint violation: \"[^\:]+: [^\s]+ FOREIGN KEY\(([^\s]+)\)" error-message)]
(let [column (db-identifier->name column)]
(merge {:type error-type}
(case action-type
:row/create
{:message (tru "Unable to create a new record.")
:errors {column (tru "This {0} does not exist." (str/capitalize column))}}
:row/delete
{:message (tru "Other tables rely on this row so it cannot be deleted.")
:errors {}}
:row/update
{:message (tru "Unable to update the record.")
:errors {column (tru "This {0} does not exist." (str/capitalize column))}}))))) | |
(defmethod sql-jdbc.actions/maybe-parse-sql-error [:h2 actions/incorrect-value-type]
[_driver error-type _database _action-type error-message]
(when-let [[_ _expected-type _value]
(re-find #"Data conversion error converting .*" error-message)]
{:type error-type
:message (tru "Some of your values aren’t of the correct type for the database.")
:errors {}})) | |