{"id":21588790,"url":"https://github.com/jolby/cl-sql-utils","last_synced_at":"2025-03-18T09:42:13.048Z","repository":{"id":262767834,"uuid":"888277946","full_name":"jolby/cl-sql-utils","owner":"jolby","description":"An attempt to port Simon Willison's fantastic sqlite-utils to common lisp","archived":false,"fork":false,"pushed_at":"2024-11-23T21:07:36.000Z","size":94,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-24T16:12:11.431Z","etag":null,"topics":["common-lisp","sql","sqlite","sqlite-utils"],"latest_commit_sha":null,"homepage":"","language":"Common Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jolby.png","metadata":{"files":{"readme":"README.org","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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}},"created_at":"2024-11-14T05:46:59.000Z","updated_at":"2024-11-23T21:07:39.000Z","dependencies_parsed_at":"2024-11-14T07:32:41.427Z","dependency_job_id":null,"html_url":"https://github.com/jolby/cl-sql-utils","commit_stats":null,"previous_names":["jolby/cl-sql-utils"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jolby%2Fcl-sql-utils","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jolby%2Fcl-sql-utils/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jolby%2Fcl-sql-utils/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jolby%2Fcl-sql-utils/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jolby","download_url":"https://codeload.github.com/jolby/cl-sql-utils/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244197651,"owners_count":20414437,"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":["common-lisp","sql","sqlite","sqlite-utils"],"created_at":"2024-11-24T16:11:12.292Z","updated_at":"2025-03-18T09:42:13.017Z","avatar_url":"https://github.com/jolby.png","language":"Common Lisp","funding_links":[],"categories":[],"sub_categories":[],"readme":"#+TITLE: cl-sql-utils\n#+AUTHOR: Joel Boehland\n#+DATE: 2024\n\n* cl-sql-utils - A Common Lisp SQL Database Utility Library\n\ncl-sql-utils is a Common Lisp library providing a high-level interface for\nworking with SQL databases, with initial support for SQLite. It aims to provide\nan intuitive API similar to Python's sqlite-utils while embracing Common Lisp\nidioms.\n\n** Features\n\n- Simple database connection management\n- Table creation and modification\n- Foreign key support\n- Schema transformation capabilities\n- Row insertion and querying\n- (PLANNED) Index management\n- (PLANNED) Transaction support\n- (IN PROGRESS) robust CLI interface\n\n** Installation\n\nThis project is not in quicklisp. You will need to checkout this project into your $HOME/quicklisp/local-projects dir. Then:\n\n#+begin_src lisp\n(ql:quickload :sql-utils)\n#+end_src\n\nRun the tests:\n#+begin_src sh\n# assumes the project is in your quicklisp local-projects dir\nsbcl --eval \"(ql:quickload '(:sql-utils :sql-utils/tests))\" --eval \"(5am:run! 'sql-utils-test.sql-utils-tests::sql-utils-suite)\" --eval \"(quit)\"\n#+end_src\n\n** API Quick Start\n\n#+begin_src lisp\n;; Create a new database file\n(defvar *db* (sql-utils:make-db-connection :sqlite :filename \"examples/test.db\"))\n\n;; Create a table with constraints\n(sql-utils:create-table *db* \"people\"\n                       '((\"id\" . \"INTEGER\")\n                         (\"name\" . \"TEXT\")\n                         (\"email\" . \"TEXT\")\n                         (\"age\" . \"INTEGER\"))\n                       :pk \"id\"\n                       :not-null '(\"name\" \"email\")\n                       :defaults '((\"age\" . \"0\")))\n\n;; Insert some data\n(sql-utils:insert (sql-utils:make-table *db* \"people\")\n                 '(:name \"Alice\" :email \"alice@example.com\" :age 30))\n\n;; Query the data\n(sql-utils:rows (sql-utils:make-table *db* \"people\"))\n#+end_src\n\n** API Documentation\n\n*** Database Operations\n\n- =make-db-connection= - Create a new database connection\n- =create-table= - Create a new table\n- =execute= - Execute raw SQL\n\n*** Table Operations\n\n- =transform= - Modify table schema\n- =add-column= - Add a new column\n- =add-foreign-key= - Add a foreign key constraint\n- =drop= - Drop/delete a table\n\n*** Query Operations\n\n- =lookup= - Find a record by primary key\n- =row-count= - Get total number of rows\n- =row-count-where= - Get total number of rows matching a condition\n- =rows= - Query rows from a table\n- =rows-where= - Query rows with conditions\n- =insert= - Insert a single record\n- =insert-all= - Insert multiple records\n- =delete-record= - Delete a single record\n- =delete-where= - Delete records matching a condition\n\n** API Examples\n\n*** Creating Tables with Foreign Keys\n\n#+begin_src lisp\n;; Create parent table\n(sql-utils:create-table *db* \"authors\"\n                       '((\"id\" . \"INTEGER\")\n                         (\"name\" . \"TEXT\"))\n                       :pk \"id\")\n\n;; Create child table with foreign key\n(sql-utils:create-table *db* \"books\"\n                       '((\"id\" . \"INTEGER\")\n                         (\"title\" . \"TEXT\")\n                         (\"author_id\" . \"INTEGER\"))\n                       :pk \"id\"\n                       :foreign-keys '((\"author_id\" \"authors\" \"id\")))\n#+end_src\n\n*** Transforming Tables\n\n#+begin_src lisp\n;; Add a column and change types\n(sql-utils:transform table\n                    :types '((\"age\" . \"TEXT\"))\n                    :add-column '((\"email\" . \"TEXT\")))\n#+end_src\n\n** CLI\nThe project includes a command-line interface for interacting with SQLite databases.\n\n*** Building the CLI binary executable\n\nTo build the CLI binary executable:\n\n#+begin_src sh\n# From within the root project directory:\nsbcl --load \"sql-utils.asd\" --eval \"(ql:quickload '(:sql-utils :sql-utils/sqlite-cli))\" --eval \"(dump-system-executable :sql-utils/sqlite-cli)\"\n#+end_src\n\nThis will create an executable at =bin/sql-utils=.\n\n\n*** Available Commands\n\n- =create-database= - Create a new SQLite database file\n  - =--enable-wal= - Enable Write-Ahead Logging mode\n\n- =create-table= - Create a new table with specified columns\n  - =--pk= - Specify primary key column(s)\n  - =--not-null= - Mark columns as NOT NULL\n  - =--default= - Set default values for columns\n  - =--fk= - Add foreign key constraints\n  - =--ignore= - Skip if table exists\n  - =--replace= - Replace existing table\n  - =--strict= - Apply STRICT mode\n\n- =drop= - Drop/delete a table\n  - =--ignore= - Skip if table doesn't exist\n\n- =tables= - List tables in the database\n  - =--fts4= - Show only FTS4 enabled tables\n  - =--fts5= - Show only FTS5 enabled tables  \n  - =--counts= - Include row counts\n  - =--columns= - Show column information\n  - =--schema= - Show table schemas\n\n- =rows= - Output rows from a table\n  - =-c/--column= - Select specific columns\n  - =--where= - Filter rows with WHERE clause\n  - =-o/--order= - Order results\n  - =--limit= - Limit number of rows\n  - =--offset= - Skip initial rows\n\n- =insert= - Insert records into a table\n  - =--pk= - Specify column(s) to use as primary key\n  - =--nl= - Read newline-delimited JSON\n\n- =delete-record= - Delete a single row by primary key value(s)\n\n- =delete-where= - Delete rows matching a WHERE clause\n  - =--where= - WHERE clause for deletion\n  - =--analyze= - Run ANALYZE after deletion\n\n\n*** Command Examples\n\nCreate a new database:\n#+begin_src sh\n$ bin/sql-utils create-database examples/test.db --enable-wal\n#+end_src\n\nCreate a table with constraints:\n#+begin_src sh :results replace\nbin/sql-utils create-table examples/test.db people \\\n  id integer \\\n  name text \\\n  email text \\\n  age integer \\\n  --pk id \\\n  --not-null name \\\n  --not-null email \\\n  --default \"age=0\"\n#+end_src\n\nInsert records:\n#+begin_src sh\n# Insert a single record\n$ echo '(:name \"Alice\" :email \"alice@example.com\" :age 30)' | \\\n  bin/sql-utils insert examples/test.db people\n\n$ echo '(:name \"FOO\")' | bin/sql-utils insert examples/test.db chickens -\n\n# Insert multiple records (list of plists)\n$ echo '((:name \"Bob\" :email \"bob@example.com\" :age 25)(:name \"Carol\" :email \"carol@example.com\" :age 35))' | bin/sql-utils insert examples/test.db people\n$ echo '((:id 82 :name \"BAZ\" :parent_id 44) (:id 67 :name \"BUB\" :parent_id 44))' | bin/sql-utils insert examples/test.db chickens -\n\n# Insert with primary key\n$ echo '(:id 1 :name \"BAR\")' | bin/sql-utils insert examples/test.db chickens --pk id -\n#+end_src\n\nQuery table information:\n#+begin_src sh\n$ bin/sql-utils tables examples/test.db\ntest_table\ntest_table_2\ntest_table_3\npeople\n\n# List all tables showing schema\n$ bin/sql-utils tables examples/test.db --schema\ntest_table\n  Schema: CREATE TABLE \"test_table\" (\n   [id] INTEGER,\n   [name] TEXT DEFAULT 'FOO',\n   [parent_id] INTEGER REFERENCES [test_table]([id])\n)\ntest_table_2\n  Schema: CREATE TABLE [test_table_2] (\n   [id] INTEGER,\n   [test_text] TEXT,\n   [blah] TEXT\n)\ntest_table_3\n  Schema: CREATE TABLE [test_table_3] (\n   [id] INTEGER,\n   [test_text] TEXT,\n   [created_at] TEXT DEFAULT CURRENT_TIMESTAMP\n)\npeople\n  Schema: CREATE TABLE \"people\" ([id] INTEGER, [name] TEXT, [email] TEXT, [age] INTEGER, PRIMARY KEY([id]))\n\n\n# Show table contents\n$ bin/sql-utils rows examples/test.db people\n(id 1 name Alice email alice@example.com age 30)\n(id 2 name Bob email bob@example.com age 25)\n(id 3 name Carol email carol@example.com age 35)\n\n# Filter and order results\n$ bin/sql-utils rows examples/test.db people \\\n  --where \"age \u003e 25\" \\\n  --order \"name DESC\" \\\n  --limit 10\n(id 3 name Carol email carol@example.com age 35)\n(id 1 name Alice email alice@example.com age 30)\n#+end_src\n\nDelete records:\n#+begin_src sh\n# Delete a single record by primary key\n$ bin/sql-utils delete-record examples/test.db people 1\n\n# Delete records matching a condition\n$ bin/sql-utils delete-where examples/test.db people --where \"age \u003c 18\"\n\n# Delete all records in a table\n$ bin/sql-utils delete-where examples/test.db people\n\n# Drop/delete an entire table\n$ bin/sql-utils drop examples/test.db old_table --ignore\n#+end_src\n\n** Contributing\n\nContributions are welcome! Please feel free to submit pull requests.\n\n1. Fork the repository\n2. Create your feature branch\n3. Commit your changes\n4. Push to the branch\n5. Create a Pull Request\n\n** License\n\nThis project is licensed under the MIT License - see the LICENSE file for details.\n\n** Acknowledgments\n\nThis project was heavily inspired by the Python sqlite-utils library by Simon Willison.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjolby%2Fcl-sql-utils","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjolby%2Fcl-sql-utils","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjolby%2Fcl-sql-utils/lists"}