GithubHelp home page GithubHelp logo

kkharji / sqlite.lua Goto Github PK

View Code? Open in Web Editor NEW
466.0 6.0 25.0 701 KB

SQLite LuaJIT binding with a very simple api.

License: MIT License

Lua 99.29% Makefile 0.31% Nix 0.36% Vim Script 0.04%
sqlite neovim luajit database luarocks

sqlite.lua's Introduction

sqlite.lua ๐Ÿ’ซ

SQLite/LuaJIT binding and a highly opinionated wrapper for storing, retrieving, caching, and persisting SQLite databases. sqlite.lua present new possibilities for plugin development and while it's primarily created for neovim, it support all luajit environments.

preview

โœจ Features:

  • Connect, reconnect, close sql db connections sqlite:open/sql:close
  • Evaluate any sqlite statement and return result if any sqlite:eval
  • Helper function over sqlite:eval to do all sort of operation.
  • High level API with sqlite.tbl for better experience.
  • lua tables deserialization/serialization (in helper functions and high level api)
  • 90% test coverage.
  • Up-to-date docs and changelog

๐Ÿšง Installation

Packer.nvim (Neovim)

use { "kkharji/sqlite.lua" }

luarocks (LuaJIT)

luarocks install sqlite luv

Ensure you have sqlite3 installed locally. (if you are on mac it might be installed already)

Windows

Download precompiled and set let g:sqlite_clib_path = path/to/sqlite3.dll (note: /)

Linux

sudo pacman -S sqlite # Arch
sudo apt-get install sqlite3 libsqlite3-dev # Ubuntu
sudo dnf install sqlite sqlite-devel # Fedora

Nix (home-manager)

programs.neovim.plugins = [
    {
      plugin = pkgs.vimPlugins.sqlite-lua;
      config = "let g:sqlite_clib_path = '${pkgs.sqlite.out}/lib/libsqlite3.so'";
    }
];

Notes:

  • Ensure you install pkgs.sqlite
  • If you are using home-manager on OSX, you must replace libsqlite3.so with libsqlite3.dylib

๐Ÿ”ฅ Powered by sqlite.lua

sqlite.lua's People

Contributors

abosnjakovic avatar akinsho avatar cassin01 avatar conni2461 avatar ditsuke avatar github-actions[bot] avatar holdenlucas avatar iceshuttle avatar jasselin avatar jlesquembre avatar kkharji avatar kurotych avatar lrangell avatar muniter avatar ndavd avatar nilsvreman avatar pierremacherel avatar tami5 avatar teto avatar weissle avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

sqlite.lua's Issues

configuring sqlite pargma results in db lock error

The current way by which pargma or sqlite database configuration get set, is causing the following error msg

sql.nvim: couldn't parse sql statement, ERRMSG: database is locked.

Perhaps using __wrap_stmts would fix this ...

The current way:

    if self.sqlite_opts then
      for k,v in pairs(self.sqlite_opts) do
        if not u.valid_pargma_key(k) then
          return error("sql.nvim: " .. k .. " is not a valid pragma")
        end
        clib.exec(self.conn, string.format("pragma %s = %s", k, v), nil, nil, nil)
      end
    end

should all db/sql method ensure valid connection?

Should all db methods ensure like sql.with_open that the db connection is open? and should we introuduce additional parameter that if provided the connection should be closed after this method is executed?

(extend) support adding custom table name

support setting a different table name than the provided key. e.g.

require('sql') {
  init = true,
  fs = {
      _name = "filesystem_table"
  }
}
fs:insert {...} -- will insert into table named "filesystem_table" and not "fs"

better support for boolean and lua tables

Currently sql.nvim uses the following for interoping lua boolean to 0/1.

-- lua/sql.lua
local booleansql = function(value)
 if type(value) == "boolean" then
   value = (value == true) and 1 or 0
 end
 return value
end

However there are no support for interop 0/1 back to lua boolean. This aspect
should be handled in stmt module with the above code removed from sql:eval.

sql:insert and table:insert should not require the primary key, instead should return it.

Hello just testing the project, looks great just have some comments on the insert behavior.
stmt:step returns SQLITE_ROW after an insert from where the id of the inserted row is retrieved, there's also last_insert_row_id. Therefore the primary key of a table shouldn't be required when inserting.

Each entry in most SQLite tables (except for WITHOUT ROWID tables) has a unique 64-bit signed integer key called the "rowid". The rowid is always available as an undeclared column named ROWID, OID, or ROWID as long as those names are not also used by explicitly declared columns. If the table has a column of type INTEGER PRIMARY KEY then that column is another alias for the rowid.

(extend) Merge table function and set warnings

When creating un initialized db, table functions defined before initialization get removed. Additionally, table fields under db would be nil when trying to extend them or add function to them.

Thus, an empty table should be in place and at initialization merge the result of table:extend with what has been already defined.

Note: This maybe tricky with __new_index and __index setup of sql.table. I may need to modify the function to add fields to self instead of sql.table

foregin keys support & tests

schema = { ..., foreign_keys = { client = "projects.id", } } == "KEY(client) REFERENCES projects(id)""

In addition to auto enable.

auto alter table schema

It's really annoying to have to call table drop or keep track of schema.ensure. In nvim-telescope/telescope-frecency.nvim#35, I was trying to debug and understand why count field keep being nil, even though the schema has default=0. It turns out the calling files.schema() returned an old schema, which means that the new schema has not effect on pre-existing table.

I'd like to compare the sql table schema and user provided schema and compare
between them, and if they are different. I'd like table module to auto alter.

The full table schema information as it exists in C side can be found with db:schema("files", true), it output something like that

{
  def = {},
  info = {
    count = { cid = 0, primary = false, required = false, type = "INTEGER" },
    id = { cid = 1, primary = true, required = false, type = "INTEGER" },
    path = { cid = 2, primary = false, required = false, type = "TEXT" }
  },
  req = {},
  types = { count = "INTEGER", id = "INTEGER", path = "TEXT" }
}

In telescope-frecency case, I wanted it to change to the following:

{
  def = { count = "0" },
  info = {
    count = { cid = 0, default = "0", primary = false, required = false, type = "integer" },
    id = { cid = 1, primary = true, required = true, type = "integer" },
    path = { cid = 2, primary = false, required = false, type = "string" }
  },
  req = {},
  types = { count = "integer", id = "integer", path = "string" }
}

Add auto changelog

Use github actions scheduled jobs to auto generate CHANGELOG.md once every two days.

libsqlite3.so fails to load on nix/guix (workaround)

This probably isn't pertinent to most people, but I'm posting this in case others run into this issue. libz will fail to load before libsqlite3.so is loaded, so you get an error message about libsqlite3 when it's really the fault of libz.

I'm going to try to figure out how to deal with this on the nix packaging side. The following is for neovim installed on a linux system (not nixos), but it should work with the store path's substituted for the appropriate libraries.

diff --git a/lua/sql/defs.lua b/lua/sql/defs.lua
index edc260a..31de4ef 100644
--- a/lua/sql/defs.lua
+++ b/lua/sql/defs.lua
@@ -3,13 +3,8 @@ local bit = require'bit'
 
 local M = {}
 
-local clib_path = vim.g.sql_clib_path or (function()
-  if vim.loop.os_uname().sysname == 'Darwin' then
-    return '/usr/local/opt/sqlite3/lib/libsqlite3.dylib'
-  end
-  return 'libsqlite3'
-end)()
-local clib = ffi.load(clib_path)
+ffi.load('/usr/lib64/libz.so')
+local clib = ffi.load('/usr/lib64/libsqlite3.so')
 
 -- Constants
 M.flags = {

tables should have methods of their own

Data and sql tables might be eaiser to deal if they can have a set of method.

Policies:

  • Connection is opend or not shouldn't be an issue. Additionally, if one of the functions gets called with closed connection then the connection will be opend and closed after the operations is done.
  • Results should be cached with the the query and deleted on insert/update/delete operations.

Specifications

sql:table

Initialize the sql table object with:

  • exists,
  • has_content,
  • schema,
  • last_action,
  • last_insert_id, -- hmmm I wonder if thats even important
  • cache (when for some reason we had to call for all the rows)
local t = db:table("todos")

t:schema

Create or change schema of the table. If the schema doesn't have ensure key, then the table will be droped and created with the new schema. Unless, the table already has data, then the it should error out.

t:schema{
  id = {"integer", "not", "null"},
  name = "text",
  age = "integer"
}

t:add

same functionalities as sql.insert

t:add{ 
  id = 1, 
  name = "a" 
}

t:add{ 
  {id = 1, name = "b" }, 
  {id = 3, name = "c", age = 19} 
}

t:remove

same functionalities as sql.delete. if no arguments is provided then it should remove all rows.

t:remove() -- remove everything
t:remove{ id = 1, name = "a" }
t:remove{ id = {1,2,3,4}, name = "a" }
t:remove{ contains = { name = "tam" }}

t:replace

same functionalities as sql.insert, but delete the content of table and replaced with the content.

t:replace{
  -- rows
}

t:get

same functionalities sql:select.

t:get{
  keys = { "name" },
  where = { 
    id = 1 
    name = function() return "do stuff and return a string" end
  },
  contains = { name = "ab" },
  -- join
}

t:update

same functionalities as sql.update

t:update{
  where = { .. }
  values = { .. },
}

t:update{
  {where = { .. } values = { .. }},
  {where = { .. } values = { .. }},
  {where = { .. } values = { .. }},
}

t:drop

drop the table if it exists and delete content if any. Else do nothing.

t:drop()

t:empty

returns not t.has_content

t:empty()

t:exists

returns t.exists

t:exists()

t:each

Iterates over a table rows of with a function that get execute on each row.

t:each({
  where = { .. }
  contains = { .. },
}, function(row) 
  -- execute stuff
end)

t:map

produce a new table by a function. uses t:each.

local modified = t:map({
  where = { .. }
  contains = { .. },
}, function(row) 
  -- execute stuff
  -- return modified a new row or nothing
end)

t:count`:

Returns the size or count of rows, used to set has_content

t:sort`:

Returns sorted rows based on comparison function, if not function
is provided and key is then it sorts by that key value

cc @Conni2461

(extend) accept pre-made tables

With #92 merged. Users can define independent tables then inject db in them. Thus, db:extend need to accept pre-made table and call set_db on them.

:insert{table, table} doesn't work as expected.

Reproduce:

local table1 = { ... }
local table2 = { ... }
local db = sql.open(path)
db:insert{ table1, table2 }
assert.are.same("table", type(db:eval("select * from table1")))

After doing the insertation indvidiually, It errored out that one of the table
had undefined field in the sqlite table schema. However, after fixing that and
trying to re-run insert{}, same issue.

Expected

  • It errors out as it would with indvidiual insertation.
  • It insert multiple row in multiple tables successfully.

Suggestions

  • Check if all table fields are vaild fields based on the table schema.

sql:insert doesn't respect default values

When a schema defines default values, the current implementation doesn't respect that and somehow endup inserting null in those fields instead.

Example:

create table if not exists test(
  id integer primary key,
  title text not null,
  created datetime default current_timestamp,
)

sqlite function in insert break other values

Apparently the following parsed statement is not good enough to pass:

insert into timestamps (fid, date) values(:fid, julianday('now'))"

it does insert the date, but sadly ignore fid.

๐Ÿค” not sure how this would be solved ๐Ÿ˜ญ

function DB:insert(tbl_name, rows)
  --- ...
  clib.wrap_stmts(self.conn, function()
    for _, v in ipairs(items) do
      local s = stmt:parse(self.conn, P.insert(tbl_name, { values = v }))
      --> P.insert(....) => "insert into timestamps (fid, date) values(:fid, julianday('now'))"
      --> stmt:parse(..) => "insert into timestamps (fid, date) values(NULL, julianday('now'))"
      s:bind(v)
      s:step()
      s:bind_clear()
      table.insert(ret_vals, s:finalize())
    end
  end)
end

On the parser side, instead of binding to :date it uses the value in place.

local pvalues = function(defs, kv)
  --- ....
  local keys = {}
  for k, v in u.opairs(defs) do
    if type(v) == "string" and v:match "%a+%(.+%)" then
      tinsert(keys, v)
    else
      tinsert(keys, ":" .. k)
    end
  end

  return ("values(%s)"):format(tconcat(keys, ", "))
end

try uv before vim.loop

There are few places where we use vim.loop, we should have pacall to uv before loop. Also if we can't replace vim.fn.fnamemodify in utill then we should just remove since it will be rarely where plugin developers try to load db from current path.

fix parser.lua failing test #56

#56

Fail	||  parse [create] ensure that the table is created with a key being true	
            parser_spec.lua:126: should be identical
            Expected objects to be the same.
            Passed in:
            (string) 'create table if not exists people(age int, id integer not null primary key, name text)'
            Expected:
            (string) 'create table if not exists people(age int, id integer primary key, name text)'
            
            stack traceback:
               parser_spec.lua:117>

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.