diff --git a/assets/.lua/schema.lua b/assets/.lua/schema.lua new file mode 100644 index 0000000..2f9b2dd --- /dev/null +++ b/assets/.lua/schema.lua @@ -0,0 +1,59 @@ +local csexp = require"csexp" +local sqlite = require"lsqlite3" + +local function sqlite_ok(errcode) + return errcode == sqlite.OK or errcode == sqlite.DONE +end + +local function quote_name(name) + return [["]]..string.gsub(name, [["]], [[""]])..[["]] +end + +local function setup(db) + db:create_function("parse_node_name", 1, function(ctx, table_name) + local obj = csexp.parse(table_name) + if type(obj) ~= "table" or obj[1] ~= "node" then + ctx:result_null() + return + end + local node_name = obj[2] + if not node_name then + ctx:result_null() + return + end + ctx:result_text(node_name) + end) +end + +local function new_node_type(db, name) + local table_name = csexp.serialize{"node", name} + local quoted_name = quote_name(table_name) + local query = string.format([[ + CREATE TABLE %s ( + id INTEGER PRIMARY KEY + ); + ]], quoted_name) + -- for _ in db:rows(query) do end + db:exec(query) + + if not sqlite_ok(db:errcode()) then + error(db:errmsg()) + end +end + +local function get_node_types(db) + local query = [[ + SELECT parse_node_name(name) + FROM sqlite_schema + WHERE parse_node_name(name) IS NOT NULL + ORDER BY parse_node_name(name) ASC; + ]] + return db:urows(query) +end + +return { + setup = setup, + quote_name = quote_name, + new_node_type = new_node_type, + get_node_types = get_node_types, +} \ No newline at end of file diff --git a/tests/main.lua b/tests/main.lua index 1424dca..dc97af1 100644 --- a/tests/main.lua +++ b/tests/main.lua @@ -3,6 +3,7 @@ lester = require "lester" lester.show_traceback = false require "test-csexp" +require "test-schema" lester.report() lester.exit() \ No newline at end of file diff --git a/tests/test-schema.lua b/tests/test-schema.lua new file mode 100644 index 0000000..eb1ee56 --- /dev/null +++ b/tests/test-schema.lua @@ -0,0 +1,47 @@ +local schema = require "schema" +local sqlite = require "lsqlite3" +local describe, it, expect = lester.describe, lester.it, lester.expect + +describe("schema", function() + local db + lester.before(function() + db = sqlite.open_memory() + db:exec("PRAGMA foreign_keys") + schema.setup(db) + end) + lester.after(function() + db:close() + db = nil + end) + + describe("new_node_type", function() + it("creates new node types", function() + local node_type_names = {"a", "b", "c", "d"} + for _, name in ipairs(node_type_names) do + schema.new_node_type(db, name) + end + local results = {} + for name in schema.get_node_types(db) do + table.insert(results, name) + end + expect.equal(node_type_names, results) + end) + + it("fails when creating a duplicate node type", function() + local function create() + schema.new_node_type(db, "a") + end + create() + expect.fail(create) + end) + end) + + describe("quote name", function() + it("quotes names and escapes double quotes", function() + local original = [[My "name" is "Frankie".]] + local expected = [["My ""name"" is ""Frankie""."]] + local actual = schema.quote_name(original) + expect.equal(expected, actual) + end) + end) +end) \ No newline at end of file