summaryrefslogtreecommitdiff
path: root/lisp/progmodes/sql.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/progmodes/sql.el')
-rw-r--r--lisp/progmodes/sql.el622
1 files changed, 438 insertions, 184 deletions
diff --git a/lisp/progmodes/sql.el b/lisp/progmodes/sql.el
index e7d7494d2ca..51f78bd840b 100644
--- a/lisp/progmodes/sql.el
+++ b/lisp/progmodes/sql.el
@@ -213,7 +213,7 @@
;; Drew Adams <drew.adams@oracle.com> -- Emacs 20 support
;; Harald Maier <maierh@myself.com> -- sql-send-string
;; Stefan Monnier <monnier@iro.umontreal.ca> -- font-lock corrections;
-;; code polish
+;; code polish; on-going guidance and mentorship
;; Paul Sleigh <bat@flurf.net> -- MySQL keyword enhancement
;; Andrew Schein <andrew@andrewschein.com> -- sql-port bug
;; Ian Bjorhovde <idbjorh@dataproxy.com> -- db2 escape newlines
@@ -221,6 +221,8 @@
;; Roman Scherer <roman.scherer@nugg.ad> -- Connection documentation
;; Mark Wilkinson <wilkinsonmr@gmail.com> -- file-local variables ignored
;; Simen Heggestøyl <simenheg@gmail.com> -- Postgres database completion
+;; Robert Cochran <robert-emacs@cochranmail.com> -- MariaDB support
+;; Alex Harsanyi <alexharsanyi@gmail.com> -- sql-indent package and support
;;
@@ -344,7 +346,8 @@ file. Since that is a plaintext file, this could be dangerous."
(const :format "" :completion)
(sexp :tag ":completion")
(const :format "" :must-match)
- (symbol :tag ":must-match")))
+ (restricted-sexp
+ :match-alternatives (listp stringp))))
(const port)))
;; SQL Product support
@@ -415,6 +418,21 @@ file. Since that is a plaintext file, this could be dangerous."
:prompt-regexp "^SQL>"
:prompt-length 4)
+ (mariadb
+ :name "MariaDB"
+ :free-software t
+ :font-lock sql-mode-mariadb-font-lock-keywords
+ :sqli-program sql-mariadb-program
+ :sqli-options sql-mariadb-options
+ :sqli-login sql-mariadb-login-params
+ :sqli-comint-func sql-comint-mariadb
+ :list-all "SHOW TABLES;"
+ :list-table "DESCRIBE %s;"
+ :prompt-regexp "^MariaDB \\[.*]> "
+ :prompt-cont-regexp "^ [\"'`-]> "
+ :syntax-alist ((?# . "< b"))
+ :input-filter sql-remove-tabs-filter)
+
(ms
:name "Microsoft"
:font-lock sql-mode-ms-font-lock-keywords
@@ -691,6 +709,8 @@ making new SQLi sessions."
:version "24.1"
:group 'SQL)
+(defvaralias 'sql-dialect 'sql-product)
+
(defcustom sql-product 'ansi
"Select the SQL database product used.
This allows highlighting buffers properly when you open them."
@@ -703,7 +723,30 @@ This allows highlighting buffers properly when you open them."
sql-product-alist))
:group 'SQL
:safe 'symbolp)
-(defvaralias 'sql-dialect 'sql-product)
+
+;; SQL indent support
+
+(defcustom sql-use-indent-support t
+ "If non-nil then use the SQL indent support features of sql-indent.
+The `sql-indent' package in ELPA provides indentation support for
+SQL statements with easy customizations to support varied layout
+requirements.
+
+The package must be available to be loaded and activated."
+ :group 'SQL
+ :link '(url-link "https://elpa.gnu.org/packages/sql-indent.html")
+ :type 'booleanp
+ :version "27.1")
+
+(defun sql-is-indent-available ()
+ "Check if sql-indent module is available."
+ (when (locate-library "sql-indent")
+ (fboundp 'sqlind-minor-mode)))
+
+(defun sql-indent-enable ()
+ "Enable `sqlind-minor-mode' if available and requested."
+ (when (sql-is-indent-available)
+ (sqlind-minor-mode (if sql-use-indent-support +1 -1))))
;; misc customization of sql.el behavior
@@ -759,16 +802,20 @@ Globally should be set to nil; it will be non-nil in `sql-mode',
(defvar sql-login-delay 7.5 ;; Secs
"Maximum number of seconds you are willing to wait for a login connection.")
-(defcustom sql-pop-to-buffer-after-send-region nil
- "When non-nil, pop to the buffer SQL statements are sent to.
+(defvaralias 'sql-pop-to-buffer-after-send-region 'sql-display-sqli-buffer-function)
-After a call to `sql-sent-string', `sql-send-region',
-`sql-send-paragraph' or `sql-send-buffer', the window is split
-and the SQLi buffer is shown. If this variable is not nil, that
-buffer's window will be selected by calling `pop-to-buffer'. If
-this variable is nil, that buffer is shown using
-`display-buffer'."
- :type 'boolean
+(defcustom sql-display-sqli-buffer-function 'display-buffer
+ "Function to be called to display a SQLi buffer after `sql-send-*'.
+
+When set to a function, it will be called to display the buffer.
+When set to t, the default function `pop-to-buffer' will be
+called. If not set, no attempt will be made to display the
+buffer."
+
+ :type '(choice (const :tag "Default" t)
+ (const :tag "No display" nil)
+ (function :tag "Display Buffer function"))
+ :version "27.1"
:group 'SQL)
;; imenu support for sql-mode.
@@ -788,7 +835,7 @@ this variable is nil, that buffer is shown using
This is used to set `imenu-generic-expression' when SQL mode is
entered. Subsequent changes to `sql-imenu-generic-expression' will
-not affect existing SQL buffers because imenu-generic-expression is
+not affect existing SQL buffers because `imenu-generic-expression' is
a local variable.")
;; history file
@@ -828,15 +875,17 @@ commands when the input history is read, as if you had set
;; The usual hooks
-(defcustom sql-interactive-mode-hook '()
+(defcustom sql-interactive-mode-hook '(sql-indent-enable)
"Hook for customizing `sql-interactive-mode'."
:type 'hook
- :group 'SQL)
+ :group 'SQL
+ :version "27.1")
-(defcustom sql-mode-hook '()
+(defcustom sql-mode-hook '(sql-indent-enable)
"Hook for customizing `sql-mode'."
:type 'hook
- :group 'SQL)
+ :group 'SQL
+ :version "27.1")
(defcustom sql-set-sqli-hook '()
"Hook for reacting to changes of `sql-buffer'.
@@ -953,10 +1002,19 @@ Starts `sql-interactive-mode' after doing some setup."
:version "26.1"
:group 'SQL)
+;; Customization for MariaDB
+
+;; MariaDB is a drop-in replacement for MySQL, so just make the
+;; MariaDB variables aliases of the MySQL ones.
+
+(defvaralias 'sql-mariadb-program 'sql-mysql-program)
+(defvaralias 'sql-mariadb-options 'sql-mysql-options)
+(defvaralias 'sql-mariadb-login-params 'sql-mysql-login-params)
+
;; Customization for MySQL
(defcustom sql-mysql-program "mysql"
- "Command to start mysql by TcX.
+ "Command to start mysql by Oracle.
Starts `sql-interactive-mode' after doing some setup."
:type 'file
@@ -1103,8 +1161,11 @@ add your name with a \"-U\" prefix (such as \"-Umark\") to the list."
(when (executable-find sql-postgres-program)
(let ((res '()))
(ignore-errors
- (dolist (row (process-lines sql-postgres-program "-ltX"))
- (when (string-match "^ \\([[:alnum:]-_]+\\) +|.*" row)
+ (dolist (row (process-lines sql-postgres-program
+ "--list"
+ "--no-psqlrc"
+ "--tuples-only"))
+ (when (string-match "^ \\([^ |]+\\) +|.*" row)
(push (match-string 1 row) res))))
(nreverse res))))
@@ -1237,7 +1298,8 @@ specified, it's `sql-product' or `sql-connection' must match."
(or (not product)
(eq product sql-product))
(or (not connection)
- (eq connection sql-connection)))))))
+ (and (stringp connection)
+ (string= connection sql-connection))))))))
;; Keymap for sql-interactive-mode.
@@ -2312,75 +2374,148 @@ regular expressions are created during compilation by calling the
function `regexp-opt'. Therefore, take a look at the source before
you define your own `sql-mode-solid-font-lock-keywords'.")
+(defvaralias 'sql-mode-mariadb-font-lock-keywords 'sql-mode-mysql-font-lock-keywords
+ "MariaDB is SQL compatible with MySQL.")
+
(defvar sql-mode-mysql-font-lock-keywords
(eval-when-compile
(list
;; MySQL Functions
(sql-font-lock-keywords-builder 'font-lock-builtin-face nil
-"ascii" "avg" "bdmpolyfromtext" "bdmpolyfromwkb" "bdpolyfromtext"
-"bdpolyfromwkb" "benchmark" "bin" "bit_and" "bit_length" "bit_or"
-"bit_xor" "both" "cast" "char_length" "character_length" "coalesce"
-"concat" "concat_ws" "connection_id" "conv" "convert" "count"
-"curdate" "current_date" "current_time" "current_timestamp" "curtime"
-"elt" "encrypt" "export_set" "field" "find_in_set" "found_rows" "from"
+"acos" "adddate" "addtime" "aes_decrypt" "aes_encrypt" "area"
+"asbinary" "ascii" "asin" "astext" "aswkb" "aswkt" "atan" "atan2"
+"avg" "bdmpolyfromtext" "bdmpolyfromwkb" "bdpolyfromtext"
+"bdpolyfromwkb" "benchmark" "bin" "binlog_gtid_pos" "bit_and"
+"bit_count" "bit_length" "bit_or" "bit_xor" "both" "boundary" "buffer"
+"cast" "ceil" "ceiling" "centroid" "character_length" "char_length"
+"charset" "coalesce" "coercibility" "column_add" "column_check"
+"column_create" "column_delete" "column_exists" "column_get"
+"column_json" "column_list" "compress" "concat" "concat_ws"
+"connection_id" "conv" "convert" "convert_tz" "convexhull" "cos" "cot"
+"count" "crc32" "crosses" "cume_dist" "cume_dist" "curdate"
+"current_date" "current_time" "current_timestamp" "curtime" "date_add"
+"datediff" "date_format" "date_sub" "dayname" "dayofmonth" "dayofweek"
+"dayofyear" "decode" "decode_histogram" "degrees" "dense_rank"
+"dense_rank" "des_decrypt" "des_encrypt" "dimension" "disjoint" "div"
+"elt" "encode" "encrypt" "endpoint" "envelope" "exp" "export_set"
+"exteriorring" "extractvalue" "field" "find_in_set" "floor" "format"
+"found_rows" "from" "from_base64" "from_days" "from_unixtime"
"geomcollfromtext" "geomcollfromwkb" "geometrycollectionfromtext"
"geometrycollectionfromwkb" "geometryfromtext" "geometryfromwkb"
-"geomfromtext" "geomfromwkb" "get_lock" "group_concat" "hex" "ifnull"
-"instr" "interval" "isnull" "last_insert_id" "lcase" "leading"
-"length" "linefromtext" "linefromwkb" "linestringfromtext"
-"linestringfromwkb" "load_file" "locate" "lower" "lpad" "ltrim"
-"make_set" "master_pos_wait" "max" "mid" "min" "mlinefromtext"
-"mlinefromwkb" "mpointfromtext" "mpointfromwkb" "mpolyfromtext"
-"mpolyfromwkb" "multilinestringfromtext" "multilinestringfromwkb"
+"geometryn" "geometrytype" "geomfromtext" "geomfromwkb" "get_format"
+"get_lock" "glength" "greatest" "group_concat" "hex" "ifnull"
+"inet6_aton" "inet6_ntoa" "inet_aton" "inet_ntoa" "instr"
+"interiorringn" "intersects" "interval" "isclosed" "isempty"
+"is_free_lock" "is_ipv4" "is_ipv4_compat" "is_ipv4_mapped" "is_ipv6"
+"isnull" "isring" "issimple" "is_used_lock" "json_array"
+"json_array_append" "json_array_insert" "json_compact" "json_contains"
+"json_contains_path" "json_depth" "json_detailed" "json_exists"
+"json_extract" "json_insert" "json_keys" "json_length" "json_loose"
+"json_merge" "json_object" "json_query" "json_quote" "json_remove"
+"json_replace" "json_search" "json_set" "json_type" "json_unquote"
+"json_valid" "json_value" "lag" "last_day" "last_insert_id" "lastval"
+"last_value" "last_value" "lcase" "lead" "leading" "least" "length"
+"linefromtext" "linefromwkb" "linestringfromtext" "linestringfromwkb"
+"ln" "load_file" "locate" "log" "log10" "log2" "lower" "lpad" "ltrim"
+"makedate" "make_set" "maketime" "master_gtid_wait" "master_pos_wait"
+"max" "mbrcontains" "mbrdisjoint" "mbrequal" "mbrintersects"
+"mbroverlaps" "mbrtouches" "mbrwithin" "md5" "median"
+"mid" "min" "mlinefromtext" "mlinefromwkb" "monthname"
+"mpointfromtext" "mpointfromwkb" "mpolyfromtext" "mpolyfromwkb"
+"multilinestringfromtext" "multilinestringfromwkb"
"multipointfromtext" "multipointfromwkb" "multipolygonfromtext"
-"multipolygonfromwkb" "now" "nullif" "oct" "octet_length" "ord"
-"pointfromtext" "pointfromwkb" "polyfromtext" "polyfromwkb"
-"polygonfromtext" "polygonfromwkb" "position" "quote" "rand"
-"release_lock" "repeat" "replace" "reverse" "rpad" "rtrim" "soundex"
-"space" "std" "stddev" "substring" "substring_index" "sum" "sysdate"
-"trailing" "trim" "ucase" "unix_timestamp" "upper" "user" "variance"
+"multipolygonfromwkb" "name_const" "nextval" "now" "nth_value" "ntile"
+"ntile" "nullif" "numgeometries" "numinteriorrings" "numpoints" "oct"
+"octet_length" "old_password" "ord" "percentile_cont"
+"percentile_disc" "percent_rank" "percent_rank" "period_add"
+"period_diff" "pi" "pointfromtext" "pointfromwkb" "pointn"
+"pointonsurface" "polyfromtext" "polyfromwkb" "polygonfromtext"
+"polygonfromwkb" "position" "pow" "power" "quote" "radians"
+"rand" "rank" "rank" "regexp" "regexp_instr" "regexp_replace"
+"regexp_substr" "release_lock" "repeat" "replace" "reverse" "rlike"
+"row_number" "row_number" "rpad" "rtrim" "sec_to_time" "setval" "sha"
+"sha1" "sha2" "sign" "sin" "sleep" "soundex" "space"
+"spider_bg_direct_sql" "spider_copy_tables" "spider_direct_sql"
+"spider_flush_table_mon_cache" "sqrt" "srid" "st_area" "startpoint"
+"st_asbinary" "st_astext" "st_aswkb" "st_aswkt" "st_boundary"
+"st_buffer" "st_centroid" "st_contains" "st_convexhull" "st_crosses"
+"std" "stddev" "stddev_pop" "stddev_samp" "st_difference"
+"st_dimension" "st_disjoint" "st_distance" "st_endpoint" "st_envelope"
+"st_equals" "st_exteriorring" "st_geomcollfromtext"
+"st_geomcollfromwkb" "st_geometrycollectionfromtext"
+"st_geometrycollectionfromwkb" "st_geometryfromtext"
+"st_geometryfromwkb" "st_geometryn" "st_geometrytype"
+"st_geomfromtext" "st_geomfromwkb" "st_interiorringn"
+"st_intersection" "st_intersects" "st_isclosed" "st_isempty"
+"st_isring" "st_issimple" "st_length" "st_linefromtext"
+"st_linefromwkb" "st_linestringfromtext" "st_linestringfromwkb"
+"st_numgeometries" "st_numinteriorrings" "st_numpoints" "st_overlaps"
+"st_pointfromtext" "st_pointfromwkb" "st_pointn" "st_pointonsurface"
+"st_polyfromtext" "st_polyfromwkb" "st_polygonfromtext"
+"st_polygonfromwkb" "strcmp" "st_relate" "str_to_date" "st_srid"
+"st_startpoint" "st_symdifference" "st_touches" "st_union" "st_within"
+"st_x" "st_y" "subdate" "substr" "substring" "substring_index"
+"subtime" "sum" "sysdate" "tan" "timediff" "time_format"
+"timestampadd" "timestampdiff" "time_to_sec" "to_base64" "to_days"
+"to_seconds" "touches" "trailing" "trim" "ucase" "uncompress"
+"uncompressed_length" "unhex" "unix_timestamp" "updatexml" "upper"
+"user" "utc_date" "utc_time" "utc_timestamp" "uuid" "uuid_short"
+"variance" "var_pop" "var_samp" "version" "weekday"
+"weekofyear" "weight_string" "within"
)
;; MySQL Keywords
(sql-font-lock-keywords-builder 'font-lock-keyword-face nil
-"action" "add" "after" "against" "all" "alter" "and" "as" "asc"
-"auto_increment" "avg_row_length" "bdb" "between" "by" "cascade"
-"case" "change" "character" "check" "checksum" "close" "collate"
-"collation" "column" "columns" "comment" "committed" "concurrent"
-"constraint" "create" "cross" "data" "database" "default"
-"delay_key_write" "delayed" "delete" "desc" "directory" "disable"
-"distinct" "distinctrow" "do" "drop" "dumpfile" "duplicate" "else" "elseif"
-"enable" "enclosed" "end" "escaped" "exists" "fields" "first" "for"
-"force" "foreign" "from" "full" "fulltext" "global" "group" "handler"
-"having" "heap" "high_priority" "if" "ignore" "in" "index" "infile"
-"inner" "insert" "insert_method" "into" "is" "isam" "isolation" "join"
-"key" "keys" "last" "left" "level" "like" "limit" "lines" "load"
-"local" "lock" "low_priority" "match" "max_rows" "merge" "min_rows"
-"mode" "modify" "mrg_myisam" "myisam" "natural" "next" "no" "not"
-"null" "offset" "oj" "on" "open" "optionally" "or" "order" "outer"
-"outfile" "pack_keys" "partial" "password" "prev" "primary"
-"procedure" "quick" "raid0" "raid_type" "read" "references" "rename"
-"repeatable" "restrict" "right" "rollback" "rollup" "row_format"
-"savepoint" "select" "separator" "serializable" "session" "set"
-"share" "show" "sql_big_result" "sql_buffer_result" "sql_cache"
-"sql_calc_found_rows" "sql_no_cache" "sql_small_result" "starting"
-"straight_join" "striped" "table" "tables" "temporary" "terminated"
-"then" "to" "transaction" "truncate" "type" "uncommitted" "union"
-"unique" "unlock" "update" "use" "using" "values" "when" "where"
-"with" "write" "xor"
+"accessible" "action" "add" "after" "against" "all" "alter" "analyze"
+"and" "as" "asc" "auto_increment" "avg_row_length" "bdb" "between"
+"body" "by" "cascade" "case" "change" "character" "check" "checksum"
+"close" "collate" "collation" "column" "columns" "comment" "committed"
+"concurrent" "condition" "constraint" "create" "cross" "data"
+"database" "databases" "default" "delayed" "delay_key_write" "delete"
+"desc" "directory" "disable" "distinct" "distinctrow" "do" "drop"
+"dual" "dumpfile" "duplicate" "else" "elseif" "elsif" "enable"
+"enclosed" "end" "escaped" "exists" "exit" "explain" "fields" "first"
+"for" "force" "foreign" "from" "full" "fulltext" "global" "group"
+"handler" "having" "heap" "high_priority" "history" "if" "ignore"
+"ignore_server_ids" "in" "index" "infile" "inner" "insert"
+"insert_method" "into" "is" "isam" "isolation" "join" "key" "keys"
+"kill" "last" "leave" "left" "level" "like" "limit" "linear" "lines"
+"load" "local" "lock" "long" "loop" "low_priority"
+"master_heartbeat_period" "master_ssl_verify_server_cert" "match"
+"max_rows" "maxvalue" "merge" "min_rows" "mode" "modify" "mrg_myisam"
+"myisam" "natural" "next" "no" "not" "no_write_to_binlog" "null"
+"offset" "oj" "on" "open" "optimize" "optionally" "or" "order" "outer"
+"outfile" "over" "package" "pack_keys" "partial" "partition"
+"password" "period" "prev" "primary" "procedure" "purge" "quick"
+"raid0" "raid_type" "raise" "range" "read" "read_write" "references"
+"release" "rename" "repeatable" "require" "resignal" "restrict"
+"returning" "right" "rollback" "rollup" "row_format" "rowtype"
+"savepoint" "schemas" "select" "separator" "serializable" "session"
+"set" "share" "show" "signal" "slow" "spatial" "sql_big_result"
+"sql_buffer_result" "sql_cache" "sql_calc_found_rows" "sql_no_cache"
+"sql_small_result" "ssl" "starting" "straight_join" "striped"
+"system_time" "table" "tables" "temporary" "terminated" "then" "to"
+"transaction" "truncate" "type" "uncommitted" "undo" "union" "unique"
+"unlock" "update" "use" "using" "values" "versioning" "when" "where"
+"while" "window" "with" "write" "xor"
)
;; MySQL Data Types
(sql-font-lock-keywords-builder 'font-lock-type-face nil
-"bigint" "binary" "bit" "blob" "bool" "boolean" "char" "curve" "date"
-"datetime" "dec" "decimal" "double" "enum" "fixed" "float" "geometry"
-"geometrycollection" "int" "integer" "line" "linearring" "linestring"
-"longblob" "longtext" "mediumblob" "mediumint" "mediumtext"
+"bigint" "binary" "bit" "blob" "bool" "boolean" "byte" "char" "curve"
+"date" "datetime" "day" "day_hour" "day_microsecond" "day_minute"
+"day_second" "dec" "decimal" "double" "enum" "fixed" "float" "float4"
+"float8" "geometry" "geometrycollection" "hour" "hour_microsecond"
+"hour_minute" "hour_second" "int" "int1" "int2" "int3" "int4" "int8"
+"integer" "json" "line" "linearring" "linestring" "longblob"
+"longtext" "mediumblob" "mediumint" "mediumtext" "microsecond"
+"middleint" "minute" "minute_microsecond" "minute_second" "month"
"multicurve" "multilinestring" "multipoint" "multipolygon"
"multisurface" "national" "numeric" "point" "polygon" "precision"
-"real" "smallint" "surface" "text" "time" "timestamp" "tinyblob"
-"tinyint" "tinytext" "unsigned" "varchar" "year" "year2" "year4"
-"zerofill"
+"quarter" "real" "second" "second_microsecond" "signed" "smallint"
+"surface" "text" "time" "timestamp" "tinyblob" "tinyint" "tinytext"
+"unsigned" "varbinary" "varchar" "varcharacter" "week" "year" "year2"
+"year4" "year_month" "zerofill"
)))
"MySQL SQL keywords used by font-lock.
@@ -2712,18 +2847,52 @@ adds a fontification pattern to fontify identifiers ending in
;; Save product setting and fontify.
(setq sql-product product)
(sql-highlight-product))
+(defalias 'sql-set-dialect 'sql-set-product)
-
-;;; Compatibility functions
-
-(if (not (fboundp 'comint-line-beginning-position))
- ;; comint-line-beginning-position is defined in Emacs 21
- (defun comint-line-beginning-position ()
- "Return the buffer position of the beginning of the line, after any prompt.
-The prompt is assumed to be any text at the beginning of the line
-matching the regular expression `comint-prompt-regexp', a buffer
-local variable."
- (save-excursion (comint-bol nil) (point))))
+(defun sql-buffer-hidden-p (buf)
+ "Is the buffer hidden?"
+ (string-prefix-p " "
+ (cond
+ ((stringp buf)
+ (when (get-buffer buf)
+ buf))
+ ((bufferp buf)
+ (buffer-name buf))
+ (t nil))))
+
+(defun sql-display-buffer (buf)
+ "Display a SQLi buffer based on `sql-display-sqli-buffer-function'.
+
+If BUF is hidden or `sql-display-sqli-buffer-function' is nil,
+then the buffer will not be displayed. Otherwise the BUF is
+displayed."
+ (unless (sql-buffer-hidden-p buf)
+ (cond
+ ((eq sql-display-sqli-buffer-function t)
+ (pop-to-buffer buf))
+ ((not sql-display-sqli-buffer-function)
+ nil)
+ ((functionp sql-display-sqli-buffer-function)
+ (funcall sql-display-sqli-buffer-function buf))
+ (t
+ (message "Invalid setting of `sql-display-sqli-buffer-function'")
+ (pop-to-buffer buf)))))
+
+(defun sql-make-progress-reporter (buf message &optional min-value max-value current-value min-change min-time)
+ "Make a progress reporter if BUF is not hidden."
+ (unless (or (sql-buffer-hidden-p buf)
+ (not sql-display-sqli-buffer-function))
+ (make-progress-reporter message min-value max-value current-value min-change min-time)))
+
+(defun sql-progress-reporter-update (reporter &optional value)
+ "Report progress of an operation in the echo area."
+ (when reporter
+ (progress-reporter-update reporter value)))
+
+(defun sql-progress-reporter-done (reporter)
+ "Print reporter’s message followed by word \"done\" in echo area."
+ (when reporter
+ (progress-reporter-done reporter)))
;;; SMIE support
@@ -2760,8 +2929,8 @@ local variable."
(prod-stmt (sql-get-product-feature prod :statement)))
(concat "^\\<"
(if prod-stmt
- ansi-stmt
- (concat "\\(" ansi-stmt "\\|" prod-stmt "\\)"))
+ (concat "\\(" ansi-stmt "\\|" prod-stmt "\\)")
+ ansi-stmt)
"\\>")))
(defun sql-beginning-of-statement (arg)
@@ -2952,7 +3121,12 @@ regexp pattern specified in its value.
The `:completion' property prompts for a string specified by its
value. (The property value is used as the PREDICATE argument to
-`completing-read'.)"
+`completing-read'.)
+
+For both `:file' and `:completion', there can also be a
+`:must-match' property that controls REQUIRE-MATCH parameter to
+`completing-read'."
+
(set-default
symbol
(let* ((default (plist-get plist :default))
@@ -2972,7 +3146,9 @@ value. (The property value is used as the PREDICATE argument to
(read-file-name prompt
(file-name-directory last-value)
default
- (plist-get plist :must-match)
+ (if (plist-member plist :must-match)
+ (plist-get plist :must-match)
+ t)
(file-name-nondirectory last-value)
(when (plist-get plist :file)
`(lambda (f)
@@ -2989,7 +3165,9 @@ value. (The property value is used as the PREDICATE argument to
(completing-read prompt-def
(plist-get plist :completion)
nil
- (plist-get plist :must-match)
+ (if (plist-member plist :must-match)
+ (plist-get plist :must-match)
+ t)
last-value
history-var
default))
@@ -3031,21 +3209,21 @@ function like this: (sql-get-login \\='user \\='password \\='database)."
(dolist (w what)
(let ((plist (cdr-safe w)))
(pcase (or (car-safe w) w)
- (`user
+ ('user
(sql-get-login-ext 'sql-user "User: " 'sql-user-history plist))
- (`password
+ ('password
(setq-default sql-password
(read-passwd "Password: " nil (sql-default-value 'sql-password))))
- (`server
+ ('server
(sql-get-login-ext 'sql-server "Server: " 'sql-server-history plist))
- (`database
+ ('database
(sql-get-login-ext 'sql-database "Database: "
'sql-database-history plist))
- (`port
+ ('port
(sql-get-login-ext 'sql-port "Port: "
nil (append '(:number t) plist)))))))
@@ -3129,7 +3307,7 @@ See also `sql-help' on how to create such a buffer."
(sql-set-sqli-buffer))
(display-buffer sql-buffer))
-(defun sql-make-alternate-buffer-name ()
+(defun sql-make-alternate-buffer-name (&optional product)
"Return a string that can be used to rename a SQLi buffer.
This is used to set `sql-alternate-buffer-name' within
`sql-interactive-mode'.
@@ -3151,23 +3329,23 @@ server/database name."
(cdr
(apply #'append nil
(sql-for-each-login
- (sql-get-product-feature sql-product :sqli-login)
+ (sql-get-product-feature (or product sql-product) :sqli-login)
(lambda (token plist)
(pcase token
- (`user
+ ('user
(unless (string= "" sql-user)
(list "/" sql-user)))
- (`port
+ ('port
(unless (or (not (numberp sql-port))
(= 0 sql-port))
(list ":" (number-to-string sql-port))))
- (`server
+ ('server
(unless (string= "" sql-server)
(list "."
(if (plist-member plist :file)
(file-name-nondirectory sql-server)
sql-server))))
- (`database
+ ('database
(unless (string= "" sql-database)
(list "@"
(if (plist-member plist :file)
@@ -3198,6 +3376,34 @@ server/database name."
;; Use the name we've got
name))))
+(defun sql-generate-unique-sqli-buffer-name (product base)
+ "Generate a new, unique buffer name for a SQLi buffer.
+
+Append a sequence number until a unique name is found."
+ (let ((base-name (when (stringp base)
+ (substring-no-properties
+ (or base
+ (sql-get-product-feature product :name)
+ (symbol-name product)))))
+ buf-fmt-1st buf-fmt-rest)
+
+ ;; Calculate buffer format
+ (if base-name
+ (setq buf-fmt-1st (format "*SQL: %s*" base-name)
+ buf-fmt-rest (format "*SQL: %s-%%d*" base-name))
+ (setq buf-fmt-1st "*SQL*"
+ buf-fmt-rest "*SQL-%d*"))
+
+ ;; See if we can find an unused buffer
+ (let ((buf-name buf-fmt-1st)
+ (i 1))
+ (while (sql-buffer-live-p buf-name)
+ ;; Check a sequence number on the BASE
+ (setq buf-name (format buf-fmt-rest i)
+ i (1+ i)))
+
+ buf-name)))
+
(defun sql-rename-buffer (&optional new-name)
"Rename a SQL interactive buffer.
@@ -3213,18 +3419,20 @@ NEW-NAME is empty, then the buffer name will be \"*SQL*\"."
(user-error "Current buffer is not a SQL interactive buffer")
(setq sql-alternate-buffer-name
- (cond
- ((stringp new-name) new-name)
- ((consp new-name)
- (read-string "Buffer name (\"*SQL: XXX*\"; enter `XXX'): "
- sql-alternate-buffer-name))
- (t sql-alternate-buffer-name)))
-
- (setq sql-alternate-buffer-name (substring-no-properties sql-alternate-buffer-name))
- (rename-buffer (if (string= "" sql-alternate-buffer-name)
- "*SQL*"
- (format "*SQL: %s*" sql-alternate-buffer-name))
- t)))
+ (substring-no-properties
+ (cond
+ ((stringp new-name)
+ new-name)
+ ((consp new-name)
+ (read-string "Buffer name (\"*SQL: XXX*\"; enter `XXX'): "
+ sql-alternate-buffer-name))
+ (t
+ sql-alternate-buffer-name))))
+
+ (rename-buffer
+ (sql-generate-unique-sqli-buffer-name sql-product
+ sql-alternate-buffer-name)
+ t)))
(defun sql-copy-column ()
"Copy current column to the end of buffer.
@@ -3439,15 +3647,14 @@ to avoid deleting non-prompt output."
(sql-input-sender (get-buffer-process sql-buffer) s)
;; Send a command terminator if we must
- (if sql-send-terminator
- (sql-send-magic-terminator sql-buffer s sql-send-terminator))
+ (when sql-send-terminator
+ (sql-send-magic-terminator sql-buffer s sql-send-terminator))
- (message "Sent string to buffer %s" sql-buffer)))
+ (when sql-pop-to-buffer-after-send-region
+ (message "Sent string to buffer %s" sql-buffer))))
;; Display the sql buffer
- (if sql-pop-to-buffer-after-send-region
- (pop-to-buffer sql-buffer)
- (display-buffer sql-buffer)))
+ (sql-display-buffer sql-buffer))
;; We don't have no stinkin' sql
(user-error "No SQL process started"))))
@@ -3546,15 +3753,22 @@ of commands accepted by the SQLi program. COMMAND may also be a
list of SQLi command strings."
(let* ((visible (and outbuf
- (not (string= " " (substring outbuf 0 1))))))
+ (not (sql-buffer-hidden-p outbuf))))
+ (this-save save-prior)
+ (next-save t))
+
(when visible
(message "Executing SQL command..."))
+
(if (consp command)
- (mapc (lambda (c) (sql-redirect-one sqlbuf c outbuf save-prior))
- command)
+ (dolist (onecmd command)
+ (sql-redirect-one sqlbuf onecmd outbuf this-save)
+ (setq this-save next-save))
(sql-redirect-one sqlbuf command outbuf save-prior))
+
(when visible
- (message "Executing SQL command...done"))))
+ (message "Executing SQL command...done"))
+ nil))
(defun sql-redirect-one (sqlbuf command outbuf save-prior)
(when command
@@ -3603,7 +3817,7 @@ list of SQLi command strings."
(replace-match "" t t))
(goto-char start))))))))
-(defun sql-redirect-value (sqlbuf command regexp &optional regexp-groups)
+(defun sql-redirect-value (sqlbuf command &optional regexp regexp-groups)
"Execute the SQL command and return part of result.
SQLBUF must be an active SQL interactive buffer. COMMAND should
@@ -3618,7 +3832,7 @@ for each match."
(results nil))
(sql-redirect sqlbuf command outbuf nil)
(with-current-buffer outbuf
- (while (re-search-forward regexp nil t)
+ (while (re-search-forward (or regexp "^.+$") nil t)
(push
(cond
;; no groups-return all of them
@@ -4031,15 +4245,16 @@ Writes the input history to a history file using
This function is a sentinel watching the SQL interpreter process.
Sentinels will always get the two parameters PROCESS and EVENT."
- (with-current-buffer (process-buffer process)
- (let
- ((comint-input-ring-separator sql-input-ring-separator)
- (comint-input-ring-file-name sql-input-ring-file-name))
- (comint-write-input-ring))
+ (when (buffer-live-p (process-buffer process))
+ (with-current-buffer (process-buffer process)
+ (let
+ ((comint-input-ring-separator sql-input-ring-separator)
+ (comint-input-ring-file-name sql-input-ring-file-name))
+ (comint-write-input-ring))
- (if (not buffer-read-only)
- (insert (format "\nProcess %s %s\n" process event))
- (message "Process %s %s" process event))))
+ (if (not buffer-read-only)
+ (insert (format "\nProcess %s %s\n" process event))
+ (message "Process %s %s" process event)))))
@@ -4099,11 +4314,11 @@ is specified in the connection settings."
(mapcar
(lambda (v)
(pcase (car v)
- (`sql-user 'user)
- (`sql-password 'password)
- (`sql-server 'server)
- (`sql-database 'database)
- (`sql-port 'port)
+ ('sql-user 'user)
+ ('sql-password 'password)
+ ('sql-server 'server)
+ ('sql-database 'database)
+ ('sql-port 'port)
(s s)))
connect-set))
@@ -4167,11 +4382,11 @@ optionally is saved to the user's init file."
`(product ,@login)
(lambda (token _plist)
(pcase token
- (`product `(sql-product ',product))
- (`user `(sql-user ,user))
- (`database `(sql-database ,database))
- (`server `(sql-server ,server))
- (`port `(sql-port ,port)))))))
+ ('product `(sql-product ',product))
+ ('user `(sql-user ,user))
+ ('database `(sql-database ,database))
+ ('server `(sql-server ,server))
+ ('port `(sql-port ,port)))))))
(setq alist (append alist (list connect)))
@@ -4215,31 +4430,30 @@ the call to \\[sql-product-interactive] with
;; Handle universal arguments if specified
(when (not (or executing-kbd-macro noninteractive))
- (when (and (consp product)
- (not (cdr product))
- (numberp (car product)))
- (when (>= (prefix-numeric-value product) 16)
- (when (not new-name)
- (setq new-name '(4)))
- (setq product '(4)))))
+ (when (>= (prefix-numeric-value product) 16)
+ (when (not new-name)
+ (setq new-name '(4)))
+ (setq product '(4))))
;; Get the value of product that we need
(setq product
(cond
((= (prefix-numeric-value product) 4) ; C-u, prompt for product
(sql-read-product "SQL product: " sql-product))
- ((and product ; Product specified
- (symbolp product)) product)
+ ((assoc product sql-product-alist) ; Product specified
+ product)
(t sql-product))) ; Default to sql-product
;; If we have a product and it has an interactive mode
(if product
(when (sql-get-product-feature product :sqli-comint-func)
- ;; If no new name specified, try to pop to an active SQL
- ;; interactive for the same product
+ ;; If no new name specified or new name in buffer name,
+ ;; try to pop to an active SQL interactive for the same product
(let ((buf (sql-find-sqli-buffer product sql-connection)))
- (if (and (not new-name) buf)
- (pop-to-buffer buf)
+ (if (and buf (or (not new-name)
+ (and (stringp new-name)
+ (string-match-p (regexp-quote new-name) buf))))
+ (sql-display-buffer buf)
;; We have a new name or sql-buffer doesn't exist or match
;; Start by remembering where we start
@@ -4251,34 +4465,37 @@ the call to \\[sql-product-interactive] with
(sql-get-product-feature product :sqli-login))
;; Connect to database.
- (setq rpt (make-progress-reporter "Login"))
+ (setq rpt (sql-make-progress-reporter nil "Login"))
(let ((sql-user (default-value 'sql-user))
(sql-password (default-value 'sql-password))
(sql-server (default-value 'sql-server))
(sql-database (default-value 'sql-database))
(sql-port (default-value 'sql-port))
- (default-directory (or sql-default-directory
- default-directory)))
+ (default-directory
+ (or sql-default-directory
+ default-directory)))
+
+ ;; Call the COMINT service
(funcall (sql-get-product-feature product :sqli-comint-func)
product
(sql-get-product-feature product :sqli-options)
+ ;; generate a buffer name
(cond
- ((null new-name)
- "*SQL*")
- ((stringp new-name)
- (if (string-prefix-p "*SQL: " new-name t)
- new-name
- (concat "*SQL: " new-name "*")))
- ((equal new-name '(4))
- (concat
- "*SQL: "
+ ((not new-name)
+ (sql-generate-unique-sqli-buffer-name product nil))
+ ((consp new-name)
+ (sql-generate-unique-sqli-buffer-name product
(read-string
"Buffer name (\"*SQL: XXX*\"; enter `XXX'): "
- sql-alternate-buffer-name)
- "*"))
+ (sql-make-alternate-buffer-name product))))
+ ((or (string-prefix-p " " new-name)
+ (string-match-p "\\`[*].*[*]\\'" new-name))
+ new-name)
+ ((stringp new-name)
+ (sql-generate-unique-sqli-buffer-name product new-name))
(t
- (format "*SQL: %s*" new-name)))))
+ (sql-generate-unique-sqli-buffer-name product nil)))))
;; Set SQLi mode.
(let ((sql-interactive-product product))
@@ -4306,25 +4523,26 @@ the call to \\[sql-product-interactive] with
(<= 0.0 (setq secs (- secs step))))
(progn (goto-char (point-max))
(not (re-search-backward sql-prompt-regexp 0 t))))
- (progress-reporter-update rpt)))
+ (sql-progress-reporter-update rpt)))
(goto-char (point-max))
(when (re-search-backward sql-prompt-regexp nil t)
(run-hooks 'sql-login-hook))
;; All done.
- (progress-reporter-done rpt)
- (pop-to-buffer new-sqli-buffer)
+ (sql-progress-reporter-done rpt)
(goto-char (point-max))
- (current-buffer)))))
- (user-error "No default SQL product defined. Set `sql-product'.")))
+ (let ((sql-display-sqli-buffer-function t))
+ (sql-display-buffer new-sqli-buffer))
+ (get-buffer new-sqli-buffer)))))
+ (user-error "No default SQL product defined: set `sql-product'")))
(defun sql-comint (product params &optional buf-name)
"Set up a comint buffer to run the SQL processor.
PRODUCT is the SQL product. PARAMS is a list of strings which are
passed as command line arguments. BUF-NAME is the name of the new
-buffer. If nil, a name is chosen for it."
+buffer. If nil, a name is chosen for it."
(let ((program (sql-get-product-feature product :sqli-program)))
;; Make sure we can find the program. `executable-find' does not
@@ -4337,15 +4555,10 @@ buffer. If nil, a name is chosen for it."
;; if not specified, try *SQL* then *SQL-product*, then *SQL-product1*, ...
;; otherwise, use *buf-name*
(if buf-name
- (unless (string-match-p "\\`[*].*[*]\\'" buf-name)
+ (unless (or (string-prefix-p " " buf-name)
+ (string-match-p "\\`[*].*[*]\\'" buf-name))
(setq buf-name (concat "*" buf-name "*")))
- (setq buf-name "*SQL*")
- (when (sql-buffer-live-p buf-name)
- (setq buf-name (format "*SQL-%s*" product)))
- (let ((i 1))
- (while (sql-buffer-live-p buf-name)
- (setq buf-name (format "*SQL-%s%d*" product i)
- i (1+ i)))))
+ (setq buf-name (sql-generate-unique-sqli-buffer-name product nil)))
(set-text-properties 0 (length buf-name) nil buf-name)
;; Start the command interpreter in the buffer
@@ -4426,7 +4639,8 @@ The default comes from `process-coding-system-alist' and
(or coding 'utf-8))
(when (string-match (format "\\.%s\\'" (car cs)) nlslang)
(setq coding (cdr cs)))))
- (set-buffer-process-coding-system coding coding)))
+ (set-process-coding-system (get-buffer-process (current-buffer))
+ coding coding)))
(defun sql-oracle-save-settings (sqlbuf)
"Save most SQL*Plus settings so they may be reset by \\[sql-redirect]."
@@ -4787,6 +5001,46 @@ The default comes from `process-coding-system-alist' and
(list sql-database)))))
(sql-comint product params buf-name)))
+;;;###autoload
+(defun sql-mariadb (&optional buffer)
+ "Run mysql by MariaDB as an inferior process.
+
+MariaDB is free software.
+
+If buffer `*SQL*' exists but no process is running, make a new process.
+If buffer exists and a process is running, just switch to buffer
+`*SQL*'.
+
+Interpreter used comes from variable `sql-mariadb-program'. Login uses
+the variables `sql-user', `sql-password', `sql-database', and
+`sql-server' as defaults, if set. Additional command line parameters
+can be stored in the list `sql-mariadb-options'.
+
+The buffer is put in SQL interactive mode, giving commands for sending
+input. See `sql-interactive-mode'.
+
+To set the buffer name directly, use \\[universal-argument]
+before \\[sql-mariadb]. Once session has started,
+\\[sql-rename-buffer] can be called separately to rename the
+buffer.
+
+To specify a coding system for converting non-ASCII characters
+in the input and output to the process, use \\[universal-coding-system-argument]
+before \\[sql-mariadb]. You can also specify this with \\[set-buffer-process-coding-system]
+in the SQL buffer, after you start the process.
+The default comes from `process-coding-system-alist' and
+`default-process-coding-system'.
+
+\(Type \\[describe-mode] in the SQL buffer for a list of commands.)"
+ (interactive "P")
+ (sql-product-interactive 'mariadb buffer))
+
+(defun sql-comint-mariadb (product options &optional buf-name)
+ "Create comint buffer and connect to MariaDB.
+
+Use the MySQL comint driver since the two are compatible."
+ (sql-comint-mysql product options buf-name))
+
;;;###autoload