From: Kevin M. Rosenberg Date: Sun, 1 Dec 2002 21:07:28 +0000 (+0000) Subject: r3527: *** empty log message *** X-Git-Tag: debian-2.11.0-2~252 X-Git-Url: http://git.kpe.io/?p=hyperobject.git;a=commitdiff_plain;h=d7f427eeebf7e6404ad2d1ebabbc8ebaa064898c r3527: *** empty log message *** --- diff --git a/connect.lisp b/connect.lisp new file mode 100644 index 0000000..434a4f4 --- /dev/null +++ b/connect.lisp @@ -0,0 +1,103 @@ +;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10; Package: umlisp -*- +;;;; ************************************************************************* +;;;; FILE IDENTIFICATION +;;;; +;;;; Name: connect.lisp +;;;; Purpose: Low-level SQL routines data for UMLisp +;;;; Programmer: Kevin M. Rosenberg +;;;; Date Started: Apr 2000 +;;;; +;;;; $Id: connect.lisp,v 1.1 2002/12/01 21:07:28 kevin Exp $ +;;;; +;;;; This file, part of Hyperobject-SQL, is +;;;; Copyright (c) 2000-2002 by Kevin M. Rosenberg, M.D. +;;;; ************************************************************************* + +(in-package :hyperobject) +(declaim (optimize (speed 3) (safety 1) (compilation-speed 0) (debug 3))) + + +(defvar *ho-sql-db* "ho") +(defun ho-sql-db () + *ho-sql-db*) +(defun ho-sql-db! (dbname) + (sql-disconnect-pooled) + (setq *ho-sql-db* dbname)) + +(defvar *ho-sql-user* "secret") +(defun ho-sql-user () + *ho-sql-user*) +(defun ho-sql-user! (u) + (sql-disconnect-pooled) + (setq *ho-sql-user* u)) + +(defvar *ho-sql-passwd* "secret") +(defun ho-sql-passwd () + *ho-sql-passwd*) +(defun ho-sql-passwd! (p) + (sql-disconnect-pooled) + (setq *ho-sql-passwd* p)) + +(defvar *ho-sql-host* "localhost") +(defun ho-sql-host () + *ho-sql-host*) +(defun ho-sql-host! (h) + (sql-disconnect-pooled) + (setq *ho-sql-host* h)) + +(defvar *ho-sql-type* :mysql) +(defun ho-sql-type () + *ho-sql-type*) +(defun ho-sql-type! (h) + (sql-disconnect-pooled) + (setq *ho-sql-type* h)) + +(defun sql-connect () + "Connect to HO database, automatically used pooled connections" + (clsql:connect `(,(ho-sql-host) ,(ho-sql-db) ,(ho-sql-user) ,(ho-sql-passwd)) + :database-type *ho-sql-type* :pool t)) + +(defun sql-disconnect (conn) + "Disconnect from HO database, but put connection back into pool" + (clsql:disconnect :database conn)) + +(defun sql-disconnect-pooled () + (clsql:disconnect-pooled)) + +(defmacro with-sql-connection ((conn) &body body) + `(let ((,conn (sql-connect))) + (unwind-protect + (progn ,@body) + (when ,conn (clsql:disconnect :database ,conn))))) + +(defun sql (stmt conn) + (if (string-equal "SELECT" (subseq stmt 0 6)) + (sql-query stmt conn) + (sql-execute stmt conn))) + +(defun sql-query (cmd conn &key (types :auto)) + (clsql:query cmd :database conn :types types)) + +(defun sql-execute (cmd conn) + (clsql:execute-command cmd :database conn)) + +(defun ho-sql (stmt) + (check-type stmt string) + (with-sql-connection (conn) + (sql stmt conn))) + +;;; Pool of open connections + +(defmacro with-mutex-sql ((conn) &body body) + `(let ((,conn (sql-connect))) + (unwind-protect + (progn ,@body) + (when ,conn (sql-disconnect ,conn))))) + +(defun mutex-sql-execute (cmd) + (with-mutex-sql (conn) + (sql-execute cmd conn))) + +(defun mutex-sql-query (cmd &key (types :auto)) + (with-mutex-sql (conn) + (sql-query cmd conn :types types))) diff --git a/hyperobject.asd b/hyperobject.asd index d3134d4..1459e52 100644 --- a/hyperobject.asd +++ b/hyperobject.asd @@ -7,7 +7,7 @@ ;;;; Programmer: Kevin M. Rosenberg ;;;; Date Started: Apr 2000 ;;;; -;;;; $Id: hyperobject.asd,v 1.10 2002/11/29 05:05:29 kevin Exp $ +;;;; $Id: hyperobject.asd,v 1.11 2002/12/01 21:07:28 kevin Exp $ ;;;; ;;;; This file is Copyright (c) 2000-2002 by Kevin M. Rosenberg ;;;; ************************************************************************* @@ -22,6 +22,8 @@ ((:file "package") (:file "metaclass" :depends-on ("package")) (:file "mop" :depends-on ("metaclass")) + (:file "connect" :depends-on ("mop")) + (:file "sqlgen" :depends-on ("connect")) (:file "views" :depends-on ("mop")) (:file "base-class" :depends-on ("views")) ) diff --git a/mop.lisp b/mop.lisp index 1e83ae3..39e58b9 100644 --- a/mop.lisp +++ b/mop.lisp @@ -11,7 +11,7 @@ ;;;; in Text, HTML, and XML formats. This includes hyperlinking ;;;; capability and sub-objects. ;;;; -;;;; $Id: mop.lisp,v 1.3 2002/11/29 23:14:31 kevin Exp $ +;;;; $Id: mop.lisp,v 1.4 2002/12/01 21:07:28 kevin Exp $ ;;;; ;;;; This file is Copyright (c) 2000-2002 by Kevin M. Rosenberg ;;;; @@ -34,6 +34,7 @@ :documentation "Class description") (version :initarg :version :initform nil :documentation "Version number for class") + (sql-name :initarg :table-name :initform nil :reader sql-name) ;;; The remainder of these fields are calculated one time ;;; in finalize-inheritence. @@ -45,6 +46,10 @@ (class-id :type integer :initform nil :documentation "Unique ID for the class") + (create-table-cmd :initform nil :reader create-table-cmd) + (create-index-cmds :initform nil :reader create-index-cmds) + (drop-table-cmd :initform nil :reader drop-table-cmd) + (value-func :initform nil :type function) (xmlvalue-func :initform nil :type function) (fmtstr-text :initform nil :type string) @@ -207,6 +212,23 @@ (otherwise ho-type))) + + +(defun ho-type-to-sql-type (sqltype) + (ecase sqltype + (:string + 'string) + (:fixnum + 'fixnum) + (:bigint + 'integer) + (:short-float + 'short-float) + (:long + 'long-float) + (:text + 'string))) + ;;;; Class initialization function (defun process-subobjects (cl) @@ -246,6 +268,7 @@ "Initialize a hyperobject class. Calculates all class slots" (process-subobjects cl) (process-views cl) + (process-sql cl) (process-documentation cl)) diff --git a/sqlgen.lisp b/sqlgen.lisp new file mode 100644 index 0000000..37a2c01 --- /dev/null +++ b/sqlgen.lisp @@ -0,0 +1,272 @@ +;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10; Package: umlisp -*- +;;;; ************************************************************************* +;;;; FILE IDENTIFICATION +;;;; +;;;; Name: sqlgen.lisp +;;;; Purpose: SQL Generation functions for Hyperobject +;;;; Programmer: Kevin M. Rosenberg +;;;; Date Started: Apr 2000 +;;;; +;;;; $Id: sqlgen.lisp,v 1.1 2002/12/01 21:07:28 kevin Exp $ +;;;; +;;;; This file, part of Hyperobject-SQL, is +;;;; Copyright (c) 2000-2002 by Kevin M. Rosenberg, M.D. +;;;; ************************************************************************* + +(in-package :hyperobject) +(declaim (optimize (speed 3) (safety 1) (compilation-speed 0) (debug 3))) + + +;;;; Metaclass initialization commands +(defun process-sql (cl) + (let ((esds (class-slots cl))) + (let* ((table-name-slot (slot-value cl 'sql-name)) + (generate-table-cmd (generate-create-table-string + (if (consp table-name-slot) + (car table-name-slot) + table-name-slot) + dsds))) + (setf (slot-value cl 'create-table-cmd) generate-table-cmd)) + + (dolist (dsd dsds) + (when (dsd-inverse dsd) + (define-inverse cl dsd)))) + ) + +(defun define-inverse (class dsd) + (let ((inverse (dsd-inverse dsd))) + (when inverse + (eval + `(defun ,inverse (key) + (format t "~&Finding key: ~a~%" key) + (make-instance 'st) + )) + + ;; create inverse function + )) + ) + +(defun generate-create-table-string (table-name dsds) + (let ((cmd (format nil "CREATE TABLE ~A (" + (slot-name-to-sql-name table-name)))) + (dolist (dsd dsds) + (unless (eq dsd (car dsds)) + (string-append cmd ", ")) + (string-append cmd (slot-name-to-sql-name + #+allegro (clos:slot-definition-name dsd) + #+lispworks (clos:slot-definition-name dsd) + ) " ") + (let ((length (dsd-length dsd)) + (sql-type (dsd-sql-type dsd))) + (string-append cmd (sql-field-cmd sql-type length)))) + (string-append cmd ")"))) + + +;;;; Runtime Commands + +(defclass sqltable () + () + ) + +(defmethod sql-create ((self sqltable)) + (sql (sql-cmd-create-table self)) + (dolist (cmd (sql-cmd-create-indices self)) + (sql cmd)) + (values)) + +(defmethod sql-drop ((self sqltable)) + (sql (sql-cmd-drop-table self)) + (values)) + +(defmethod sql-insert ((self sqltable)) + (sql + (format nil "INSERT INTO ~a (~a) VALUES (~a)" + (table-name self) (sql-cmd-field-names self) (format-values self)))) + +(defmethod sql-select ((self sqltable) key) + (let ((tuple + (car + (sql + (format nil "SELECT ~a FROM ~a WHERE ~a=~a" + (sql-cmd-field-names self) (table-name self) + (inverse-field-name self) key))))) + (when tuple + (format t "process returned fields")))) + + +(defun format-values (self) + (let ((values "") + (fields (fields self))) + (dolist (field fields) + (unless (eq field (car fields)) + (string-append values ",")) + (let ((name (car field))) + (with-key-value-list (key value (rest field)) + (when (eq key :type) + (string-append values + (ecase value + ((:fixnum :bigint :short-float :double-float) + (write-to-string + (slot-value self name))) + ((:string :text) + (format nil "'~a'" + (add-sql-quotes + (slot-value self name)))))))))) + values)) + +(defmacro defsqltable (tname &key fields) + `(progn + (defclass ,tname (sqltable) + ,(parse-fields tname fields) + ,(default-initargs fields)) + + (defmethod table-name ((self ,tname)) + ,(substitute #\_ #\- (write-to-string tname))) + + (defmethod fields ((self ,tname)) + (quote ,fields)) + + (defmethod sql-cmd-create-table ((self ,tname)) + ,(create-table-string tname fields)) + + (defmethod sql-cmd-create-indices ((self ,tname)) + "Return a list of index cmds" + (quote ,(create-indices-string tname fields))) + + (defmethod sql-cmd-drop-table ((self ,tname)) + ,(format nil "DROP TABLE ~a" tname)) + + (defmethod sql-cmd-field-names ((self ,tname)) + ,(row-field-string fields)) + + (defmethod inverse-field-name ((self ,tname)) + ,(inverse-field-string fields)) + )) + +(defun create-indices-string (table-name fields) + (let (indices) + (dolist (field fields) + (let ((name-string (write-to-string (car field)))) + (with-key-value-list (key value (rest field)) + (when (eq key :unique) + (case value + (nil + (push (sql-cmd-index table-name name-string nil) indices)) + (t + (push (sql-cmd-index table-name name-string t) indices))))))) + indices)) + +(defun inverse-field-string (fields) + (let (inverse) + (dolist (field fields) + (let ((name-string (write-to-string (car field)))) + (with-key-value-list (key value (rest field)) + (when (eq key :inverse) + (setq inverse value))))) + (when inverse + (write-to-string inverse)))) + +(defun sql-cmd-index (table field unique) + (let ((*print-circle* nil)) + (format nil "CREATE ~A INDEX ~A_~A ON ~A(~A)" + (if unique "UNIQUE" "") + (slot-name-to-sql-name table) + (slot-name-to-sql-name field) + (slot-name-to-sql-name table) + (slot-name-to-sql-name field)))) + +(defun row-field-string (fields) + (let ((names "")) + (dolist (field fields) + (unless (eq field (car fields)) + (string-append names ",")) + (string-append names (slot-name-to-sql-name (car field)))) + names)) + +(defun slot-name-to-sql-name (name) + (substitute #\_ #\- (format nil "~a" name))) + +(defun create-table-string (table-name fields) + (let ((cmd (format nil "CREATE TABLE ~A (" (slot-name-to-sql-name table-name)))) + (dolist (field fields) + (unless (eq field (car fields)) + (string-append cmd ", ")) + (string-append cmd (slot-name-to-sql-name (car field)) " ") + (let (length type) + (with-key-value-list (key value (rest field)) + (case key + (:length + (setq length value)) + (:type + (setq type value)))) + (string-append cmd (sql-field-cmd type length)))) + (string-append cmd ")"))) + + +(defun sql-field-cmd (type length) + (ecase type + (:string + (if (< length 8) + (format nil "CHAR(~d)" length) + (format nil "VARCHAR(~d)" length))) + (:text + "LONGTEXT") + (:fixnum + "INTEGER") + (:bigint + "BIGINT") + (:short-float + "SINGLE") + (:long-float + "DOUBLE"))) + +(defmacro with-key-value-list ((key value list) form) + (let ((i (gensym))) + `(loop for ,i from 0 to (1- (length ,list)) by 2 do + (let ((,key (nth ,i ,list)) + (,value (nth (1+ ,i) ,list))) + ,form)))) + +(defun parse-fields (table-name fields) + (let (class-fields) + (dolist (field fields) + (let* ((fname (car field)) + (name-string (write-to-string fname)) + (initarg (intern name-string :keyword))concat-symbol + (def (list fname)) + (options (rest field))) + (with-key-value-list (key value options) + (case key + (:type + (setq def (nconc def (list :type + (ecase value + (:string + 'string) + (:fixnum + 'fixnum) + (:bigint + 'integer) + (:short-float + 'short-float) + (:long + 'long-float) + (:text + 'string)))))))) + (setq def (nconc def (list + :initarg initarg + :accessor (concat-symbol + (write-to-string table-name) "-" + (write-to-string fname))))) + (push def class-fields))) + class-fields)) + +(defun default-initargs (fields) + (let ((initargs (list :default-initargs))) + (dolist (field fields) + (let* ((fname (car field)) + (name-string (write-to-string fname)) + (initarg (intern name-string :keyword))) + (setq initargs (nconc initargs (list initarg nil))))) + initargs)) + +