Skip to content

Commit 1c88610

Browse files
authored
🐛 extended tables referencing mutable db object + other fixes (#100)
closes #101, #99 - `table.extend` reference db object instead of `db.extend`ed object. this fix issue with calling methods that has been already modified by the user. - remove debug stuff - modify `table.extend` mechanism. - stop mutating insert/update source data when processing for `sql.insert`
1 parent d744c0b commit 1c88610

5 files changed

Lines changed: 106 additions & 70 deletions

File tree

lua/sql.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ function DB:extend(opts)
9999
local name = schema._name and schema._name or tbl_name
100100
cls[tbl_name] = schema.set_db and schema or t:extend(name, schema)
101101
if not cls[tbl_name].db then
102-
cls[tbl_name].set_db(cls)
102+
cls[tbl_name].set_db(cls.db)
103103
end
104104
end
105105
end

lua/sql/parser.lua

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -418,17 +418,18 @@ end
418418
---@param schema table tbl schema with extra info
419419
---@return table pre processed rows
420420
M.pre_insert = function(rows, schema)
421+
local res = {}
421422
rows = u.is_nested(rows) and rows or { rows }
422-
for _, row in ipairs(rows) do
423+
for i, row in ipairs(rows) do
423424
u.foreach(schema.req, function(k)
424425
a.missing_req_key(row[k], k)
425426
end)
426-
u.foreach(row, function(k, v)
427+
res[i] = u.map(row, function(v, k)
427428
local is_json = schema.types[k] == "luatable" or schema.types[k] == "json"
428-
row[k] = is_json and json.encode(v) or M.sqlvalue(v)
429+
return is_json and json.encode(v) or M.sqlvalue(v)
429430
end)
430431
end
431-
return rows
432+
return res
432433
end
433434

434435
---Postprocess data queried from a sql db. for now it is mainly used

lua/sql/table.lua

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,6 @@ local run = function(func, o)
2424

2525
if o.tbl_schema and next(o.tbl_schema) ~= nil and o.tbl_exists == false then
2626
o.tbl_schema.ensure = u.if_nil(o.tbl_schema.ensure, true)
27-
if not o.db.create then
28-
error(vim.inspect(o.db))
29-
end
3027
o.db:create(o.name, o.tbl_schema)
3128
end
3229

@@ -65,26 +62,22 @@ function tbl:extend(db, name, schema)
6562
name, db, schema = db, nil, name
6663
end
6764

68-
local t = tbl:new(db, name, { schema = schema })
65+
local t = self:new(db, name, { schema = schema })
6966
return setmetatable({
7067
set_db = function(o)
71-
if not o then
72-
error(vim.inspect(db))
73-
end
7468
t.db = o
7569
end,
7670
}, {
77-
__index = function(_, key, ...)
78-
return type(t[key]) == "function" and function(...)
79-
return t[key](t, ...)
80-
end or t[key]
81-
end,
82-
__newindex = function(_, key, val)
83-
if type(val) == "function" then
84-
t["_" .. key] = t[key]
85-
t[key] = val
86-
else
87-
t[key] = val
71+
__index = function(o, key, ...)
72+
if type(key) == "string" then
73+
key = key:sub(1, 1) == "_" and key:sub(2, -1) or key
74+
if type(t[key]) == "function" then
75+
return function(...)
76+
return t[key](t, ...)
77+
end
78+
else
79+
return t[key]
80+
end
8881
end
8982
end,
9083
})
@@ -335,8 +328,5 @@ function tbl:replace(rows)
335328
end
336329

337330
tbl = setmetatable(tbl, { __call = tbl.extend })
338-
-- local db = require("sql").new "/tmp/dbfds.sql"
339-
-- local t = tbl:extend("fatable", { id = true, name = "text" })
340-
-- t.set_db(db)
341-
-- print(vim.inspect(t.db))
331+
342332
return tbl

test/auto/sql_spec.lua

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -772,8 +772,8 @@ describe("sql", function()
772772
end)
773773

774774
describe(":extend", function()
775-
local testrui = "/tmp/extend_db"
776-
local testrui2 = "/tmp/extend_db2"
775+
local testrui = "/tmp/extend_db_new"
776+
local testrui2 = "/tmp/extend_db_new5"
777777
local ok, manager
778778

779779
it("Initialize manager", function()
@@ -803,7 +803,7 @@ describe("sql", function()
803803
end)
804804

805805
it("process opts and set sql table objects", function()
806-
eq("/tmp/extend_db", manager.uri, "should set self.uri.")
806+
eq("/tmp/extend_db_new", manager.uri, "should set self.uri.")
807807
eq(true, manager.closed, "should construct without openning connection.")
808808
eq(nil, manager.conn, "should not set connection object.")
809809
eq("boolean", type(manager.sqlite_opts.foreign_keys), "should have added opts to sqlite_opts.")
@@ -833,24 +833,28 @@ describe("sql", function()
833833
},
834834
}
835835

836-
local id = manager.projects.insert(sqlnvim)
837-
838-
eq("cdata", type(manager.conn), "should set connection object.")
839-
eq(1, id, "should have returned id.")
836+
eq(1, manager.projects.insert(sqlnvim), "should insert and return id.")
837+
eq("cdata", type(manager.conn), "should set connection object after first call to sql api")
838+
eq("table", type(manager.projects.where({ id = 1 }).objectives), "should return as table.")
839+
eq("table", type(sqlnvim.objectives), "It shouldn't have mutated objectives table.")
840840
eq(true, manager.projects.remove(), "should remove after default insert.")
841841

842842
function manager.projects.insert()
843843
return manager.projects._insert(sqlnvim)
844844
end
845-
846-
local id = manager.projects.insert()
847-
eq(1, id, "should have returned id.")
848-
849845
function manager.projects.get()
850-
return manager.projects._get({ where = { title = sqlnvim.title } })[1].title
846+
return manager.projects._get({ where = { title = sqlnvim.title } })[1]
851847
end
848+
function manager.projects.remove_objectives()
849+
return manager.projects.update { where = { id = 1 }, set = { objectives = {} } }
850+
end
851+
852+
eq(1, manager.projects.insert(), "should have inserted and returned id.")
852853

853-
eq(sqlnvim.title, manager.projects.get(), "should have inserted sqlnvim project")
854+
eq(sqlnvim.title, manager.projects.get().title, "should have inserted sqlnvim project")
855+
eq(true, manager.projects.get().objectives ~= "")
856+
eq(true, manager.projects.remove_objectives(), "should succeed at updating")
857+
eq({}, manager.projects.get().objectives, "should return empty table")
854858
end)
855859

856860
it("set a different name for sql db table, with access using extend field", function()

test/auto/table_spec.lua

Lines changed: 71 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -30,36 +30,6 @@ end
3030
describe("table", function()
3131
local t1, t2 = seed()
3232

33-
describe(":extend", function()
34-
local t
35-
it("missing db object", function()
36-
---@type SQLTableExt
37-
t = tbl("tbl_name", { id = true, name = "text" })
38-
eq(false, pcall(t.insert, { name = "tami" }), "should fail early.")
39-
t.set_db(db)
40-
eq(true, pcall(t.insert, { name = "conni" }), "should work now we have a db object to operate against.")
41-
eq({ { id = 1, name = "conni" } }, t.get(), "only insert conni")
42-
end)
43-
44-
it("with db object", function()
45-
t = tbl(db, "tansactions", { id = true, amount = "real" })
46-
eq(1, t.insert { amount = 20.2 })
47-
eq(
48-
"20.2",
49-
t.map(function(row)
50-
return tostring(row.amount)
51-
end)[1]
52-
)
53-
end)
54-
55-
it("overwrite functions and fallback to t.db", function()
56-
t.get = function()
57-
return math.floor(t._get({ where = { id = 1 } })[1].amount)
58-
end
59-
eq(20, t.get())
60-
end)
61-
end)
62-
6333
describe(":new", function()
6434
it("create new object with sql.table methods.", function()
6535
eq("table", type(t1))
@@ -537,4 +507,75 @@ describe("table", function()
537507
eq(true, next(tracks:get { where = { artist = 100 } }) == nil, "shouldn't be any rows referencing Dean Martin")
538508
end)
539509
end)
510+
511+
describe(":extend", function()
512+
local t
513+
it("missing db object", function()
514+
---@type SQLTableExt
515+
t = tbl("tbl_name", { id = true, name = "text" })
516+
eq(false, pcall(t.insert, { name = "tami" }), "should fail early.")
517+
t.set_db(db)
518+
eq(true, pcall(t.insert, { name = "conni" }), "should work now we have a db object to operate against.")
519+
eq({ { id = 1, name = "conni" } }, t.get(), "only insert conni")
520+
end)
521+
522+
it("with db object", function()
523+
t = tbl(db, "tansactions", { id = true, amount = "real" })
524+
eq(1, t.insert { amount = 20.2 })
525+
eq(
526+
"20.2",
527+
t.map(function(row)
528+
return tostring(row.amount)
529+
end)[1]
530+
)
531+
end)
532+
533+
it("overwrite functions and fallback to t.db", function()
534+
t.get = function()
535+
return math.floor(t._get({ where = { id = 1 } })[1].amount)
536+
end
537+
eq(20, t.get())
538+
end)
539+
540+
local some
541+
542+
it("create a new table", function()
543+
some = tbl:extend(db, "somename", {
544+
name = "text",
545+
id = true,
546+
count = {
547+
type = "integer",
548+
default = 0,
549+
},
550+
})
551+
end)
552+
553+
it("access function", function()
554+
eq("function", type(some.get), "should we still have access for")
555+
eq("function", type(some._get), "should be hard coded.")
556+
eq("cdata", type(some.db.conn))
557+
end)
558+
559+
it("custom function", function()
560+
function some.insert_or_update(name)
561+
eq("string", type(name), "name should be a string")
562+
local entry = some.where { name = name }
563+
if not entry then
564+
some.insert { name = name }
565+
else
566+
eq("function", type(some.update))
567+
some.update {
568+
where = { id = entry.id },
569+
values = { count = entry.count + 2 },
570+
}
571+
end
572+
end
573+
-- eq({}, getmetatable(some))
574+
eq("function", type(some.insert_or_update), "should be registered as function")
575+
some.insert_or_update "ff"
576+
-- eq("ff", some.get({ name = "ff" }).name)
577+
some.insert_or_update "ff"
578+
-- eq(2, some.where({ name = "ff" }).count)
579+
end)
580+
end)
540581
end)

0 commit comments

Comments
 (0)