commit 51d9305ad40b15e623e8f6230a43f5288775d70d Author: Daniel Ziltener Date: Mon Mar 9 04:10:41 2020 +0100 In the beginning there was darkness diff --git a/deps.edn b/deps.edn new file mode 100644 index 0000000..0b6d975 --- /dev/null +++ b/deps.edn @@ -0,0 +1,34 @@ +{:deps + {org.clojure/clojure {:mvn/version "1.10.1"} + org.clojure/core.async {:mvn/version "1.0.567"} + org.postgresql/postgresql {:mvn/version "42.2.10"} + seancorfield/next.jdbc {:mvn/version "1.0.395"} + cljfx {:mvn/version "1.6.5"} + org.openjfx/javafx-base {:mvn/version "13.0.2"} + org.openjfx/javafx-controls {:mvn/version "13.0.2"} + org.openjfx/javafx-graphics {:mvn/version "13.0.2"} + org.openjfx/javafx-web {:mvn/version "13.0.2"} + org.openjfx/javafx-swing {:mvn/version "13.0.2"} + org.controlsfx/controlsfx {:mvn/version "11.0.1"} + metosin/malli {:git/url "https://github.com/metosin/malli.git" + :sha "9db4ff998b641d4a0cff4eb6d772fd0cb5d3b56c"}} + :paths ["src" "resources"] + :aliases + {:outdated {:extra-deps {olical/depot {:mvn/version "0.2.30"}} + :main-opts ["-m" "depot.outdated.main" "-a" "outdated"]} + :graph {:extra-deps {org.clojure/tools.deps.graph {:mvn/version "0.2.30"}} + :main-opts ["-m" "clojure.tools.deps.graph"]} + :dev {:extra-paths ["test"]} + :test {:extra-deps {lambdaisland/kaocha {:mvn/version "0.0-590"}} + :main-opts ["-m" "kaocha.runner"]} + :uberjar {:main-opts ["-m" "hf.depstar.uberjar" + "pgwiz.jar" + "-C" "-m" "ch.lyrion.pgwiz"] + :jvm-opts ["-Dclojure.compiler.direct-linking=true" + "-Dclojure.spec.skip-macros=true"] + :extra-paths ["classes"] + :extra-deps {seancorfield/depstar {:mvn/version "0.5.2"}}}} + :mvn/repos + {"gluon-releases" {:url "https://nexus.gluonhq.com/nexus/content/repositories/releases/"} + "gluon-snapshots" {:url "https://nexus.gluonhq.com/nexus/content/repositories/public-snapshots/"} + "snapshot" {:url "https://oss.sonatype.org/content/repositories/snapshots/"}}} diff --git a/src/ch/lyrion/pgwiz.clj b/src/ch/lyrion/pgwiz.clj new file mode 100644 index 0000000..ac130f9 --- /dev/null +++ b/src/ch/lyrion/pgwiz.clj @@ -0,0 +1,52 @@ +(ns ch.lyrion.pgwiz + (:gen-class) + (:require [cljfx.api :as fx] + [next.jdbc :as jdbc] + [ch.lyrion.pgwiz.gui :as gui] + [ch.lyrion.pgwiz.inspect :as inspect] + [ch.lyrion.pgwiz.db-table-editor :refer [db-table-editor]])) + +(def value-conversion-types + {"serial" :long + "citext" :string + "text" :string + "timestamp" :string + "uuid" :string + "bool" :string + "interval" :string}) + +(def *context + (atom + (fx/create-context + {}))) + +(defn mainwindow [{:keys [fx/context]}] + {:fx/type :stage + :showing true + :title "PgWiz" + :scene {:fx/type :scene + :root {:fx/type :v-box + :children [{:fx/type db-table-editor + :schema "company" + :table "vc" + :offset 0 + :limit 20}]}} + }) + +(def app + (fx/create-app *context + :event-handler gui/event-handler + :desc-fn (fn [_] {:fx/type mainwindow}))) + +#_(defonce renderer + (fx/create-renderer + :middleware (comp + fx/wrap-context-desc + (fx/wrap-map-desc (fn [_] {:fx/type sampletable}))) + :opts {:fx.opt/type->lifecycle #(or (fx/keyword->lifecycle %) + (fx/fn->lifecycle-with-context %)) + :fx.opt/map-event-handler event-handler})) + +(when *compile-files* + (Thread/sleep 1500) + (javafx.application.Platform/exit)) diff --git a/src/ch/lyrion/pgwiz/db_table_editor.clj b/src/ch/lyrion/pgwiz/db_table_editor.clj new file mode 100644 index 0000000..2adfce3 --- /dev/null +++ b/src/ch/lyrion/pgwiz/db_table_editor.clj @@ -0,0 +1,68 @@ +(ns ch.lyrion.pgwiz.db-table-editor + (:require [ch.lyrion.pgwiz.gui :as gui] + [ch.lyrion.pgwiz.inspect :as inspect] + [next.jdbc :as jdbc] + [cljfx.api :as fx])) + +(defmethod gui/event-handler ::on-edit-start [{:keys [fx/context fx/event attr]}] + (when (instance? javafx.scene.control.TableColumn$CellEditEvent event) + {:context (fx/swap-context context assoc :edit [attr (:lyrion/index (.getRowValue event))])})) + +(defn editable-cell [{:keys [fx/context data attr id value-converter]}] + (let [value (:values data) + value (nth value id) + value (get value attr) + value (str value)] + (if (= (fx/sub context :edit) [attr id]) + {:fx/type :text-field + :text-formatter {:fx/type :text-formatter + :value-converter value-converter + :value value}} + {:fx/type :label + :text (str value)}))) + +(defn editable-cell-factory [view data attr value-converter] + (fn [id] + {:text "" + :graphic {:fx/type view + :data data + :attr attr + :id id + :value-converter value-converter}})) + +(defn generate-columns [data] + (reduce #(let [attr (keyword (:TABLE_NAME %2) (:COLUMN_NAME %2))] + (cons {:fx/type :table-column + :cell-value-factory (fn [x] (get x :lyrion/index)) + :cell-factory (editable-cell-factory editable-cell + data + attr + :default) + :on-edit-start {:event/type ::on-edit-start :attr attr} + :on-edit-cancel {:event/type ::on-edit-cancel :attr attr} + :on-edit-commit {:event/type ::on-edit-commit :attr attr} + :text (:COLUMN_NAME %2)} + %1)) + (list) + (:columns data))) + +(defn generate-table-data [schema table offset limit] + (let [table-meta (inspect/get-columns schema table)] + {:schema schema + :table table + :columns table-meta + :values (map #(-> %1 + (assoc :lyrion/meta table-meta) + (assoc :lyrion/index %2)) + (jdbc/execute! + (jdbc/prepare @inspect/conn + [(str "select * from " schema "." table " limit ? offset ?") limit offset])) + (range))})) + +(defn db-table-editor [{:keys [fx/context schema table offset limit]}] + (let [data (generate-table-data schema table offset limit)] +; (let [context (fx/swap-context context assoc :table (generate-table-data schema table offset limit))]) + {:fx/type :table-view + :editable true + :items (:values data) + :columns (generate-columns data)})) diff --git a/src/ch/lyrion/pgwiz/gui.clj b/src/ch/lyrion/pgwiz/gui.clj new file mode 100644 index 0000000..6724a62 --- /dev/null +++ b/src/ch/lyrion/pgwiz/gui.clj @@ -0,0 +1,4 @@ +(ns ch.lyrion.pgwiz.gui + (:require [cljfx.api :as fx])) + +(defmulti event-handler :event/type) diff --git a/src/ch/lyrion/pgwiz/inspect.clj b/src/ch/lyrion/pgwiz/inspect.clj new file mode 100644 index 0000000..e3f594d --- /dev/null +++ b/src/ch/lyrion/pgwiz/inspect.clj @@ -0,0 +1,44 @@ +(ns ch.lyrion.pgwiz.inspect + (:require [next.jdbc :as jdbc])) + +(def db {:dbtype "postgresql" + :dbname "sompani-dev" + :host "127.0.0.1" + :port 5432 + :user "sompani_dev" + :password "sompani-dev"}) + +(def conn (atom (jdbc/get-connection db))) + +(defn get-named-rows [pg-result-set] + (.next pg-result-set) + (loop [pg-meta (.getMetaData pg-result-set) + result (list)] + (let [result (cons (reduce #(assoc %1 (keyword (.getColumnName pg-meta %2)) + (.getString pg-result-set %2)) + {} (range 1 (inc (.getColumnCount pg-meta)))) + result)] + (if (.next pg-result-set) + (recur (.getMetaData pg-result-set) result) + result)))) + +(defn get-exported-keys [schema table] + (get-named-rows (.getExportedKeys (.getMetaData @conn) nil schema table))) + +(defn get-imported-keys [schema table] + (get-named-rows (.getImportedKeys (.getMetaData @conn) nil schema table))) + +(defn get-primary-keys [schema table] + (get-named-rows (.getPrimaryKeys (.getMetaData @conn) nil schema table))) + +(defn get-version-columns [schema table] + (get-named-rows (.getVersionColumns (.getMetaData @conn) nil schema table))) + +(defn get-index-info [schema table unique approximate] + (get-named-rows (.getIndexInfo (.getMetaData @conn) nil schema table unique approximate))) + +(defn get-table-privileges [schema table] + (get-named-rows (.getTablePrivileges (.getMetaData @conn) nil schema table))) + +(defn get-columns [schema table] + (get-named-rows (.getColumns (.getMetaData @conn) nil schema table nil)))