(require 'sql)
(defvar sql-mode-mysql-font-lock-keywords nil
"MySQL keywords used by font-lock.")
(eval-when-compile
(setq sql-mode-mysql-font-lock-keywords
(list
(sql-font-lock-keywords-builder 'font-lock-type-face nil
"ada" "asensitive" "assignment" "asymmetric" "atomic" "between"
"bigint" "binary" "bit" "blob" "char" "character" "date" "datetime" "dec"
"decimal" "double" "enum" "float" "float4" "float8" "int" "int1" "int2" "int3"
"int4" "int8" "integer" "long" "longblob" "longtext" "mediumblob" "mediumint"
"mediumtext" "numeric" "precision" "real" "smallint" "time" "timestamp"
"tinyblob" "tinyint" "tinytext" "unsigned" "varbinary" "varchar"
"varcharacter" "year" "zerofill"
)
(sql-font-lock-keywords-builder 'font-lock-keyword-face nil
"all" "alter" "and" "as" "asc" "between" "by" "check" "create" "cross"
"declare" "default" "delete" "desc" "distinct" "drop" "exists" "for" "from"
"grant" "group" "having" "in" "inner" "insert" "into" "is" "join" "left"
"like" "not" "null" "on" "option" "or" "order" "outer" "right" "select" "set"
"table" "to" "top" "union" "unique" "update" "values" "view" "where" "with"
)
(sql-font-lock-keywords-builder 'font-lock-function-name-face nil
"add" "after" "analyze" "asensitive" "before" "both" "call" "cascade" "btree"
"change" "collate" "column" "condition" "connection" "constraint" "continue"
"convert" "cursor" "database" "databases" "delayed" "describe" "deterministic"
"distinctrow" "div" "dual" "each" "else" "elseif" "enclosed" "escaped" "exit"
"explain" "false" "fetch" "force" "foreign" "fulltext" "goto" "high_priority"
"if" "ignore" "index" "infile" "inout" "insensitive" "interval" "iterate"
"key" "keys" "kill" "label" "leading" "leave" "leave" "limit" "lines" "load"
"lock" "loop" "match" "mod" "modifies" "natural" "optimize" "optionally" "out"
"outfile" "primary" "procedure" "purge" "raid0" "read" "reads" "real"
"references" "regexp" "release" "rename" "repeat" "replace" "require"
"restrict" "return" "revoke" "rlike" "schema" "schemas" "sensitive"
"separator" "show" "soname" "spatial" "specific" "sql" "sqlexception"
"sqlstate" "sqlwarning" "ssl" "starting" "straight_join" "terminated" "then"
"trailing" "trigger" "true" "undo" "unlock" "unsigned" "upgrade" "usage" "use"
"using" "varying" "when" "while" "write" "x509" "xor"
)
(sql-font-lock-keywords-builder 'font-lock-builtin-face nil
"case" "current_date" "current_time" "current_timestamp" "current_user"
"day_hour" "day_microsecond" "day_minute" "day_second" "hour_microsecond"
"hour_minute" "hour_second" "localtime" "localtimestamp" "low_priority"
"minute_microsecond" "minute_second" "no_write_to_binlog" "second_microsecond"
"sql_big_result" "sql_calc_found_rows" "sql_small_result" "utc_date"
"utc_time" "utc_timestamp" "year_month"
))))
(defun sql-highlight-mysql-keywords ()
"Highlight MySQL keywords.
Set `font-lock-keywords' appropriately."
(interactive)
(setq font-lock-keywords sql-mode-mysql-font-lock-keywords)
(font-lock-fontify-buffer))
(easy-menu-add-item sql-mode-map '("menu-bar" "SQL")
'("Highlighting"
["MySQL keywords" sql-highlight-mysql-keywords t]
["T-SQL keywords" sql-highlight-tsql-keywords t]
["ANSI SQL keywords" sql-highlight-ansi-keywords t]
["Oracle keywords" sql-highlight-oracle-keywords t]
["Postgres keywords" sql-highlight-postgres-keywords t]))
(setq sql-mode-font-lock-keywords sql-mode-mysql-font-lock-keywords)
(defvar sql-mode-syntax-table
(let ((table (make-syntax-table)))
(modify-syntax-entry ?/ ". 14" table)
(modify-syntax-entry ?* ". 23" table)
(if (string-match "XEmacs\\|Lucid" emacs-version)
(modify-syntax-entry ?- ". 56" table)
(modify-syntax-entry ?- ". 12b" table))
(modify-syntax-entry ?\n "> b" table)
(modify-syntax-entry ?\f "> b" table)
(modify-syntax-entry ?' "\"" table)
(modify-syntax-entry ?_ "w" table)
(modify-syntax-entry ?@ "w" table)
table)
"Syntax table used in `sql-mode' and `sql-interactive-mode'.")
(defvar sql-mode-keyword-upcase-p t
"Whether or not to upcase SQL keywords.")
(defvar sql-mode-keyword-upcase-face
(list 'font-lock-type-face
'font-lock-keyword-face
'font-lock-function-name-face
'font-lock-builtin-face)
"Font-lock faces that are considered keywords.
\nThese will be uppercased if `sql-mode-keyword-upcase-p' is
true.")
(defconst sql-mode-keyword-space-regexp
"\\s-"
"Regular expression that matches a single white-space character.")
(defun sql-mode-keyword-upcase (beg end)
"Upcase SQL keywords in range."
(interactive "*r")
(save-excursion
(let (face
face-list)
(goto-char beg)
(while (< (point) end)
(while (and
(< (point) end)
(looking-at sql-mode-keyword-space-regexp))
(forward-char))
(setq face (get-char-property (point) 'face))
(when face
(setq face-list sql-mode-keyword-upcase-face)
(while face-list
(if (equal face (car face-list))
(progn
(save-excursion
(upcase-word 1))
(setq face-list ()))
(setq face-list (cdr face-list)))))
(forward-word 1)))))
(defconst sql-indent-blank-regexp
"^\\s-*$"
"Regular expression that matches a blank line.")
(defconst sql-indent-begin-regexp
"^\\s-*\\bbegin\\b"
"Regular expression that matches a begin block.")
(defconst sql-indent-end-regexp
"^\\s-*\\b\\(end\\s-*$\\|commit tran\\)\\b"
"Regular expression that matches an end block.
\nDoes not match the end of an inline case statement.")
(defconst sql-indent-if-else-regexp
"^\\s-*\\b\\(if\\|else\\)\\b"
"Regular expression that matches an if or else statement.")
(defconst sql-indent-comment-regexp
"^\\s-*\\(\\-\\-\\|\\/\\*\\)"
"Regular expression that matches a comment begin or line.")
(defconst sql-indent-comment-line-regexp
"^\\s-*\\-\\-"
"Regular expression that matches a comment line.")
(defconst sql-indent-comment-begin-regexp
"^.*\\/\\*"
"Regular expression that matches a comment begin.")
(defconst sql-indent-comment-end-regexp
"^.*\\*\\/"
"Regular expression that matches a comment end.")
(defconst sql-indent-asterisk-regexp
"^\\s-*\\*"
"Regular expression that matches a line starting with `*'.")
(defconst sql-indent-statement-regexp
(eval-when-compile
(concat "^\\s-*\\b"
(regexp-opt '(
"add" "alter" "as" "authorization" "backup" "break" "browse" "bulk" "cascade"
"checkpoint" "close" "clustered" "coalesce" "collate" "column" "commit"
"compute" "constraint" "contains" "containstable" "continue" "convert"
"create" "cross" "current_date" "current_time" "current_timestamp"
"current_user" "cursor" "database" "dbcc" "deallocate" "declare" "delete"
"deny" "disk" "distributed" "drop" "dummy" "dump" "else" "errlvl" "escape"
"except" "exec" "execute" "exit" "fetch" "file" "for" "freetext"
"freetexttable" "full" "go" "goto" "grant" "holdlock" "identity_insert"
"identitycol" "if" "insert" "intersect" "key" "kill" "lineno" "load"
"national" "offsets" "open" "opendatasource" "openquery" "openrowset"
"openxml" "over" "plan" "print" "public" "raiserror" "read" "reconfigure"
"references" "replication" "restore" "restrict" "return" "revoke" "rollback"
"rowguidcol" "rule" "save" "schema" "select" "session_user" "set" "setuser"
"shutdown" "some" "statistics" "system_user" "textsize" "truncate" "tsequal"
"union" "update" "use" "varying" "waitfor" "while"
) t) "\\b"))
"Regular expression that matches the beginning of SQL statements.")
(defconst sql-indent-dml-regexp
(eval-when-compile
(concat "^\\s-*\\b"
(regexp-opt '("delete" "insert" "select" "update") t) "\\b"))
"Regular expression that matches the beginning of DML SQL statements.
\nOnes that start with `select', `insert', `update', or `delete'.")
(defconst sql-indent-select-regexp
"^\\s-*\\bselect\\b"
"Regular expression that matches the beginning of a select statement.")
(defconst sql-indent-insert-regexp
"^\\s-*\\binsert\\b"
"Regular expression that matches an insert statement.")
(defconst sql-indent-insert-values-regexp
"^\\s-*\\binsert\\b.*\\bvalues\\b"
"Regular expression that matches an insert/values statement.")
(defconst sql-indent-cursor-regexp
"^\\s-*\\bdeclare\\b.*\\bcursor\\b\\s-*\\bfor\\b\\s-*$"
"Regular expression that matches a declare cursor statement.")
(defun sql-indent-line-get-info ()
"Get info about statement on current line.
Return a list containing the following:
type: Type of SQL statement
keyword: Starting SQL keyword (lowercased)
indent: Column number of indentation
Move point to start of statement. If in a comment block, will
move point to the start of the comment block.
You may call it again after doing `forward-word -1' to get info
on the previous statement.
Possible types are:
bob: Beginning of block
comment-line: -- type of comment
comment-block-begin: /* */ type of comment (first line)
comment-block-end: /* */ type of comment (last line)
comment-block-middle: /* */ type of comment (a middle line)
blank: Blank line
begin: Begin statement (block begin)
end: End statement (block end)
if-else: If or else statement
comment: (Should never happend; should get a more specific type, above)
statement: Other sql statement
statement-select: An sql statement that may be followed by a select
continue: Continuation of an sql statement"
(interactive)
(let ((type nil)
(keyword nil)
(indent nil)
(case-fold-search t))
(beginning-of-line)
(if (bobp)
(progn
(setq type 'bob)
(setq indent 0))
(progn
(goto-char (+ (point-at-bol) (current-indentation)))
(when (eq (get-char-property (point) 'face) 'font-lock-comment-face)
(save-excursion
(beginning-of-line)
(cond
((looking-at sql-indent-comment-line-regexp)
(setq type 'comment-line)
(save-excursion
(forward-line -1)
(end-of-line)
(while (and (not (bobp))
(eq (get-char-property (point) 'face)
'font-lock-comment-face))
(forward-char -1))
(when (looking-at sql-indent-comment-begin-regexp)
(setq type 'comment-block-middle))))
((looking-at sql-indent-comment-begin-regexp)
(setq type 'comment-block-begin))
((looking-at sql-indent-comment-end-regexp)
(setq type 'comment-block-end))
(t (setq type 'comment-block-middle)))))
(when type
(while (and (not (bobp))
(eq (get-char-property (point) 'face)
'font-lock-comment-face))
(forward-line -1)
(beginning-of-line)
(unless (bobp)
(goto-char (+ (point-at-bol) (current-indentation)))))
(unless (and (bobp)
(eq (get-char-property (point) 'face)
'font-lock-comment-face))
(forward-line 1)))
(unless type
(beginning-of-line)
(cond
((looking-at sql-indent-blank-regexp)
(setq type 'blank))
((looking-at sql-indent-begin-regexp)
(setq type 'begin))
((looking-at sql-indent-end-regexp)
(setq type 'end))
((looking-at sql-indent-if-else-regexp)
(setq type 'if-else))
((looking-at sql-indent-comment-regexp)
(setq type 'comment))
((looking-at sql-indent-statement-regexp)
(setq type 'statement)
(when (and
(looking-at sql-indent-insert-regexp)
(not (looking-at sql-indent-insert-values-regexp)))
(setq type 'statement-select))
(when (looking-at sql-indent-cursor-regexp)
(setq type 'statement-select)))
(t (setq type 'continue))))))
(goto-char (+ (point-at-bol) (current-indentation)))
(when (stringp (thing-at-point 'word))
(setq keyword (downcase (thing-at-point 'word))))
(unless indent
(setq indent (current-indentation)))
(list type keyword indent)))
(defun sql-indent-line-get-info-root ()
"Call sql-indent-line-get-info until a non-continue type line is reached.
\nTypes `continue' and `statement-select' are skipped."
(let (line-info)
(while (or (not line-info)
(eq (car line-info) 'continue)
(eq (car line-info) 'statement-select))
(setq line-info (sql-indent-line-get-info)))))
(defun sql-indent-line-is-comment (type)
"Test if type is a comment symbol."
(or
(eq type 'comment)
(eq type 'comment-line)
(eq type 'comment-block-begin)
(eq type 'comment-block-end)
(eq type 'comment-block-middle)))
(defun sql-indent-line-is-statement (type)
"Test if type is a statement symbol."
(or
(eq type 'statement)
(eq type 'statement-select)))
(defun sql-indent-line ()
"Indent current SQL line."
(interactive)
(when sql-mode-keyword-upcase-p
(sql-mode-keyword-upcase (point-at-bol) (point-at-eol)))
(let (line-info
line-type
line-keyword
line-indent
prev-line-info
prev-line-type
prev-line-keyword
prev-line-indent
first-prev-line-indent
indent
(check-if-else t))
(save-excursion
(setq line-info (sql-indent-line-get-info))
(setq line-type (car line-info))
(setq line-keyword (car (cdr line-info)))
(setq line-indent (car (cdr (cdr line-info))))
(when (eq line-type 'bob)
(setq indent line-indent))
(when (or (eq line-type 'comment-block-middle)
(eq line-type 'comment-block-end))
(setq indent (+ line-indent 3))
(when (looking-at sql-indent-asterisk-regexp)
(setq indent (- indent 2))))
(unless indent
(while (and (not (bobp)) (not indent))
(forward-line -1)
(setq prev-line-info (sql-indent-line-get-info))
(setq prev-line-type (car prev-line-info))
(setq prev-line-keyword (car (cdr prev-line-info)))
(setq prev-line-indent (car (cdr (cdr prev-line-info))))
(unless first-prev-line-indent
(setq first-prev-line-indent prev-line-indent))
(when (eq prev-line-type 'bob)
(setq indent prev-line-indent))
(when (eq prev-line-type 'begin)
(setq indent (+ prev-line-indent tab-width))
(setq check-if-else nil))
(when (and (eq line-type 'end)
(not (eq prev-line-type 'continue))
(not (eq prev-line-type 'statement-select)))
(if (eq prev-line-type 'begin)
(setq indent prev-line-indent)
(setq indent (- prev-line-indent tab-width)))
(setq check-if-else nil))
(when (eq prev-line-type 'if-else)
(if (eq line-type 'begin)
(setq indent prev-line-indent)
(setq indent (+ prev-line-indent tab-width))))
(when (and (not indent)
(or
(sql-indent-line-is-statement prev-line-type)
(sql-indent-line-is-comment prev-line-type)
(eq prev-line-type 'end)))
(setq indent prev-line-indent)
(when (and
(eq prev-line-type 'statement-select)
(eq line-type 'statement)
(string= line-keyword "select"))
(setq indent (+ indent (/ tab-width 2)))
(setq check-if-else nil)))
))
(while (and (not (bobp))
(not (eq line-type 'continue))
(not (eq line-type 'statement-select))
(not (eq prev-line-type 'begin))
(not (eq prev-line-type 'end))
(not (eq prev-line-type 'if-else))
check-if-else)
(forward-line -1)
(setq prev-line-info (sql-indent-line-get-info))
(setq prev-line-type (car prev-line-info))
(setq prev-line-keyword (car (cdr prev-line-info)))
(setq prev-line-indent (car (cdr (cdr prev-line-info))))
(when (eq prev-line-type 'bob)
(setq check-if-else nil))
(when (eq prev-line-type 'begin)
(setq check-if-else nil))
(when (eq prev-line-type 'end)
(setq check-if-else nil))
(when (eq prev-line-type 'if-else)
(setq indent (current-indentation))
(setq check-if-else nil))
(when (sql-indent-line-is-statement prev-line-type)
(setq check-if-else nil))
)
(when (and (bobp) (not indent))
(setq indent 0))
(when (eq line-type 'continue)
(setq indent (+ indent (/ tab-width 2))))
(when (eq line-type 'blank)
(setq indent first-prev-line-indent)))
(if (and indent (>= indent 0))
(indent-line-to indent)
(indent-line-to 0))))
(defun indent-newline-and-indent ()
"Indent current line, then add a newline at the end, then indent the new line."
(interactive)
(save-excursion
(indent-for-tab-command))
(newline-and-indent))
(defun mysql-mode-hook ()
(set-syntax-table sql-mode-syntax-table)
(sql-add-product-keywords 'mysql sql-mode-mysql-font-lock-keywords 'set)
(set (make-local-variable 'indent-line-function) 'sql-indent-line)
(local-set-key (kbd "<return>") 'indent-newline-and-indent)
(local-set-key (kbd "C-m") 'indent-newline-and-indent))
(add-hook 'sql-mode-hook 'mysql-mode-hook)
(provide 'mysql)