{"id":13440414,"url":"https://github.com/magit/emacsql","last_synced_at":"2025-12-29T22:39:32.878Z","repository":{"id":13274066,"uuid":"15959613","full_name":"magit/emacsql","owner":"magit","description":"A high-level Emacs Lisp RDBMS front-end ","archived":false,"fork":false,"pushed_at":"2024-04-15T15:35:33.000Z","size":4200,"stargazers_count":529,"open_issues_count":9,"forks_count":39,"subscribers_count":26,"default_branch":"main","last_synced_at":"2024-04-16T00:57:25.717Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/magit.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"custom":"https://magit.vc/donate","github":"tarsius"}},"created_at":"2014-01-16T06:06:21.000Z","updated_at":"2024-05-30T00:44:30.494Z","dependencies_parsed_at":"2023-02-12T05:17:16.168Z","dependency_job_id":"ad0d73b4-616f-45d1-a28d-e0b5492fecb8","html_url":"https://github.com/magit/emacsql","commit_stats":{"total_commits":466,"total_committers":12,"mean_commits":"38.833333333333336","dds":0.2424892703862661,"last_synced_commit":"59de83a1276a5fbcf8a682b64bbdcf5e00c6ce8b"},"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/magit%2Femacsql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/magit%2Femacsql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/magit%2Femacsql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/magit%2Femacsql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/magit","download_url":"https://codeload.github.com/magit/emacsql/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244586037,"owners_count":20476864,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T03:01:22.545Z","updated_at":"2025-12-29T22:39:32.840Z","avatar_url":"https://github.com/magit.png","language":"C","funding_links":["https://magit.vc/donate","https://github.com/sponsors/tarsius"],"categories":["C","Emacs Lisp"],"sub_categories":[],"readme":"# EmacSQL\n\nEmacSQL is a high-level Emacs Lisp front-end for SQLite.\n\nPostgreSQL and MySQL are also supported, but use of these connectors\nis not recommended.\n\nAny [readable lisp value][readable] can be stored as a value in\nEmacSQL, including numbers, strings, symbols, lists, vectors, and\nclosures. EmacSQL has no concept of \"TEXT\" values; it's all just lisp\nobjects. The lisp object `nil` corresponds 1:1 with `NULL` in the\ndatabase.\n\nRequires Emacs 26 or later.\n\n[![Compile](https://github.com/magit/emacsql/actions/workflows/compile.yml/badge.svg)](https://github.com/magit/emacsql/actions/workflows/compile.yml)\n[![Test](https://github.com/magit/emacsql/actions/workflows/test.yml/badge.svg)](https://github.com/magit/emacsql/actions/workflows/test.yml)\n[![NonGNU ELPA](https://emacsair.me/assets/badges/nongnu-elpa.svg)](https://elpa.nongnu.org/nongnu-devel/emacsql.html)\n[![MELPA Stable](https://stable.melpa.org/packages/emacsql-badge.svg)](https://stable.melpa.org/#/emacsql)\n[![MELPA](https://melpa.org/packages/emacsql-badge.svg)](https://melpa.org/#/emacsql)\n\n### FAQ\n#### Why are all values stored as strings?\n\nEmacSQL is not intended to interact with arbitrary databases, but to\nbe an ACID-compliant database for Emacs extensions.  This means that\nEmacSQL cannot be used with a regular SQL database used by other\nnon-Emacs clients.\n\nAll database values must be s-expressions. When EmacSQL stores a\nvalue — string, symbol, cons, etc. — it is printed and written to\nthe database in its printed form. Strings are wrapped in quotes\nand escaped as necessary. That means \"bare\" symbols in the database\ngenerally look like strings. The only exception is `nil`, which is\nstored as `NULL`.\n\n#### Will EmacSQL ever support arbitrary databases?\n\nThe author of EmacSQL [thinks][mistake] that it was probably a\ndesign mistake to restrict it to Emacs by storing only printed values,\nand that it would be a lot more useful if it just handled primitive\ndatabase types.\n\nHowever, EmacSQL is in maintenance mode and there are no plans to\nmake any fundamental changes, not least because they would break all\nexisting packages and databases that rely on the current EmacSQL\nbehavior.\n\n### Windows Issues\n\nEmacs `start-process-shell-command` function is not supported on\nWindows. Since both `emacsql-mysql` and `emacsql-psql` rely on this\nfunction, neither of these connection types are supported on Windows.\n\n## Example Usage\n\n```el\n(defvar db (emacsql-sqlite-open \"~/company.db\"))\n\n;; Create a table. Table and column identifiers are symbols.\n(emacsql db [:create-table people ([name id salary])])\n\n;; Or optionally provide column constraints.\n(emacsql db [:create-table people\n             ([name (id integer :primary-key) (salary float)])])\n\n;; Insert some data:\n(emacsql db [:insert :into people\n             :values ([\"Jeff\" 1000 60000.0] [\"Susan\" 1001 64000.0])])\n\n;; Query the database for results:\n(emacsql db [:select [name id]\n             :from people\n             :where (\u003e salary 62000)])\n;; =\u003e ((\"Susan\" 1001))\n\n;; Queries can be templates, using $1, $2, etc.:\n(emacsql db [:select [name id]\n             :from people\n             :where (\u003e salary $s1)]\n         50000)\n;; =\u003e ((\"Jeff\" 1000) (\"Susan\" 1001))\n```\n\nWhen editing these prepared SQL s-expression statements, the `M-x\nemacsql-show-last-sql` command (think `eval-last-sexp`) is useful for\nseeing what the actual SQL expression will become when compiled.\n\n## Schema\n\nA table schema is a list whose first element is a vector of column\nspecifications. The rest of the list specifies table constraints. A\ncolumn identifier is a symbol and a column's specification can either\nbe just this symbol or it can include constraints as a list. Because\nEmacSQL stores entire lisp objects as values, the only relevant (and\nallowed) types are `integer`, `float`, and `object` (default).\n\n    ([(\u003ccolumn\u003e) ...] (\u003ctable-constraint\u003e ...) ...])\n\nDashes in identifiers are converted into underscores when compiled\ninto SQL. This allows for lisp-style identifiers to be used in SQL.\nConstraints follow the compilation rules below.\n\n```el\n;; No constraints schema with four columns:\n([name id building room])\n\n;; Add some column constraints:\n([(name :unique) (id integer :primary-key) building room])\n\n;; Add some table constraints:\n([(name :unique) (id integer :primary-key) building room]\n (:unique [building room])\n (:check (\u003e id 0)))\n```\n\nHere's an example using foreign keys.\n\n```el\n;; \"subjects\" table schema\n([(id integer :primary-key) subject])\n\n;; \"tag\" table references subjects\n([(subject-id integer) tag]\n (:foreign-key [subject-id] :references subjects [id]\n               :on-delete :cascade))\n```\n\nForeign key constraints are enabled by default in EmacSQL.\n\n## Operators\n\nExpressions are written lisp-style, with the operator first. If it\nlooks like an operator EmacSQL treats it like an operator. However,\nseveral operators are special.\n\n    \u003c=    \u003e=    funcall    quote\n\nThe `\u003c=` and `\u003e=` operators accept 2 or 3 operands, transforming into\na SQL `_ BETWEEN _ AND _` operator as appropriate.\n\nFor function-like \"operators\" like `count` and `max` use the `funcall`\n\"operator.\"\n\n```el\n[:select (funcall max age) :from people]\n```\n\nWith `glob` and `like` SQL operators keep in mind that they're\nmatching the *printed* representations of these values, even if the\nvalue is a string.\n\nThe `||` concatenation operator is unsupported because concatenating\nprinted representations breaks an important constraint: all values must\nremain readable within SQLite.\n\n## Quoting\n\nInside expressions, EmacSQL cannot tell the difference between symbol\nliterals and column references. If you're talking about the symbol\nitself, just quote it as you would in normal Elisp. Note that this\ndoes not \"escape\" `$tn` parameter symbols.\n\n```el\n(emacsql db [... :where (= category 'hiking)])\n```\n\nQuoting a string makes EmacSQL handle it as a \"raw string.\" These raw\nstrings are not printed when being assembled into a query. These are\nintended for use in special circumstances like filenames (`ATTACH`) or\npattern matching (`LIKE`). It is vital that raw strings are not\nreturned as results.\n\n```el\n(emacsql db [... :where (like name '\"%foo%\")])\n(emacsql db [:attach '\"/path/to/foo.db\" :as foo])\n```\n\nSince template parameters include their type they never need to be\nquoted.\n\n## Prepared Statements\n\nThe database is interacted with via prepared SQL s-expression\nstatements. You shouldn't normally be concatenating strings on your\nown. (And it leaves out any possibility of a SQL injection!) See the\n\"Usage\" section above for examples. A statement is a vector of\nkeywords and other lisp object.\n\nPrepared EmacSQL s-expression statements are compiled into SQL\nstatements. The statement compiler is memorized so that using the same\nstatement multiple times is fast. To assist in this, the statement can\nact as a template -- using `$i1`, `$s2`, etc. -- working like the\nElisp `format` function.\n\n### Compilation Rules\n\nRather than the typical uppercase SQL keywords, keywords in a prepared\nEmacSQL statement are literally just that: lisp keywords. EmacSQL only\nunderstands a very small amount of SQL's syntax. The compiler follows\nsome simple rules to convert an s-expression into SQL.\n\n#### All prepared statements are vectors.\n\nA prepared s-expression statement is a vector beginning with a keyword\nfollowed by a series of keywords and special values. This includes\nmost kinds of sub-queries.\n\n```el\n[:select ... :from ...]\n[:select tag :from tags\n :where (in tag [:select ...])]\n```\n\n#### Keywords are split and capitalized.\n\nDashes are converted into spaces and the keyword gets capitalized. For\nexample, `:if-not-exists` becomes `IF NOT EXISTS`. How you choose to\ncombine keywords is up to your personal taste (e.g., `:drop :table` vs.\n`:drop-table`).\n\n#### Standalone symbols are identifiers.\n\nEmacSQL doesn't know what symbols refer to identifiers and what\nsymbols should be treated as values. Use quotes to mark a symbol as a\nvalue. For example, `people` here will be treated as an identifier.\n\n```el\n[:insert-into people :values ...]\n```\n\n#### Row-oriented information is always represented as vectors.\n\nThis includes rows being inserted, and sets of columns in a query. If\nyou're talking about a row-like thing then put it in a vector.\n\n```el\n[:select [id name] :from people]\n```\n\nNote that `*` is actually a SQL keyword, so don't put it in a vector.\n\n```el\n[:select * :from ...]\n```\n\n#### Lists are treated as expressions.\n\nThis is true even within row-oriented vectors.\n\n```el\n[... :where (= name \"Bob\")]\n[:select [(/ seconds 60) count] :from ...]\n```\n\nSome things that are traditionally keywords -- particularly those that\nare mixed in with expressions -- have been converted into operators\n(`AS`, `ASC`, `DESC`).\n\n```el\n[... :order-by [(asc b), (desc a)]]   ; \"ORDER BY b ASC, a DESC\"\n[:select p:name :from (as people p)]  ; \"SELECT p.name FROM people AS p\"\n```\n\n#### The `:values` keyword is special.\n\nWhat follows `:values` is always treated like a vector or list of\nvectors. Normally this sort of thing would appear to be a column\nreference.\n\n```el\n[... :values [1 2 3]]\n[... :values ([1 2 3] [4 5 6])]  ; insert multiple rows\n```\n\n#### A list whose first element is a vector is a table schema.\n\nThis is to distinguish schemata from everything else. With the\nexception of what follows `:values`, nothing else is shaped like this.\n\n```el\n[:create-table people ([(id :primary-key) name])]\n```\n\n### Templates\n\nTo make statement compilation faster, and to avoid making you build up\nstatements dynamically, you can insert `$tn` parameters in place of\nidentifiers and values. These refer to the argument's type and its\nargument position after the statement in the `emacsql` function,\none-indexed.\n\n```el\n(emacsql db [:select * :from $i1 :where (\u003e salary $s2)] 'employees 50000)\n\n(emacsql db [:select * :from employees :where (like name $r1)] \"%Smith%\")\n```\n\nThe letter before the number is the type.\n\n * `i` : identifier\n * `s` : scalar\n * `v` : vector (or multiple vectors)\n * `r` : raw, unprinted strings\n * `S` : schema\n\nWhen combined with `:values`, the vector type can refer to lists of\nrows.\n\n```el\n(emacsql db [:insert-into favorite-characters :values $v1]\n            '([0 \"Calvin\"] [1 \"Hobbes\"] [3 \"Susie\"]))\n```\n\nThis is why rows must be vectors and not lists.\n\n### Ignored Features\n\nEmacSQL doesn't cover all of SQLite's features. Here are a list of\nthings that aren't supported, and probably will never be.\n\n * Collating. SQLite has three built-in collation functions: BINARY\n   (default), NOCASE, and RTRIM. EmacSQL values never have right-hand\n   whitespace, so RTRIM won't be of any use. NOCASE is broken\n   (ASCII-only) and there's little reason to use it.\n\n * Text manipulation functions. Like collating this is incompatible\n   with EmacSQL s-expression storage.\n\n * Date and time. These are incompatible with the printed values\n   stored by EmacSQL and therefore have little use.\n\n## Limitations\n\nEmacSQL is *not* intended to play well with other programs accessing\nthe SQLite database. Non-numeric values are stored encoded as\ns-expressions TEXT values. This avoids ambiguities in parsing output\nfrom the command line and allows for storage of Emacs richer data\ntypes. This is an efficient, ACID-compliant database specifically for\nEmacs.\n\n## Emacs Lisp Indentation Annoyance\n\nBy default, `emacs-lisp-mode` indents vectors as if they were regular\nfunction calls.\n\n```el\n;; Ugly indentation!\n(emacsql db [:select *\n                     :from people\n                     :where (\u003e age 60)])\n```\n\nCalling the function `emacsql-fix-vector-indentation` (interactive)\nadvises the major mode to fix this annoyance.\n\n```el\n;; Such indent!\n(emacsql db [:select *\n             :from people\n             :where (\u003e age 60)])\n```\n\n## Contributing and Extending\n\nTo run the test suite, clone the `pg` and `sqlite3` packages into\nsibling directories. The Makefile will automatically put these paths on\nthe Emacs load path (override `LDFLAGS` if your situation is different).\n\n```shell\ngit clone https://github.com/emarsden/pg-el ../pg\ngit clone https://github.com/pekingduck/emacs-sqlite3-api ../sqlite3\n```\n\nOr set `LOAD_PATH` to point at these packages elsewhere:\n\n```shell\nmake LOAD_PATH='-L path/to/pg -L path/to/sqlite3'\n```\n\nThen invoke make:\n\n```shell\nmake test\n```\n\nIf the environment variable `PGDATABASE` is present then the unit\ntests will also be run with PostgreSQL (emacsql-psql). Provide\n`PGHOST`, `PGPORT`, and `PGUSER` if needed. If `PGUSER` is provided,\nthe pg.el back-end (emacsql-pg) will also be tested.\n\nIf the environment variable `MYSQL_DBNAME` is present then the unit\ntests will also be run with MySQL in the named database. Note that\nthis is not an official MySQL variable, just something made up for\nEmacSQL.\n\n### Creating a New Front-end\n\nEmacSQL uses EIEIO so that interactions with a connection occur\nthrough generic functions. You need to define a new class that\ninherits from `emacsql-connection`.\n\n * Implement `emacsql-send-message`, `emacsql-waiting-p`,\n   `emacsql-parse`, and `emacsql-close`.\n * Provide a constructor that initializes the connection and calls\n   `emacsql-register` (for automatic connection cleanup).\n * Provide `emacsql-types` if needed (hint: use a class-allocated slot).\n * Ensure that you properly read NULL as nil (hint: ask your back-end\n   to print it that way).\n * Register all reserved words with `emacsql-register-reserved`.\n * Preferably provide `emacsql-reconnect` if possible.\n * Set the default isolation level to *serializable*.\n * Enable autocommit mode by default.\n * Prefer ANSI syntax (value escapes, identifier escapes, etc.).\n * Enable foreign key constraints by default.\n\nThe goal of the autocommit, isolation, parsing, and foreign key\nconfiguration settings is to normalize the interface as much as\npossible. The connection's user should have the option to be agnostic\nabout which back-end is actually in use.\n\nThe provided implementations should serve as useful examples. If your\nback-end outputs data in a clean, standard way you may be able to use\nthe emacsql-protocol-mixin class to do most of the work.\n\n## See Also\n\n * [SQLite Documentation](https://www.sqlite.org/docs.html)\n\n[readable]: http://nullprogram.com/blog/2013/12/30/#almost_everything_prints_readably\n[mistake]: https://github.com/magit/emacsql/issues/35#issuecomment-346352439\n\n\u003c!-- LocalWords: EIEIO Elisp EmacSQL MELPA Makefile NOCASE RTRIM  --\u003e\n\u003c!-- LocalWords: SQL's autocommit el emacsql unprinted whitespace --\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmagit%2Femacsql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmagit%2Femacsql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmagit%2Femacsql/lists"}