FYI, `INSTEAD OF` triggers on views can function like stored procedures in SQLite3.
It's not exactly the same, no, but you still get to have DMLs "stored" in the DB. For example, I'm not sure how to return results from an `INSTEAD OF` trigger (that is, if I write `INSERT INTO <view> (...) SELECT ... RETURNING ...;`, what will be returned? Apparently what's returned is the values of the NEW row, while one might think that if the view ends in a `SELECT` then maybe that would be used as the source for the `RETURNING` (but, no).
Even with limitations, `INSTEAD OF` triggers as an approximation of stored procedures is very powerful.
You'd do something like:
CREATE VIEW IF NOT EXISTS things AS ...;
--
-- INSERTs on `things` function like SPs
-- where values furnished for columns
-- function as SP input parameters.
CREATE TRIGGER IF NOT EXISTS create_thing
INSTEAD OF INSERT ON things
FOR EACH ROW
BEGIN
...
END;
and to invoke such a "stored procedure" you'd
INSERT INTO things (...) VALUES (...), ...;
or
INSERT .. SELECT ..;
This language is Turing-complete. You can also loop via recursive triggers. And you can use `WHERE` clauses (and `OR IGNORE`) on statements in the bodies of `INSTEAD OF` triggers to implement conditionals.
Most of this is taken from a comment I just wrote, https://news.ycombinator.com/item?id=31912640 but I've written this sort of thing before elsewhere too, including on the old sqlite3-users mailing list years ago. I'm pretty sure lots of you know about this -- it's really quite obvious.