--- /dev/null
+;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+;;;; *************************************************************************
+;;;;
+;;;; $Id$
+;;;;
+;;;; CLSQL broadcast streams which can be used to monitor the
+;;;; flow of commands to, and results from, a database.
+;;;;
+;;;; This file is part of CLSQL.
+;;;;
+;;;; CLSQL users are granted the rights to distribute and use this software
+;;;; as governed by the terms of the Lisp Lesser GNU Public License
+;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL.
+;;;; *************************************************************************
+
+(in-package #:clsql-sys)
+
+(defun start-sql-recording (&key (type :commands) (database *default-database*))
+ "Begin recording SQL command or result traffic. By default the
+broadcast stream is just *STANDARD-OUTPUT* but this can be modified
+using ADD-SQL-STREAM or DELETE-SQL-STREAM. TYPE determines whether SQL
+command or result traffic is recorded, or both. It must be either
+:commands, :results or :both, and defaults to :commands. DATABASE
+defaults to *default-database*."
+ (when (or (eq type :both) (eq type :commands))
+ (setf (command-recording-stream database)
+ (make-broadcast-stream *standard-output*)))
+ (when (or (eq type :both) (eq type :results))
+ (setf (result-recording-stream database)
+ (make-broadcast-stream *standard-output*)))
+ (values))
+
+(defun stop-sql-recording (&key (type :commands) (database *default-database*))
+ "Stops recording of SQL command or result traffic. TYPE determines
+whether to stop SQL command or result traffic, or both. It must be
+either :commands, :results or :both, defaulting to :commands. DATABASE
+defaults to *default-database*."
+ (when (or (eq type :both) (eq type :commands))
+ (setf (command-recording-stream database) nil))
+ (when (or (eq type :both) (eq type :results))
+ (setf (result-recording-stream database) nil))
+ (values))
+
+(defun sql-recording-p (&key (type :commands) (database *default-database*))
+ "Returns t if recording of TYPE of SQL interaction specified is
+enabled. TYPE must be either :commands, :results, :both or :either.
+DATABASE defaults to *default-database*."
+ (when (or (and (eq type :commands)
+ (command-recording-stream database))
+ (and (eq type :results)
+ (result-recording-stream database))
+ (and (eq type :both)
+ (result-recording-stream database)
+ (command-recording-stream database))
+ (and (eq type :either)
+ (or (result-recording-stream database)
+ (command-recording-stream database))))
+ t))
+
+(defun add-sql-stream (stream &key (type :commands)
+ (database *default-database*))
+ "Add the given STREAM as a component stream for the recording
+broadcast stream for the given SQL interaction TYPE. TYPE must be
+either :commands, :results, or :both, defaulting to :commands.
+DATABASE defaults to *default-database*."
+ (when (or (eq type :both) (eq type :commands))
+ (unless (member stream
+ (list-sql-streams :type :commands :database database))
+ (setf (command-recording-stream database)
+ (apply #'make-broadcast-stream
+ (cons stream (list-sql-streams :type :commands
+ :database database))))))
+ (when (or (eq type :both) (eq type :results))
+ (unless (member stream (list-sql-streams :type :results :database database))
+ (setf (result-recording-stream database)
+ (apply #'make-broadcast-stream
+ (cons stream (list-sql-streams :type :results
+ :database database))))))
+ stream)
+
+(defun delete-sql-stream (stream &key (type :commands)
+ (database *default-database*))
+ "Removes the given STREAM from the recording broadcast stream for
+the given TYPE of SQL interaction. TYPE must be either :commands,
+:results, or :both, defaulting to :commands. DATABASE defaults to
+*default-database*."
+ (when (or (eq type :both) (eq type :commands))
+ (setf (command-recording-stream database)
+ (apply #'make-broadcast-stream
+ (remove stream (list-sql-streams :type :commands
+ :database database)))))
+ (when (or (eq type :both) (eq type :results))
+ (setf (result-recording-stream database)
+ (apply #'make-broadcast-stream
+ (remove stream (list-sql-streams :type :results
+ :database database)))))
+ stream)
+
+(defun list-sql-streams (&key (type :commands) (database *default-database*))
+ "Returns the set of streams which the recording broadcast stream
+send SQL interactions of the given TYPE sends data. TYPE must be
+either :commands, :results, or :both, defaulting to :commands.
+DATABASE defaults to *default-database*."
+ (let ((crs (command-recording-stream database))
+ (rrs (result-recording-stream database)))
+ (cond
+ ((eq type :commands)
+ (when crs (broadcast-stream-streams crs)))
+ ((eq type :results)
+ (when rrs (broadcast-stream-streams rrs)))
+ ((eq type :both)
+ (append (when crs (broadcast-stream-streams crs))
+ (when rrs (broadcast-stream-streams rrs))))
+ (t
+ (error "Unknown recording type. ~A" type)))))
+
+(defun sql-stream (&key (type :commands) (database *default-database*))
+ "Returns the broadcast streams used for recording SQL commands or
+results traffic. TYPE must be either :commands or :results defaulting
+to :commands while DATABASE defaults to *default-database*."
+ (cond
+ ((eq type :commands)
+ (command-recording-stream database))
+ ((eq type :results)
+ (result-recording-stream database))
+ (t
+ (error "Unknown recording type. ~A" type))))
+
+(defun record-sql-command (expr database)
+ (if database
+ (with-slots (command-recording-stream)
+ database
+ (if command-recording-stream
+ (format command-recording-stream "~&;; ~A ~A => ~A~%"
+ (iso-timestring (get-time))
+ (database-name database)
+ expr)))))
+
+(defun record-sql-result (res database)
+ (if database
+ (with-slots (result-recording-stream)
+ database
+ (if result-recording-stream
+ (format result-recording-stream "~&;; ~A ~A <= ~A~%"
+ (iso-timestring (get-time))
+ (database-name database)
+ res)))))
+
+
+