{"id":18961466,"url":"https://github.com/kvakil/sqlvm","last_synced_at":"2025-06-23T01:06:57.771Z","repository":{"id":87306942,"uuid":"204412406","full_name":"kvakil/sqlvm","owner":"kvakil","description":"virtual machine in SQL","archived":false,"fork":false,"pushed_at":"2019-08-26T06:42:39.000Z","size":10,"stargazers_count":13,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-19T16:22:16.538Z","etag":null,"topics":["mysql","sql-injections","virtual-machine"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kvakil.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-08-26T06:41:00.000Z","updated_at":"2024-01-04T16:31:12.000Z","dependencies_parsed_at":"2023-03-06T16:45:35.264Z","dependency_job_id":null,"html_url":"https://github.com/kvakil/sqlvm","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/kvakil/sqlvm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kvakil%2Fsqlvm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kvakil%2Fsqlvm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kvakil%2Fsqlvm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kvakil%2Fsqlvm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kvakil","download_url":"https://codeload.github.com/kvakil/sqlvm/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kvakil%2Fsqlvm/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261392178,"owners_count":23151718,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["mysql","sql-injections","virtual-machine"],"created_at":"2024-11-08T14:13:03.769Z","updated_at":"2025-06-23T01:06:52.759Z","avatar_url":"https://github.com/kvakil.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SQLVM\n\nSQLVM (Structured Query Language Virtual Machine) is a source-to-source\ncompilation system. It allows you to use `goto`-like constructs in MySQL,\nallowing for easy imperative programming.\n\nFor example, say we have a table `ARRAY` as follows:\n\n```sql\nCREATE TABLE ARRAY (`id` int not null primary key, `n` int);\nINSERT INTO ARRAY (`id`, `n`) VALUES (1, 1), (2, 3), (3, -5), (4, 8); \n```\n\nSay we want to find the sum of squares of the `n` column.  We can write a\nJinja2 template which can then be transpiled with `sqlvm`:\n\n```jinja2\n{% sqlvm %}\n{# We can set our variables (one statement per line). #}\n@idx := 0\n@accumulator := 0\n\n{# As usual, parenthesis denote subqueries. #}\n(SELECT @count := COUNT(*) FROM ARRAY)\n\n{# Create a label we can jump to. #}\n{{ label(\"loop_start\") }}\n@idx := @idx + 1\n{# Pull element out using a subquery. #}\n(SELECT @e := n FROM ARRAY WHERE id = @idx)\n\n@accumulator := @accumulator + @e * @e\n{# We can use the jump function to jump to a label. #}\nIF(@idx = @count, {{ jump(\"done\") }}, {{ jump(\"loop_start\") }})\n\n{{ label(\"done\") }}\n@out := CONVERT(@accumulator, CHAR)\n{% endsqlvm %}\n```\n\nAs you can see, SQLVM has labels and jumps, so it can do everything a \"real\"\nprogramming language can.  \n\nRunning `python3 sqlvm.py` on the above code will give us this MySQL program.\nNote the result is a single MySQL statement (no [stacked\nqueries](http://www.sqlinjection.net/stacked-queries/) needed).\n\n```sql\nSELECT o FROM (\n    SELECT 0 v, '' o, 0 pc FROM (SELECT @pc:=0, @mem:='', @out:='') i UNION ALL\n    SELECT v,\n    CASE @pc\n        WHEN 0 THEN @idx := 0\n        WHEN 1 THEN @res := 0\n        WHEN 2 THEN (SELECT @count := COUNT(*) FROM ARRAY)\n        WHEN 3 THEN 0\n        WHEN 4 THEN @idx := @idx + 1\n        WHEN 5 THEN (SELECT @e := n FROM ARRAY WHERE id = @idx)\n        WHEN 6 THEN @res := @res + @e * @e\n        WHEN 7 THEN IF(@idx = @count, @pc := 8, @pc := 3)\n        WHEN 8 THEN 0\n        WHEN 9 THEN @out := CONVERT(@res,CHAR)\n        WHEN 10 THEN 0\n    ELSE @out END,\n    @pc:=@pc+1\n    FROM (SELECT (E0.v+E1.v+E2.v+E3.v+E4.v+E5.v+E6.v+E7.v) v FROM(SELECT 0 v UNION ALL SELECT 1 v) E0 CROSS JOIN (SELECT 0 v UNION ALL SELECT 2 v) E1 CROSS JOIN (SELECT 0 v UNION ALL SELECT 4 v) E2 CROSS JOIN (SELECT 0 v UNION ALL SELECT 8 v) E3 CROSS JOIN (SELECT 0 v UNION ALL SELECT 16 v) E4 CROSS JOIN (SELECT 0 v UNION ALL SELECT 32 v) E5 CROSS JOIN (SELECT 0 v UNION ALL SELECT 64 v) E6 CROSS JOIN (SELECT 0 v UNION ALL SELECT 128 v) E7 ORDER BY v) s) q ORDER BY v DESC LIMIT 1\n```\n\n(More details about the transpilation process are [available\nbelow](#how-does-it-work).)\n\n## FAQ\n\n### Why does this exist?\n\nThis is a good question. After all, the above example can be very succinctly\nexpressed in pure SQL as `SELECT SUM(n * n) FROM ARRAY`.\n\nI mainly created this for fun. Being able to do stuff like this might be useful\nin security [Capture The\nFlags](https://en.wikipedia.org/wiki/Capture_the_flag#Computer_security). With\nan eye towards that, the generated SQL is a single statement suitable for\nSQL injections.\n\n### How does it work?\n\nThe pseudocode is basically this:\n\n```c\n/* the program counter (i.e., which statement we'll be executing) */\npc = 0;\n/* the output of the program */\nout = \"\";\nwhile (true) {\n    switch (pc) {\n        case 0: /* statement 0 */; break;\n        case 1: /* statement 1 */; break;\n        case 2: /* statement 2 */; break;\n        /* ... */\n    }\n\n    pc = pc + 1;\n}\n```\n\nWe can represent variables using MySQL's [User-Defined\nVariables](https://dev.mysql.com/doc/refman/8.0/en/user-variables.html), and\nthe `switch ... case` construct can be done via MySQL's [case\nexpression](https://dev.mysql.com/doc/refman/8.0/en/control-flow-functions.html#operator_case).\nIn other words, we can write something like this:\n\n```sql\n@pc := 0, @out := ''\nCASE @pc\n    WHEN 0 THEN /* statement 0 */\n    WHEN 1 THEN /* statement 1 */\n    WHEN 2 THEN /* statement 2 */\n    /* ... */\n    ELSE @out\nEND\n```\n\nWhen `@pc` becomes large enough, the program stops executing statements and\njust returns `@out` (which is our program \"output\").\n\nThe only problem is representing the `while (true)` construct, which doesn't\nhave a great MySQL analogy. We could do something with [Common Table\nExpressions](https://dev.mysql.com/doc/refman/8.0/en/with.html) to get\nrecursion, but those (1) can't be used SQL injections and (2) don't work in the\n5.X branch of MySQL.\n\nSo scratch `while (true)`, we'll settle for getting a really big `for` loop:\n\n```diff\n-while (true) {\n+for (int i = 0; i \u003c (big power of 2); i++) {\n```\n\nTo get a \"for loop\" in MySQL, we'll create a table and iterate over it with a\n`FROM` clause.  The easiest way to get a very large table is to [`CROSS\nJOIN`](https://dev.mysql.com/doc/refman/8.0/en/join.html) a bunch of small\ntables together--in particular, we join power of two tables together:\n\n```sql\nSELECT (E0.v+E1.v+E2.v+/* ... */) v FROM\n    (SELECT 0 v UNION ALL SELECT 1 v) E0 CROSS JOIN\n    (SELECT 0 v UNION ALL SELECT 2 v) E1 CROSS JOIN\n    (SELECT 0 v UNION ALL SELECT 4 v) E2 CROSS JOIN\n    /* ... */\nORDER BY v\n```\n\nPutting it together (and adding some initialization code) we get:\n```sql\n/* Select the output */\nSELECT o FROM (\n    SELECT 0 v, '' o, 0 pc FROM (SELECT @pc:=0, @mem:='', @out:='') i UNION ALL\n    SELECT v,\n    CASE @pc\n        WHEN 0 THEN /* statement 0 */\n        WHEN 1 THEN /* statement 1 */\n        WHEN 2 THEN /* statement 2 */\n        /* ... */\n    ELSE @out END,\n    @pc:=@pc+1\n(SELECT (E0.v+E1.v+E2.v+/* ... */) v FROM\n    (SELECT 0 v UNION ALL SELECT 1 v) E0 CROSS JOIN\n    (SELECT 0 v UNION ALL SELECT 2 v) E1 CROSS JOIN\n    (SELECT 0 v UNION ALL SELECT 4 v) E2 CROSS JOIN\n    /* ... */\nORDER BY v) s) q\nORDER BY v DESC LIMIT 1 /* filter to select the \"last\" output */\n```\n\nAnd tada, we have a virtual machine!\n\nFinally, there's a Jinja2 extension for ergonomics purposes.\n\n### How can I use it?\n\nYou'll need Python 3.\n\n    $ python3 -m pip install -r requirements.txt\n    $ python3 sqlvm.py {input template file here}\n\n### Does this work with other SQL variants?\n\nNot really, but the necessary scaffolding is there--for example see\n`languages/mysql.py`.\n\n### Documentation? Test Cases?\n\nNot really, but there's examples under [examples/](examples/).\n\n### Is this production ready? It doesn't sound like it.\n\nYes, it absolutely is.\n\n## Similar Projects\n\n[ELVM](https://github.com/shinh/elvm/) can compile C-like code to SQLite. It's\nnot too hard to recreate these ideas in ELVM, but the resulting code is far\nless efficient because it doesn't interface with MySQL functions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkvakil%2Fsqlvm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkvakil%2Fsqlvm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkvakil%2Fsqlvm/lists"}