Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ use dot notiation with extended tbl object #92

Merged
merged 16 commits into from
Aug 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 16 additions & 29 deletions doc/sql.txt
Original file line number Diff line number Diff line change
Expand Up @@ -394,32 +394,32 @@ SQLTable *SQLTable*
{db} (SQLDatabase) database in which the tbl is part of.


SQLTableOpts *SQLTableOpts*
Supported sql.table configurations.

Fields: ~
{schema} (table) <string, string>
tbl:new({db}, {name}, {schema}) *tbl:new()*
Create new sql table object


SQLTableExt *SQLTableExt*
SQLTable
Parameters: ~
{db} (SQLDatabase)
{name} (string) table name
{schema} (table) table schema

Fields: ~
{tbl} (SQLTable) fallback when the user overwrite @SQLTableExt
methods.
Return: ~
SQLTable


tbl:new({db}, {name}, {opts}) *tbl:new()*
Create new sql table object
tbl:extend({db}, {name}, {schema}) *tbl:extend()*
Extend Sqlite Table Object. if first argument is {name} then second should
be {schema}. If no {db} is provided, the tbl object won't be initialized
until tbl.set_db is called


Parameters: ~
{db} (SQLDatabase)
{name} (string) table name
{opts} (SQLTableOpts)
{db} (SQLDatabase)
{name} (string)
{schema} (table)

Return: ~
SQLTable
SQLTableExt


tbl:schema({schema}) *tbl:schema()*
Expand Down Expand Up @@ -652,18 +652,5 @@ tbl:replace({rows}) *tbl:replace()*
|DB:insert()|


tbl:extend({db}, {name}, {opts}) *tbl:extend()*
Extend Sqlite Table Object.


Parameters: ~
{db} (SQLDatabase)
{name} (string)
{opts} (table)

Return: ~
SQLTableExt



vim:tw=78:ts=8:ft=help:norl:
6 changes: 6 additions & 0 deletions lua/sql/assert.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ local errors = {
eval_fail = "eval has failed to execute statement, ERRMSG: %s",
failed_ops = "operation failed, ERRMSG: %s",
missing_req_key = "(insert) missing a required key: %s",
missing_db_object = "'%s' db object is not set. please set it with `tbl.set_db(db)` and try again.",
}

for key, value in pairs(errors) do
Expand Down Expand Up @@ -59,4 +60,9 @@ M.missing_req_key = function(val, key)
return false
end

M.should_have_db_object = function(db, name)
assert(db ~= nil, errors.missing_db_object:format(name))
return false
end

return M
72 changes: 58 additions & 14 deletions lua/sql/parser.lua
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,60 @@ local format_action = function(value, update)
return stmt .. preappend .. value
end

local opts_to_str = function(tbl)
local f = {
pk = function()
return "primary key"
end,
type = function(v)
return v
end,
unique = function()
return "unique"
end,
nullable = function()
return "not null"
end,
default = function(v)
return "default " .. v
end,
reference = function(v)
return ("references %s"):format(v:gsub("%.", "(") .. ")")
end,
on_update = function(v)
return format_action(v, true)
end,
on_delete = function(v)
return format_action(v)
end,
}

local check = function(type)
if tbl[type] then
tbl[#tbl + 1] = f[type](tbl[type])
tbl[type] = nil
end
end

check "type"
check "unique"
check "nullable"
check "pk"
check "default"
check "reference"
check "on_update"
check "on_detach"

for k, v in pairs(tbl) do
if type(k) ~= "number" then
tbl[k] = nil
tbl[#tbl + 1] = f[k](v)
end
end

return tconcat(tbl, " ")
end

---Parse table create statement
---@param tbl string: table name
---@param defs table: keys and type pairs
Expand All @@ -338,25 +392,15 @@ M.create = function(tbl, defs)
defs.ensure = nil

for k, v in u.opairs(defs) do
if type(v) == "boolean" then
local t = type(v)
if t == "boolean" then
tinsert(items, k .. " integer not null primary key")
elseif type(v) ~= "table" then
elseif t ~= "table" then
tinsert(items, string.format("%s %s", k, v))
else
local _
_ = u.if_nil(v.type, nil) and tinsert(v, v.type)
_ = u.if_nil(v.unique, false) and tinsert(v, "unique")
_ = u.if_nil(v.nullable, nil) == false and tinsert(v, "not null")
_ = u.if_nil(v.pk, nil) and tinsert(v, "primary key")
_ = u.if_nil(v.default, nil) and tinsert(v, "default " .. v.default)
_ = u.if_nil(v.reference, nil) and tinsert(v, ("references %s"):format(v.reference:gsub("%.", "(") .. ")"))
_ = u.if_nil(v.on_update, nil) and tinsert(v, format_action(v.on_update, true))
_ = u.if_nil(v.on_delete, nil) and tinsert(v, format_action(v.on_delete))

tinsert(items, ("%s %s"):format(k, tconcat(v, " ")))
tinsert(items, ("%s %s"):format(k, opts_to_str(v)))
end
end

return ("create table %s(%s)"):format(tbl, tconcat(items, ", "))
end

Expand Down
122 changes: 75 additions & 47 deletions lua/sql/table.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,54 +3,91 @@
---@brief ]]
---@tag table.lua
local u = require "sql.utils"
local a = require "sql.assert"
local luv = require "luv"

---@class SQLTable @Main table class
---@field db SQLDatabase: database in which the tbl is part of.
local tbl = {}
tbl.__index = tbl

---@class SQLTableOpts @Supported sql.table configurations.
---@field schema table: <string, string>
local run = function(func, o)
a.should_have_db_object(o.db, o.name)
local exec = function()
if o.tbl_exists == nil then
o.tbl_exists = o.db:exists(o.name)
local stat = o.db.uri and luv.fs_stat(o.db.uri) or nil
o.mtime = stat and stat.mtime.sec or nil
local countsmt = "select count(*) from " .. o.name
o.has_content = o.tbl_exists and o.db:eval(countsmt)[1]["count(*)"] ~= 0 or 0
end

local run = function(func, self)
local _run = function()
return func() -- shoud pass tbl name?
if o.tbl_schema and next(o.tbl_schema) ~= nil and o.tbl_exists == false then
o.tbl_schema.ensure = u.if_nil(o.tbl_schema.ensure, true)
if not o.db.create then
error(vim.inspect(o.db))
end
o.db:create(o.name, o.tbl_schema)
end

return func()
end

if self.db.closed then
return self.db:with_open(_run)
if o.db.closed then
return o.db:with_open(exec)
end
return _run()
return exec()
end

---Create new sql table object
---@param db SQLDatabase
---@param name string: table name
---@param opts SQLTableOpts
---@param schema table: table schema
---@return SQLTable
function tbl:new(db, name, opts)
opts = opts or {}

local stat = luv.fs_stat(db.uri)
local schema = u.if_nil(opts.schema, opts)
function tbl:new(db, name, schema)
schema = schema or {}
local o = setmetatable({ db = db, name = name, tbl_schema = u.if_nil(schema.schema, schema) }, self)
if db then
run(function() end, o)
end
return o
end

local o = setmetatable({
db = db,
name = name,
mtime = stat and stat.mtime.sec,
tbl_schema = schema,
}, self)
---Extend Sqlite Table Object. if first argument is {name} then second should be {schema}.
---If no {db} is provided, the tbl object won't be initialized until tbl.set_db
---is called
---@param db SQLDatabase
---@param name string
---@param schema table
---@return SQLTableExt
function tbl:extend(db, name, schema)
if not schema and type(db) == "string" then
name, db, schema = db, nil, name
end

run(function()
o.tbl_exists = o.db:exists(o.name)
o.has_content = o.tbl_exists and o:count() ~= 0 or false
if o.tbl_schema and next(o.tbl_schema) ~= nil then
o.tbl_schema.ensure = u.if_nil(o.tbl_schema.ensure, true)
o:schema(o.tbl_schema)
end
end, o)
return o
local t = tbl:new(db, name, { schema = schema })
return setmetatable({
set_db = function(o)
if not o then
error(vim.inspect(db))
end
t.db = o
end,
}, {
__index = function(_, key, ...)
return type(t[key]) == "function" and function(...)
return t[key](t, ...)
end or t[key]
end,
__newindex = function(_, key, val)
if type(val) == "function" then
t["_" .. key] = t[key]
t[key] = val
else
t[key] = val
end
end,
})
end

---Create or change table schema. If no {schema} is given,
Expand All @@ -65,7 +102,6 @@ function tbl:schema(schema)
local res
return run(function()
local exists = self.db:exists(self.name)

if not schema then -- TODO: or table is empty
if exists then
self.tbl_schema = self.db:schema(self.name)
Expand Down Expand Up @@ -235,11 +271,11 @@ function tbl:sort(query, transform, comp)
return r[transform]
end
end
comp = comp or function(a, b)
return a < b
comp = comp or function(_a, _b)
return _a < _b
end
table.sort(res, function(a, b)
return comp(f(a), f(b))
table.sort(res, function(_a, _b)
return comp(f(_a), f(_b))
end)
return res
end, self)
Expand Down Expand Up @@ -298,17 +334,9 @@ function tbl:replace(rows)
end, self)
end

---@class SQLTableExt:SQLTable
---@field tbl SQLTable: fallback when the user overwrite @SQLTableExt methods.

---Extend Sqlite Table Object.
---@param db SQLDatabase
---@param name string
---@param opts table
---@return SQLTableExt
function tbl:extend(db, name, schema)
local t = self:new(db, name, { schema = schema })
return setmetatable({ tbl = t }, { __index = t })
end

tbl = setmetatable(tbl, { __call = tbl.extend })
-- local db = require("sql").new "/tmp/dbfds.sql"
-- local t = tbl:extend("fatable", { id = true, name = "text" })
-- t.set_db(db)
-- print(vim.inspect(t.db))
return tbl
Loading