Newer
Older
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; ;;;
;;; Free Software published under an MIT-like license. See LICENSE ;;;
;;; ;;;
;;; Copyright (c) 2012-2013 Google, Inc. All rights reserved. ;;;
;;; ;;;
;;; Original author: Alejandro Sedeño ;;;
;;; ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun mysql-connect (&key (host "localhost") (port 3306) (username "") (password "") database (ssl :unspecified) ssl-verify)
"Connect to a MySQL over a network (AF_INET) socket and begin the MySQL Handshake.
Returns a QMYND:MYSQL-CONNECTION object or signals a QMYND:MYSQL-ERROR.
Accepts the following keyword arguments:
HOST: The host to connect to. (default: \"localhost\")
PORT: The port to connect to. (default: 3306)
USERNAME: User to authenticate as.
PASSWORD: Password to authenticate with.
DATABASE: What database to use upon connecting. (default: nil)
SSL: Whether or not to use SSL. (default: :UNSPECIFIED)
T - Forces SSL (or error out if it's not available).
NIL - Disable SSL, even if it is available.
:UNSPECIFIED - Opportunistic use of SSL.
SSL-VERIFY: Whether or not to verify the SSL certificate presented by the server. (Default: nil)"
(let* ((socket (usocket:socket-connect host port
:protocol :stream
:element-type '(unsigned-byte 8)))
(connection (make-instance 'mysql-inet-connection
:stream (usocket:socket-stream socket)
:default-schema database)))
(mysql-connect-do-handshake connection username password database
:ssl ssl
:ssl-verify ssl-verify)))
;;; AF_LOCAL sockets should really be folded into usocket. For now, just implement CCL and SBCL support.
#+(or ccl sbcl)
(defun mysql-local-connect (&key (path #P"/var/run/mysqld/mysqld.sock") (username "") (password "") database)
"Connect to a MySQL over a local (AF_LOCAL) socket and begin the MySQL Handshake.
Returns a QMYND:MYSQL-CONNECTION object or signals a QMYND:MYSQL-ERROR.
Accepts the following keyword arguments:
PATH: The path of the local socket to connect to. (default: #P\"/var/run/mysqld/mysqld.sock\")
USERNAME: User to authenticate as.
PASSWORD: Password to authenticate with.
DATABASE: What database to use upon connecting. (default: nil)"
;; Open Socket
(let* ((socket
#+ccl (ccl:make-socket :address-family :file
:connect :active
:remote-filename path)
#+sbcl (let ((socket (make-instance 'sb-bsd-sockets:local-socket
:type :stream)))
(sb-bsd-sockets:socket-connect socket (etypecase path
(pathname (namestring path))
(string path)))
socket)
)
(connection (make-instance 'mysql-local-connection
:socket socket
:stream
#+ccl socket
#+sbcl (sb-bsd-sockets:socket-make-stream
socket :input t
:output t
:element-type '(unsigned-byte 8))
:default-schema database)))
(mysql-connect-do-handshake connection username password database :ssl nil)))
(defmethod mysql-disconnect ((c mysql-base-connection))
(when (mysql-connection-connected c)
(with-mysql-connection (c)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Simple SQL Queries
(defun mysql-query (connection query-string
&key row-fn (as-text nil) (result-type 'vector))
"Send a SQL Query over the connection using the MySQL Text Protocol.
For queries that return rows, returns two values:
A vector of rows, each of which is a vector of columns.
A vector of column descriptors.
For queries that don't return rows, returns a QMYND:RESPONSE-OK-PACKET.
May signal a QMYND:MYSQL-ERROR."
(with-mysql-connection (connection)
(send-command-query query-string
:row-fn row-fn
:as-text as-text
:result-type result-type)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Prepared Statements
(defun mysql-statement-prepare (connection query-string)
"Prepares a SQL Query using the MySQL Prepared Statement Protocol.
Returns a QMYND:MYSQL-PREPARED-STATEMENT.
NB: The QMYND:MYSQL-PREPARED-STATEMENT remembers the
QMYND:MYSQL-CONNECTION it was prepared for."
(with-mysql-connection (connection)
(send-command-statement-prepare query-string)))
(defmethod mysql-statement-execute ((statement mysql-prepared-statement) &key parameters)
"Executes a QMYND:MYSQL-PREPARED-STATEMENT.
Accepts the following keyword arguments:
PARAMETERS: a sequence of parameters for the placeholders in the prepared statement, if any."
(let ((connection (mysql-prepared-statement-connection statement)))
(unless connection (error 'invalid-prepared-statement))
(with-mysql-connection (connection)
(send-command-statement-execute statement :parameters parameters))))
(defmethod mysql-statement-close ((statement mysql-prepared-statement))
"Deallocates and invalidates STATEMENT."
(let ((connection (mysql-prepared-statement-connection statement)))
(unless connection (error 'invalid-prepared-statement))
(with-mysql-connection (connection)
(send-command-statement-close statement)
(mysql-connection-remove-stale-prepared-statements *mysql-connection*)
(values))))