(require 'sql)
(defvar sql-mode-tsql-font-lock-keywords nil
"T-SQL keywords used by font-lock.")
(unless sql-mode-tsql-font-lock-keywords
(debug)
(let ((tsql-keywords (eval-when-compile
(concat "\\b"
(regexp-opt '(
"add" "alter" "authorization" "backup" "begin" "break" "browse" "bulk" "cascade"
"case" "checkpoint" "close" "clustered" "coalesce" "collate" "column" "commit"
"compute" "constraint" "contains" "containstable" "continue" "convert" "cross"
"current_date" "current_time" "current_timestamp" "current_user" "cursor"
"database" "dbcc" "deallocate" "declare" "deny" "disk" "distributed" "double"
"drop" "dummy" "dump" "else" "end" "errlvl" "escape" "except" "exec" "execute"
"exit" "fetch" "file" "fillfactor" "foreign" "freetext" "freetexttable" "full"
"function" "goto" "holdlock" "identity" "identity_insert" "identitycol" "if"
"index" "inner" "intersect" "key" "kill" "lineno" "load" "national" "nocheck"
"nonclustered" "nullif" "off" "offsets" "open" "opendatasource" "openquery"
"openrowset" "openxml" "over" "percent" "plan" "precision" "primary" "print"
"proc" "procedure" "public" "raiserror" "read" "readtext" "reconfigure"
"references" "replication" "restore" "restrict" "return" "revoke" "rollback"
"rowcount" "rowguidcol" "rule" "save" "schema" "session_user" "setuser"
"shutdown" "some" "statistics" "system_user" "textsize" "then" "tran"
"transaction" "trigger" "truncate" "tsequal" "updatetext" "use" "varying"
"waitfor" "when" "while" "writetext"
) t) "\\b")))
(tsql-reserved-words (eval-when-compile
(concat "\\b"
(regexp-opt '(
"all" "and" "any" "as" "asc" "between" "by" "check" "create" "current" "default"
"delete" "desc" "distinct" "exists" "for" "from" "grant" "group" "having" "in"
"inner" "insert" "into" "is" "join" "left" "like" "not" "null" "of" "on"
"option" "or" "order" "outer" "public" "right" "select" "set" "table" "to" "top"
"union" "unique" "update" "user" "values" "view" "where" "with"
) t) "\\b")))
(tsql-types (eval-when-compile
(concat "\\b"
(regexp-opt '(
"bigint" "binary" "bit" "char" "character" "cursor" "datetime" "dec" "decimal"
"float" "image" "int" "integer" "money" "nchar" "ntext" "numeric" "nvarchar"
"real" "smalldatetime" "smallint" "smallmoney" "sql_variant" "table" "text"
"timestamp" "tinyint" "uniqueidentifier" "varbinary" "varchar"
) t) "\\b")))
(tsql-builtin-functions (eval-when-compile
(concat "\\b"
(regexp-opt '(
"@@total_errors" "@@total_read" "@@total_write" "@@trancount" "@@version" "abs"
"acos" "app_name" "ascii" "asin" "atan" "atn2" "case" "cast" "ceiling" "char"
"charindex" "coalesce" "collationproperty" "columnproperty" "col_length"
"col_name" "convert" "cos" "cot" "current_timestamp" "current_user"
"cursor_status" "databaseproperty" "databasepropertyex" "datalength" "dateadd"
"datediff" "datename" "datepart" "day" "db_id" "db_name" "degrees" "difference"
"exp" "filegroupproperty" "filegroup_id" "filegroup_name" "fileproperty"
"file_id" "file_name" "floor" "formatmessage" "fulltextcatalogproperty"
"fulltextserviceproperty" "getansinull" "getdate" "getutcdate" "has_dbaccess"
"host_id" "host_name" "identity" "ident_current" "ident_incr" "ident_seed"
"indexkey_property" "indexproperty" "index_col" "isdate" "isnull" "isnumeric"
"is_member" "is_srvrolemember" "left" "len" "log" "log10" "lower" "ltrim"
"month" "nchar" "newid" "nullif" "objectproperty" "object_id" "object_name"
"parsename" "patindex" "patindex" "permissions" "pi" "power" "quotename"
"radians" "rand" "replace" "replicate" "reverse" "right" "round" "rowcount_big"
"rtrim" "scope_identity" "serverproperty" "sessionproperty" "session_user"
"sign" "sin" "soundex" "space" "sql_variant_property" "sqrt" "square"
"stats_date" "str" "stuff" "substring" "suser_sid" "suser_sname" "system_user"
"tan" "textptr" "textvalid" "typeproperty" "unicode" "upper" "user" "user_id"
"user_name" "year" "fn_helpcollations" "fn_listextendedproperty"
"fn_servershareddrives" "fn_trace_geteventinfo" "fn_trace_getfilterinfo"
"fn_trace_getinfo" "fn_trace_gettable" "fn_virtualfilestats"
"fn_virtualfilestats"
) t) "\\b"))))
(setq sql-mode-tsql-font-lock-keywords
(list (cons tsql-keywords 'font-lock-function-name-face)
(cons tsql-reserved-words 'font-lock-keyword-face)
(cons tsql-types 'font-lock-type-face)
(cons tsql-builtin-functions 'font-lock-builtin-face)))))
(defun sql-highlight-tsql-keywords ()
"Highlight T-SQL keywords.
Set `font-lock-keywords' appropriately."
(interactive)
(setq font-lock-keywords sql-mode-tsql-font-lock-keywords)
(font-lock-fontify-buffer))
(easy-menu-add-item sql-mode-map '("menu-bar" "SQL")
'("Highlighting"
["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-tsql-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-function-name-face
'font-lock-keyword-face
'font-lock-type-face
'font-lock-builtin-face)
"Font-lock faces that are considered keywords.
These will be uppercased if `sql-mode-keyword-upcase-p' is true.")
(defconst sql-mode-keyword-space-regexp
"[\\t ]"
"Regular expression that matches white-space.")
(defun sql-mode-keyword-upcase (beg end)
"Upcase SQL keywords in range."
(interactive "r")
(save-excursion
(let (face
face-list)
(goto-char beg)
(while (and
(< (point) end)
(looking-at sql-mode-keyword-space-regexp))
(forward-char))
(while (< (point) end)
(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
(upcase-word 1)
(setq face-list ()))
(setq face-list (cdr face-list)))))
(forward-word)
(while (and
(< (point) end)
(looking-at sql-mode-keyword-space-regexp))
(forward-char))))))
(defconst sql-indent-blank-regexp
"^[\\t ]*$"
"Regular expression that matches a blank line.")
(defconst sql-indent-begin-regexp
"^[\\t ]*\\bbegin\\b"
"Regular expression that matches a begin block.")
(defconst sql-indent-end-regexp
"^[\\t ]*\\b\\(end[\\t ]*$\\|commit tran\\)\\b"
"Regular expression that matches an end block.
Does not match the end of an inline case statement.")
(defconst sql-indent-if-else-regexp
"^[\\t ]*\\b\\(if\\|else\\)\\b"
"Regular expression that matches an if or else statement.")
(defconst sql-indent-comment-regexp
"^[\\t ]*\\(\\-\\-\\|\\/\\*\\)"
"Regular expression that matches a comment begin or line.")
(defconst sql-indent-comment-line-regexp
"^[\\t ]*\\-\\-"
"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
"^[\\t ]*\\*"
"Regular expression that matches a line starting with `*'.")
(defconst sql-indent-statement-regexp
(eval-when-compile
(concat "^[\\t ]*\\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 "^[\\t ]*\\b"
(regexp-opt '("delete" "insert" "select" "update") t) "\\b"))
"Regular expression that matches the beginning of DML SQL statements.
Ones that start with `select', `insert', `update', or `delete'.")
(defconst sql-indent-select-regexp
"^[\\t ]*\\bselect\\b"
"Regular expression that matches the beginning of a select statement.")
(defconst sql-indent-insert-regexp
"^[\\t ]*\\binsert\\b"
"Regular expression that matches an insert statement.")
(defconst sql-indent-insert-values-regexp
"^[\\t ]*\\binsert\\b.*\\bvalues\\b"
"Regular expression that matches an insert/values statement.")
(defconst sql-indent-cursor-regexp
"^[\\t ]*\\bdeclare\\b.*\\bcursor\\b[\\t ]*\\bfor\\b[\\t ]*$"
"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)
(debug)
(when sql-mode-keyword-upcase-p
(sql-mode-keyword-upcase (point-at-bol) (point-at-eol)))
(let ((line-info nil)
(line-type nil)
(line-keyword nil)
(line-indent nil)
(prev-line-info nil)
(prev-line-type nil)
(prev-line-keyword nil)
(prev-line-indent nil)
(indent nil)
(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))
(if (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))))
(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)))))
(if (and indent (>= indent 0))
(indent-line-to indent)
(indent-line-to 0))))
(defun sql-indent-line-old ()
"Indent current SQL line."
(interactive)
(when sql-mode-keyword-upcase-p
(sql-mode-keyword-upcase (point-at-bol) (point-at-eol)))
(beginning-of-line)
(if (bobp)
(indent-line-to 0)
(let ((not-indented t)
(cur-indent nil)
(cur-line-is-begin nil)
(cur-line-is-end nil)
(cur-line-is-statement nil)
(cur-line-is-continuation nil)
(cur-line-is-blank nil)
(cur-line-is-comment nil)
(cur-line-is-select nil)
(prev-line-is-dml nil)
(prev-line-is-insert nil)
(prev-line-is-cursor nil)
(check-if-else t)
(in-comment nil))
(save-excursion
(when (and
(and (not (bobp))
(looking-at sql-indent-comment-end-regexp)
(not (looking-at sql-indent-comment-begin-regexp))))
(forward-line -1))
(while (and
(and (not (bobp))
(not cur-line-is-comment))
(not (looking-at sql-indent-comment-end-regexp)))
(forward-line -1)
(when (and (looking-at sql-indent-comment-begin-regexp)
(not (looking-at sql-indent-comment-end-regexp)))
(setq cur-line-is-comment t)
(setq cur-indent (current-indentation))
(setq not-indented nil))))
(when not-indented
(cond
((looking-at sql-indent-blank-regexp)
(setq cur-line-is-blank t))
((looking-at sql-indent-begin-regexp)
(setq cur-line-is-begin t))
((looking-at sql-indent-end-regexp)
(setq cur-line-is-end t))
((or
(looking-at sql-indent-statement-regexp)
(looking-at sql-indent-comment-regexp))
(setq cur-line-is-statement t)
(when (looking-at sql-indent-select-regexp)
(setq cur-line-is-select t)))
(t (setq cur-line-is-continuation t)
(setq check-if-else nil))))
(save-excursion
(while (and (not (bobp)) not-indented)
(forward-line -1)
(while (and
(and in-comment
(not (bobp)))
(not (looking-at sql-indent-comment-begin-regexp)))
(forward-line -1))
(setq in-comment nil)
(if (and
(looking-at sql-indent-comment-end-regexp)
(not (looking-at sql-indent-comment-begin-regexp)))
(setq in-comment t)
(unless (looking-at sql-indent-blank-regexp)
(if (looking-at sql-indent-begin-regexp)
(progn
(if cur-line-is-end
(setq cur-indent (current-indentation))
(setq cur-indent (+ (current-indentation) tab-width)))
(setq not-indented nil)
(setq check-if-else nil))
(if (looking-at sql-indent-end-regexp)
(progn
(if cur-line-is-end
(setq cur-indent (- (current-indentation) tab-width))
(setq cur-indent (current-indentation)))
(setq not-indented nil))
(if (looking-at sql-indent-if-else-regexp)
(progn
(if cur-line-is-begin
(setq cur-indent (current-indentation))
(setq cur-indent (+ (current-indentation) tab-width)))
(setq not-indented nil))
(if (or
(looking-at sql-indent-statement-regexp)
(looking-at sql-indent-comment-regexp))
(progn
(if cur-line-is-end
(setq cur-indent (- (current-indentation) tab-width))
(setq cur-indent (current-indentation)))
(when (looking-at sql-indent-dml-regexp)
(setq prev-line-is-dml t))
(when (and
(looking-at sql-indent-insert-regexp)
(not (looking-at sql-indent-insert-values-regexp)))
(setq prev-line-is-insert t))
(when (looking-at sql-indent-cursor-regexp)
(setq prev-line-is-cursor t))
(setq not-indented nil))
))))))
(when (and (bobp) not-indented)
(setq cur-indent 0)
(setq not-indented nil)))
(when check-if-else
(setq not-indented t)
(save-excursion
(while (and not-indented (not (bobp)))
(forward-line -1)
(unless (looking-at sql-indent-blank-regexp)
(if (looking-at sql-indent-begin-regexp)
(setq not-indented nil)
(if (looking-at sql-indent-end-regexp)
(setq not-indented nil)
(if (looking-at sql-indent-if-else-regexp)
(progn
(setq cur-indent (current-indentation))
(setq not-indented nil))
(if (looking-at sql-indent-statement-regexp)
(setq not-indented nil)
())))))))))
(when (and cur-line-is-select prev-line-is-insert)
(setq cur-line-is-continuation t))
(when (and cur-line-is-select prev-line-is-cursor)
(setq cur-line-is-continuation t))
(when (and cur-indent cur-line-is-continuation)
(setq cur-indent (+ cur-indent (/ tab-width 2))))
(when cur-line-is-comment
(setq cur-indent (+ cur-indent 3))
(when (looking-at sql-indent-asterisk-regexp)
(setq cur-indent (- cur-indent 2))))
(if cur-indent
(if (< cur-indent 0)
(indent-line-to 0)
(indent-line-to cur-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 local-sql-mode-hook ()
(set-syntax-table sql-mode-syntax-table)
(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 'local-sql-mode-hook)
(provide 'tsql)