package are built-in inside emacsql
This commit is contained in:
@@ -1,9 +0,0 @@
|
|||||||
(define-package "emacsql-sqlite-builtin" "20250220.1155" "EmacSQL back-end for SQLite using builtin support" 'nil :commit "b868ee6bda90022379730432610f040c62882064" :authors
|
|
||||||
'(("Jonas Bernoulli" . "emacs.emacsql@jonas.bernoulli.dev"))
|
|
||||||
:maintainers
|
|
||||||
'(("Jonas Bernoulli" . "emacs.emacsql@jonas.bernoulli.dev"))
|
|
||||||
:maintainer
|
|
||||||
'("Jonas Bernoulli" . "emacs.emacsql@jonas.bernoulli.dev"))
|
|
||||||
;; Local Variables:
|
|
||||||
;; no-byte-compile: t
|
|
||||||
;; End:
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
;;; emacsql-sqlite-builtin.el --- EmacSQL back-end for SQLite using builtin support -*- lexical-binding:t -*-
|
|
||||||
|
|
||||||
;; This is free and unencumbered software released into the public domain.
|
|
||||||
|
|
||||||
;; Author: Jonas Bernoulli <emacs.emacsql@jonas.bernoulli.dev>
|
|
||||||
;; Maintainer: Jonas Bernoulli <emacs.emacsql@jonas.bernoulli.dev>
|
|
||||||
|
|
||||||
;; SPDX-License-Identifier: Unlicense
|
|
||||||
|
|
||||||
;;; Commentary:
|
|
||||||
|
|
||||||
;; This library provides an EmacSQL back-end for SQLite, which uses
|
|
||||||
;; the built-in SQLite support in Emacs 29 an later.
|
|
||||||
|
|
||||||
;;; Code:
|
|
||||||
|
|
||||||
(require 'emacsql-sqlite)
|
|
||||||
|
|
||||||
(require 'sqlite nil t)
|
|
||||||
(declare-function sqlite-open "sqlite")
|
|
||||||
(declare-function sqlite-select "sqlite")
|
|
||||||
(declare-function sqlite-close "sqlite")
|
|
||||||
|
|
||||||
(emacsql-register-reserved emacsql-sqlite-reserved)
|
|
||||||
|
|
||||||
(defclass emacsql-sqlite-builtin-connection (emacsql--sqlite-base) ()
|
|
||||||
"A connection to a SQLite database using builtin support.")
|
|
||||||
|
|
||||||
(cl-defmethod initialize-instance :after
|
|
||||||
((connection emacsql-sqlite-builtin-connection) &rest _)
|
|
||||||
(require (quote sqlite))
|
|
||||||
(oset connection handle
|
|
||||||
(sqlite-open (oref connection file)))
|
|
||||||
(emacsql-sqlite-set-busy-timeout connection)
|
|
||||||
(emacsql connection [:pragma (= foreign-keys on)])
|
|
||||||
(emacsql-register connection))
|
|
||||||
|
|
||||||
(cl-defun emacsql-sqlite-builtin (file &key debug)
|
|
||||||
"Open a connected to database stored in FILE.
|
|
||||||
If FILE is nil use an in-memory database.
|
|
||||||
|
|
||||||
:debug LOG -- When non-nil, log all SQLite commands to a log
|
|
||||||
buffer. This is for debugging purposes."
|
|
||||||
(let ((connection (make-instance #'emacsql-sqlite-builtin-connection
|
|
||||||
:file file)))
|
|
||||||
(when debug
|
|
||||||
(emacsql-enable-debugging connection))
|
|
||||||
connection))
|
|
||||||
|
|
||||||
(cl-defmethod emacsql-live-p ((connection emacsql-sqlite-builtin-connection))
|
|
||||||
(and (oref connection handle) t))
|
|
||||||
|
|
||||||
(cl-defmethod emacsql-close ((connection emacsql-sqlite-builtin-connection))
|
|
||||||
(sqlite-close (oref connection handle))
|
|
||||||
(oset connection handle nil))
|
|
||||||
|
|
||||||
(cl-defmethod emacsql-send-message
|
|
||||||
((connection emacsql-sqlite-builtin-connection) message)
|
|
||||||
(condition-case err
|
|
||||||
(let ((headerp emacsql-include-header))
|
|
||||||
(mapcar (lambda (row)
|
|
||||||
(cond
|
|
||||||
(headerp (setq headerp nil) row)
|
|
||||||
((mapcan (lambda (col)
|
|
||||||
(cond ((null col) (list nil))
|
|
||||||
((equal col "") (list ""))
|
|
||||||
((numberp col) (list col))
|
|
||||||
((emacsql-sqlite-read-column col))))
|
|
||||||
row))))
|
|
||||||
(sqlite-select (oref connection handle) message nil
|
|
||||||
(and emacsql-include-header 'full))))
|
|
||||||
((sqlite-error sqlite-locked-error)
|
|
||||||
(if (stringp (cdr err))
|
|
||||||
(signal 'emacsql-error (list (cdr err)))
|
|
||||||
(pcase-let* ((`(,_ ,errstr ,errmsg ,errcode ,ext-errcode) err)
|
|
||||||
(`(,_ ,_ ,signal ,_)
|
|
||||||
(assq errcode emacsql-sqlite-error-codes)))
|
|
||||||
(signal (or signal 'emacsql-error)
|
|
||||||
(list errmsg errcode ext-errcode errstr)))))
|
|
||||||
(error
|
|
||||||
(signal 'emacsql-error (cdr err)))))
|
|
||||||
|
|
||||||
(cl-defmethod emacsql ((connection emacsql-sqlite-builtin-connection) sql &rest args)
|
|
||||||
(emacsql-send-message connection (apply #'emacsql-compile connection sql args)))
|
|
||||||
|
|
||||||
(provide 'emacsql-sqlite-builtin)
|
|
||||||
|
|
||||||
;;; emacsql-sqlite-builtin.el ends here
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
(define-package "emacsql-sqlite" "20250301.1637" "Code used by multiple SQLite back-ends" 'nil :commit "f111b0acc79eadeeb3c6c1332d943f11fd6932ff" :authors
|
|
||||||
'(("Jonas Bernoulli" . "emacs.emacsql@jonas.bernoulli.dev"))
|
|
||||||
:maintainers
|
|
||||||
'(("Jonas Bernoulli" . "emacs.emacsql@jonas.bernoulli.dev"))
|
|
||||||
:maintainer
|
|
||||||
'("Jonas Bernoulli" . "emacs.emacsql@jonas.bernoulli.dev"))
|
|
||||||
;; Local Variables:
|
|
||||||
;; no-byte-compile: t
|
|
||||||
;; End:
|
|
||||||
@@ -1,296 +0,0 @@
|
|||||||
;;; emacsql-sqlite.el --- Code used by multiple SQLite back-ends -*- lexical-binding:t -*-
|
|
||||||
|
|
||||||
;; This is free and unencumbered software released into the public domain.
|
|
||||||
|
|
||||||
;; Author: Jonas Bernoulli <emacs.emacsql@jonas.bernoulli.dev>
|
|
||||||
;; Maintainer: Jonas Bernoulli <emacs.emacsql@jonas.bernoulli.dev>
|
|
||||||
|
|
||||||
;; SPDX-License-Identifier: Unlicense
|
|
||||||
|
|
||||||
;;; Commentary:
|
|
||||||
|
|
||||||
;; This library contains code that is used by multiple SQLite back-ends.
|
|
||||||
|
|
||||||
;;; Code:
|
|
||||||
|
|
||||||
(require 'emacsql)
|
|
||||||
|
|
||||||
;;; Base class
|
|
||||||
|
|
||||||
(defclass emacsql--sqlite-base (emacsql-connection)
|
|
||||||
((file :initarg :file
|
|
||||||
:initform nil
|
|
||||||
:type (or null string)
|
|
||||||
:documentation "Database file name.")
|
|
||||||
(types :allocation :class
|
|
||||||
:reader emacsql-types
|
|
||||||
:initform '((integer "INTEGER")
|
|
||||||
(float "REAL")
|
|
||||||
(object "TEXT")
|
|
||||||
(nil nil))))
|
|
||||||
:abstract t)
|
|
||||||
|
|
||||||
;;; Constants
|
|
||||||
|
|
||||||
(defconst emacsql-sqlite-reserved
|
|
||||||
'( ABORT ACTION ADD AFTER ALL ALTER ANALYZE AND AS ASC ATTACH
|
|
||||||
AUTOINCREMENT BEFORE BEGIN BETWEEN BY CASCADE CASE CAST CHECK
|
|
||||||
COLLATE COLUMN COMMIT CONFLICT CONSTRAINT CREATE CROSS
|
|
||||||
CURRENT_DATE CURRENT_TIME CURRENT_TIMESTAMP DATABASE DEFAULT
|
|
||||||
DEFERRABLE DEFERRED DELETE DESC DETACH DISTINCT DROP EACH ELSE END
|
|
||||||
ESCAPE EXCEPT EXCLUSIVE EXISTS EXPLAIN FAIL FOR FOREIGN FROM FULL
|
|
||||||
GLOB GROUP HAVING IF IGNORE IMMEDIATE IN INDEX INDEXED INITIALLY
|
|
||||||
INNER INSERT INSTEAD INTERSECT INTO IS ISNULL JOIN KEY LEFT LIKE
|
|
||||||
LIMIT MATCH NATURAL NO NOT NOTNULL NULL OF OFFSET ON OR ORDER
|
|
||||||
OUTER PLAN PRAGMA PRIMARY QUERY RAISE RECURSIVE REFERENCES REGEXP
|
|
||||||
REINDEX RELEASE RENAME REPLACE RESTRICT RIGHT ROLLBACK ROW
|
|
||||||
SAVEPOINT SELECT SET TABLE TEMP TEMPORARY THEN TO TRANSACTION
|
|
||||||
TRIGGER UNION UNIQUE UPDATE USING VACUUM VALUES VIEW VIRTUAL WHEN
|
|
||||||
WHERE WITH WITHOUT)
|
|
||||||
"List of all of SQLite's reserved words.
|
|
||||||
Also see http://www.sqlite.org/lang_keywords.html.")
|
|
||||||
|
|
||||||
(defconst emacsql-sqlite-error-codes
|
|
||||||
'((1 SQLITE_ERROR emacsql-error "SQL logic error")
|
|
||||||
(2 SQLITE_INTERNAL emacsql-internal nil)
|
|
||||||
(3 SQLITE_PERM emacsql-access "access permission denied")
|
|
||||||
(4 SQLITE_ABORT emacsql-error "query aborted")
|
|
||||||
(5 SQLITE_BUSY emacsql-locked "database is locked")
|
|
||||||
(6 SQLITE_LOCKED emacsql-locked "database table is locked")
|
|
||||||
(7 SQLITE_NOMEM emacsql-memory "out of memory")
|
|
||||||
(8 SQLITE_READONLY emacsql-access "attempt to write a readonly database")
|
|
||||||
(9 SQLITE_INTERRUPT emacsql-error "interrupted")
|
|
||||||
(10 SQLITE_IOERR emacsql-access "disk I/O error")
|
|
||||||
(11 SQLITE_CORRUPT emacsql-corruption "database disk image is malformed")
|
|
||||||
(12 SQLITE_NOTFOUND emacsql-error "unknown operation")
|
|
||||||
(13 SQLITE_FULL emacsql-access "database or disk is full")
|
|
||||||
(14 SQLITE_CANTOPEN emacsql-access "unable to open database file")
|
|
||||||
(15 SQLITE_PROTOCOL emacsql-access "locking protocol")
|
|
||||||
(16 SQLITE_EMPTY emacsql-corruption nil)
|
|
||||||
(17 SQLITE_SCHEMA emacsql-error "database schema has changed")
|
|
||||||
(18 SQLITE_TOOBIG emacsql-error "string or blob too big")
|
|
||||||
(19 SQLITE_CONSTRAINT emacsql-constraint "constraint failed")
|
|
||||||
(20 SQLITE_MISMATCH emacsql-error "datatype mismatch")
|
|
||||||
(21 SQLITE_MISUSE emacsql-error "bad parameter or other API misuse")
|
|
||||||
(22 SQLITE_NOLFS emacsql-error "large file support is disabled")
|
|
||||||
(23 SQLITE_AUTH emacsql-access "authorization denied")
|
|
||||||
(24 SQLITE_FORMAT emacsql-corruption nil)
|
|
||||||
(25 SQLITE_RANGE emacsql-error "column index out of range")
|
|
||||||
(26 SQLITE_NOTADB emacsql-corruption "file is not a database")
|
|
||||||
(27 SQLITE_NOTICE emacsql-warning "notification message")
|
|
||||||
(28 SQLITE_WARNING emacsql-warning "warning message"))
|
|
||||||
"Alist mapping SQLite error codes to EmacSQL conditions.
|
|
||||||
Elements have the form (ERRCODE SYMBOLIC-NAME EMACSQL-ERROR
|
|
||||||
ERRSTR). Also see https://www.sqlite.org/rescode.html.")
|
|
||||||
|
|
||||||
;;; Variables
|
|
||||||
|
|
||||||
(defvar emacsql-include-header nil
|
|
||||||
"Whether to include names of columns as an additional row.
|
|
||||||
Never enable this globally, only let-bind it around calls to `emacsql'.
|
|
||||||
Currently only supported by `emacsql-sqlite-builtin-connection' and
|
|
||||||
`emacsql-sqlite-module-connection'.")
|
|
||||||
|
|
||||||
(defvar emacsql-sqlite-busy-timeout 20
|
|
||||||
"Seconds to wait when trying to access a table blocked by another process.
|
|
||||||
See https://www.sqlite.org/c3ref/busy_timeout.html.")
|
|
||||||
|
|
||||||
;;; Utilities
|
|
||||||
|
|
||||||
(defun emacsql-sqlite-connection (variable file &optional setup use-module)
|
|
||||||
"Return the connection stored in VARIABLE to the database in FILE.
|
|
||||||
|
|
||||||
If the value of VARIABLE is a live database connection, return that.
|
|
||||||
|
|
||||||
Otherwise open a new connection to the database in FILE and store the
|
|
||||||
connection in VARIABLE, before returning it. If FILE is nil, use an
|
|
||||||
in-memory database. Always enable support for foreign key constrains.
|
|
||||||
If optional SETUP is non-nil, it must be a function, which takes the
|
|
||||||
connection as only argument. This function can be used to initialize
|
|
||||||
tables, for example.
|
|
||||||
|
|
||||||
If optional USE-MODULE is non-nil, then use the external module even
|
|
||||||
when Emacs was built with SQLite support. This is intended for testing
|
|
||||||
purposes."
|
|
||||||
(or (let ((connection (symbol-value variable)))
|
|
||||||
(and connection (emacsql-live-p connection) connection))
|
|
||||||
(set variable (emacsql-sqlite-open file nil setup use-module))))
|
|
||||||
|
|
||||||
(defun emacsql-sqlite-open (file &optional debug setup use-module)
|
|
||||||
"Open a connection to the database stored in FILE using an SQLite back-end.
|
|
||||||
|
|
||||||
Automatically use the best available back-end, as returned by
|
|
||||||
`emacsql-sqlite-default-connection'.
|
|
||||||
|
|
||||||
If FILE is nil, use an in-memory database. If optional DEBUG is
|
|
||||||
non-nil, log all SQLite commands to a log buffer, for debugging
|
|
||||||
purposes. Always enable support for foreign key constrains.
|
|
||||||
|
|
||||||
If optional SETUP is non-nil, it must be a function, which takes the
|
|
||||||
connection as only argument. This function can be used to initialize
|
|
||||||
tables, for example.
|
|
||||||
|
|
||||||
If optional USE-MODULE is non-nil, then use the external module even
|
|
||||||
when Emacs was built with SQLite support. This is intended for testing
|
|
||||||
purposes."
|
|
||||||
(when file
|
|
||||||
(make-directory (file-name-directory file) t))
|
|
||||||
(let* ((class (emacsql-sqlite-default-connection use-module))
|
|
||||||
(connection (make-instance class :file file)))
|
|
||||||
(when debug
|
|
||||||
(emacsql-enable-debugging connection))
|
|
||||||
(emacsql connection [:pragma (= foreign-keys on)])
|
|
||||||
(when setup
|
|
||||||
(funcall setup connection))
|
|
||||||
connection))
|
|
||||||
|
|
||||||
(defun emacsql-sqlite-default-connection (&optional use-module)
|
|
||||||
"Determine and return the best SQLite connection class.
|
|
||||||
|
|
||||||
Signal an error if none of the connection classes can be used.
|
|
||||||
|
|
||||||
If optional USE-MODULE is non-nil, then use the external module even
|
|
||||||
when Emacs was built with SQLite support. This is intended for testing
|
|
||||||
purposes."
|
|
||||||
(or (and (not use-module)
|
|
||||||
(fboundp 'sqlite-available-p)
|
|
||||||
(sqlite-available-p)
|
|
||||||
(require 'emacsql-sqlite-builtin)
|
|
||||||
'emacsql-sqlite-builtin-connection)
|
|
||||||
(and (boundp 'module-file-suffix)
|
|
||||||
module-file-suffix
|
|
||||||
(condition-case nil
|
|
||||||
;; Failure modes:
|
|
||||||
;; 1. `libsqlite' shared library isn't available.
|
|
||||||
;; 2. User chooses to not compile `libsqlite'.
|
|
||||||
;; 3. `libsqlite' compilation fails.
|
|
||||||
(and (require 'sqlite3)
|
|
||||||
(require 'emacsql-sqlite-module)
|
|
||||||
'emacsql-sqlite-module-connection)
|
|
||||||
(error
|
|
||||||
(display-warning 'emacsql "\
|
|
||||||
Since your Emacs does not come with
|
|
||||||
built-in SQLite support [1], but does support C modules, we can
|
|
||||||
use an EmacSQL backend that relies on the third-party `sqlite3'
|
|
||||||
package [2].
|
|
||||||
|
|
||||||
Please install the `sqlite3' Elisp package using your preferred
|
|
||||||
Emacs package manager, and install the SQLite shared library
|
|
||||||
using your distribution's package manager. That package should
|
|
||||||
be named something like `libsqlite3' [3] and NOT just `sqlite3'.
|
|
||||||
|
|
||||||
The legacy backend, which uses a custom SQLite executable, has
|
|
||||||
been remove, so we can no longer fall back to that.
|
|
||||||
|
|
||||||
[1]: Supported since Emacs 29.1, provided it was not disabled
|
|
||||||
with `--without-sqlite3'.
|
|
||||||
[2]: https://github.com/pekingduck/emacs-sqlite3-api
|
|
||||||
[3]: On Debian https://packages.debian.org/buster/libsqlite3-0")
|
|
||||||
;; The buffer displaying the warning might immediately
|
|
||||||
;; be replaced by another buffer, before the user gets
|
|
||||||
;; a chance to see it. We cannot have that.
|
|
||||||
(let (fn)
|
|
||||||
(setq fn (lambda ()
|
|
||||||
(remove-hook 'post-command-hook fn)
|
|
||||||
(pop-to-buffer (get-buffer "*Warnings*"))))
|
|
||||||
(add-hook 'post-command-hook fn))
|
|
||||||
nil)))
|
|
||||||
(error "EmacSQL could not find or compile a back-end")))
|
|
||||||
|
|
||||||
(defun emacsql-sqlite-set-busy-timeout (connection)
|
|
||||||
(when emacsql-sqlite-busy-timeout
|
|
||||||
(emacsql connection [:pragma (= busy-timeout $s1)]
|
|
||||||
(* emacsql-sqlite-busy-timeout 1000))))
|
|
||||||
|
|
||||||
(defun emacsql-sqlite-read-column (string)
|
|
||||||
(let ((value nil)
|
|
||||||
(beg 0)
|
|
||||||
(end (length string)))
|
|
||||||
(while (< beg end)
|
|
||||||
(let ((v (read-from-string string beg)))
|
|
||||||
(push (car v) value)
|
|
||||||
(setq beg (cdr v))))
|
|
||||||
(nreverse value)))
|
|
||||||
|
|
||||||
(defun emacsql-sqlite-list-tables (connection)
|
|
||||||
"Return a list of symbols identifying tables in CONNECTION.
|
|
||||||
Tables whose names begin with \"sqlite_\", are not included
|
|
||||||
in the returned value."
|
|
||||||
(mapcar #'car
|
|
||||||
(emacsql connection
|
|
||||||
[:select name
|
|
||||||
;; The new name is `sqlite-schema', but this name
|
|
||||||
;; is supported by old and new SQLite versions.
|
|
||||||
;; See https://www.sqlite.org/schematab.html.
|
|
||||||
:from sqlite-master
|
|
||||||
:where (and (= type 'table)
|
|
||||||
(not-like name "sqlite_%"))
|
|
||||||
:order-by [(asc name)]])))
|
|
||||||
|
|
||||||
(defun emacsql-sqlite-dump-database (connection &optional versionp)
|
|
||||||
"Dump the database specified by CONNECTION to a file.
|
|
||||||
|
|
||||||
The dump file is placed in the same directory as the database
|
|
||||||
file and its name derives from the name of the database file.
|
|
||||||
The suffix is replaced with \".sql\" and if optional VERSIONP is
|
|
||||||
non-nil, then the database version (the `user_version' pragma)
|
|
||||||
and a timestamp are appended to the file name.
|
|
||||||
|
|
||||||
Dumping is done using the official `sqlite3' binary. If that is
|
|
||||||
not available and VERSIONP is non-nil, then the database file is
|
|
||||||
copied instead."
|
|
||||||
(let* ((version (caar (emacsql connection [:pragma user-version])))
|
|
||||||
(db (oref connection file))
|
|
||||||
(db (if (symbolp db) (symbol-value db) db))
|
|
||||||
(name (file-name-nondirectory db))
|
|
||||||
(output (concat (file-name-sans-extension db)
|
|
||||||
(and versionp
|
|
||||||
(concat (format "-v%s" version)
|
|
||||||
(format-time-string "-%Y%m%d-%H%M")))
|
|
||||||
".sql")))
|
|
||||||
(cond
|
|
||||||
((locate-file "sqlite3" exec-path)
|
|
||||||
(when (and (file-exists-p output) versionp)
|
|
||||||
(error "Cannot dump database; %s already exists" output))
|
|
||||||
(with-temp-file output
|
|
||||||
(message "Dumping %s database to %s..." name output)
|
|
||||||
(unless (zerop (save-excursion
|
|
||||||
(call-process "sqlite3" nil t nil db ".dump")))
|
|
||||||
(error "Failed to dump %s" db))
|
|
||||||
(when version
|
|
||||||
(insert (format "PRAGMA user_version=%s;\n" version)))
|
|
||||||
;; The output contains "PRAGMA foreign_keys=OFF;".
|
|
||||||
;; Change that to avoid alarming attentive users.
|
|
||||||
(when (re-search-forward "^PRAGMA foreign_keys=\\(OFF\\);" 1000 t)
|
|
||||||
(replace-match "ON" t t nil 1))
|
|
||||||
(message "Dumping %s database to %s...done" name output)))
|
|
||||||
(versionp
|
|
||||||
(setq output (concat (file-name-sans-extension output) ".db"))
|
|
||||||
(message "Cannot dump database because sqlite3 binary cannot be found")
|
|
||||||
(when (and (file-exists-p output) versionp)
|
|
||||||
(error "Cannot copy database; %s already exists" output))
|
|
||||||
(message "Copying %s database to %s..." name output)
|
|
||||||
(copy-file db output)
|
|
||||||
(message "Copying %s database to %s...done" name output))
|
|
||||||
((error "Cannot dump database; sqlite3 binary isn't available")))))
|
|
||||||
|
|
||||||
(defun emacsql-sqlite-restore-database (db dump)
|
|
||||||
"Restore database DB from DUMP.
|
|
||||||
|
|
||||||
DUMP is a file containing SQL statements. DB can be the file
|
|
||||||
in which the database is to be stored, or it can be a database
|
|
||||||
connection. In the latter case the current database is first
|
|
||||||
dumped to a new file and the connection is closed. Then the
|
|
||||||
database is restored from DUMP. No connection to the new
|
|
||||||
database is created."
|
|
||||||
(unless (stringp db)
|
|
||||||
(emacsql-sqlite-dump-database db t)
|
|
||||||
(emacsql-close (prog1 db (setq db (oref db file)))))
|
|
||||||
(with-temp-buffer
|
|
||||||
(unless (zerop (call-process "sqlite3" nil t nil db
|
|
||||||
(format ".read %s" dump)))
|
|
||||||
(error "Failed to read %s: %s" dump (buffer-string)))))
|
|
||||||
|
|
||||||
(provide 'emacsql-sqlite)
|
|
||||||
|
|
||||||
;;; emacsql-sqlite.el ends here
|
|
||||||
Reference in New Issue
Block a user