├── .gitignore ├── .gitmodules ├── README.md ├── container ├── Dockerfile ├── build.sh └── src │ ├── ao.lua │ ├── aolibc │ ├── Makefile │ └── aostdio.c │ ├── definition.yml │ ├── emcc-lua │ ├── emcc_lua_lib │ ├── definition.py │ ├── file.py │ └── helper.py │ ├── json.lua │ ├── loader.lua │ ├── lsqlite3.c │ ├── lsqlite3.h │ ├── main.c │ ├── main.lua │ ├── node │ ├── apply-metering.cjs │ ├── package.json │ └── yarn.lock │ ├── pack.lua │ ├── pre.js │ ├── sqlite3.c │ └── sqlite3.h ├── deploy.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "aos"] 2 | path = aos 3 | url = https://github.com/permaweb/aos 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AOS-SQLite 2 | 3 | AOS-SQLite combines the ao operating system module and sqlite to create an ao custom module to add a lightweight but powerful indexer to your aos experience. 4 | 5 | > The bulk of this effort was done by @elliotsayes during the Hack the Weave competition, Elliot was able to create this WASM Binary that includes both SQLite, LUA, and aos, as an `ao` Module. 6 | 7 | AOS-SQLite Module - `ghSkge2sIUD_F00ym5sEimC63BDBuBrq4b5OcwxOjiw` 8 | 9 | Run a SQLite Database with AOS(WASM64) 10 | 11 | ```sh 12 | aos my-sqlite --module=ghSkge2sIUD_F00ym5sEimC63BDBuBrq4b5OcwxOjiw 13 | ``` 14 | 15 | Run a SQLite Database with AOS(WASM32) 16 | 17 | ```sh 18 | aos my-sqlite --module=GYrbbe0VbHim_7Hi6zrOpHQXrSQz07XNtwCnfbFo2I0 19 | ``` 20 | 21 | ## Spawn via a process 22 | 23 | ```lua 24 | Spawn('ghSkge2sIUD_F00ym5sEimC63BDBuBrq4b5OcwxOjiw', { Data = "Hello SQLite Wasm64" }) 25 | ``` 26 | 27 | ## Examples 28 | 29 | ```lua 30 | local sqlite3 = require("lsqlite3") 31 | 32 | db = sqlite3.open_memory() 33 | 34 | db:exec[[ 35 | CREATE TABLE test (id INTEGER PRIMARY KEY, content); 36 | INSERT INTO test VALUES (NULL, 'Hello Lua'); 37 | INSERT INTO test VALUES (NULL, 'Hello Sqlite3'); 38 | INSERT INTO test VALUES (NULL, 'Hello ao!!!'); 39 | ]] 40 | return "ok" 41 | 42 | ``` 43 | 44 | ```lua 45 | local s = "" 46 | 47 | for row in db:nrows("SELECT * FROM test") do 48 | s = s .. row.id .. ": " .. row.content .. "\\n" 49 | end 50 | 51 | return s 52 | ``` 53 | 54 | ## AO Resources 55 | 56 | * [AOSqlite Workshop](https://hackmd.io/@ao-docs/rkM1C9m40) 57 | 58 | * https://ao.arweave.dev 59 | * https://cookbook_ao.arweave.dev 60 | 61 | --- 62 | 63 | This project builds the AOS-SQLITE WASM Binary and Publishes it to Arweave. 64 | 65 | ## Build Process 66 | 67 | 1. Build docker image 68 | 69 | ```sh 70 | cd container 71 | ./build.sh 72 | ``` 73 | 74 | 2. Get Latest aos module 75 | 76 | ```sh 77 | git submodule init 78 | git submodule update --remote 79 | ``` 80 | 81 | 3. Use docker image to compile process.wasm 82 | 83 | ```sh 84 | cd aos/process 85 | docker run -v .:/src p3rmaw3b/ao emcc-lua 86 | ``` 87 | 88 | 4. Publish Module with tags via arkb 89 | 90 | > You will need a funded wallet for this step 91 | 92 | ```sh 93 | export WALLET=~/.wallet.json 94 | npm run deploy 95 | ``` 96 | -------------------------------------------------------------------------------- /container/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM emscripten/emsdk:3.1.59 2 | LABEL maintainer "tom wilson " 3 | 4 | # The working directory used by the base image is /src, so we can mount volumes to there 5 | # to expose files on the host to the ao container 6 | # 7 | # https://github.com/emscripten-core/emsdk/blob/9b0db91883452051aca8deddc932363aab29060b/docker/Dockerfile#L120 8 | 9 | RUN apt-get update -qq -y 10 | RUN apt-get install -y curl vim make gcc libreadline6-dev libssl-dev zlib1g-dev zip unzip 11 | 12 | ENV LUA_VERSION 5.3.4 13 | ENV LUAROCKS_VERSION 2.4.4 14 | ENV PYTHON_VERSION 3.6.6 15 | 16 | # install deno 17 | # RUN curl -fsSL https://deno.land/x/install/install.sh | sh 18 | # RUN echo 'export DENO_INSTALL="/root/.deno"' >> $HOME/.bashrc 19 | # RUN echo 'export PATH="$DENO_INSTALL/bin:$PATH"' >> $HOME/.bashrc 20 | 21 | # Intall yaml 22 | RUN pip3 install pyyaml 23 | 24 | # Install lua runtime 25 | RUN cd / && \ 26 | curl -L http://www.lua.org/ftp/lua-${LUA_VERSION}.tar.gz | tar xzf - && \ 27 | cd /lua-${LUA_VERSION} && \ 28 | make linux test && \ 29 | make install 30 | 31 | # Install luarocks 32 | RUN cd / && \ 33 | curl -L https://luarocks.org/releases/luarocks-${LUAROCKS_VERSION}.tar.gz | tar xzf - && \ 34 | cd /luarocks-${LUAROCKS_VERSION} && \ 35 | ./configure && \ 36 | make build && \ 37 | make install 38 | 39 | # Install NodeJS 40 | ENV NODE_VERSION=18.17.0 41 | RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash 42 | ENV NVM_DIR=/root/.nvm 43 | RUN . "$NVM_DIR/nvm.sh" && nvm install ${NODE_VERSION} 44 | RUN . "$NVM_DIR/nvm.sh" && nvm use v${NODE_VERSION} 45 | RUN . "$NVM_DIR/nvm.sh" && nvm alias default v${NODE_VERSION} 46 | ENV PATH="/root/.nvm/versions/node/v${NODE_VERSION}/bin/:${PATH}" 47 | RUN node --version 48 | RUN npm --version 49 | 50 | # And, re-compile lua with "generic WASM" 51 | RUN cd /lua-${LUA_VERSION} && \ 52 | make clean && \ 53 | make generic CC='emcc -s WASM=1 -s MEMORY64=1 -s SUPPORT_LONGJMP=1' 54 | 55 | ############################# 56 | ##### Install Commands ###### 57 | ############################# 58 | 59 | # NOTE: Some commands do not require additional work 60 | # when building the image, but are still commented 61 | # here for posterity 62 | 63 | ############### 64 | #### Init ##### 65 | ############### 66 | # The init command currently does not require running 67 | # within the ao container, and so requires no additional setup 68 | 69 | ############### 70 | #### Repl ##### 71 | ############### 72 | # The repl command simply fires up lua repl, which is installed 73 | # as part of lua runtime above, and so requires no additional setup 74 | 75 | ############### 76 | #### Build #### 77 | ############### 78 | COPY ./src/emcc-lua /usr/local/bin/emcc-lua 79 | COPY ./src/emcc_lua_lib /usr/local/emcc-lua/emcc_lua_lib 80 | 81 | COPY ./src/pre.js /opt/pre.js 82 | COPY ./src/definition.yml /opt/definition.yml 83 | COPY ./src/loader.lua /opt/loader.lua 84 | 85 | RUN mkdir -p /opt/src 86 | COPY ./src/json.lua /opt/src/json.lua 87 | COPY ./src/ao.lua /opt/src/ao.lua 88 | # COPY ./src/pack.lua /opt/pack.lua 89 | COPY ./src/main.c /opt/main.c 90 | # COPY ./src/linit.c /opt/linit.c 91 | COPY ./src/lsqlite3.c /opt/lsqlite3.c 92 | COPY ./src/lsqlite3.h /opt/lsqlite3.h 93 | COPY ./src/sqlite3.c /opt/sqlite3.c 94 | COPY ./src/sqlite3.h /opt/sqlite3.h 95 | COPY ./src/main.lua /opt/main.lua 96 | 97 | ################################### 98 | # BUILD WeaveDrive Extension Helper 99 | ################################### 100 | COPY ./src/aolibc /opt/aolibc 101 | RUN cd /opt/aolibc && make CC="emcc -s WASM=1 -s MEMORY64=1 -s SUPPORT_LONGJMP=1" 102 | 103 | 104 | RUN chmod +x /usr/local/bin/emcc-lua 105 | 106 | ENV CC 'emcc -s WASM=1' 107 | ENV NM 'llvm-nm' 108 | 109 | ########################################### 110 | ### Publish, Bundler, Process, Contract ### 111 | ########################################### 112 | # We first create a directory for the node impls to be placed 113 | # and dependencies installed 114 | # 115 | # By running npm link, we allow any commands exposed by 116 | # the node module to be ran globally within the container 117 | RUN mkdir -p /opt/node 118 | COPY ./src/node /opt/node 119 | RUN cd /opt/node && \ 120 | npm install --omit="dev" && \ 121 | npm link -------------------------------------------------------------------------------- /container/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | docker build . -t p3rmaw3b/ao -------------------------------------------------------------------------------- /container/src/ao.lua: -------------------------------------------------------------------------------- 1 | local oldao = ao or {} 2 | local ao = { 3 | _version = "0.0.6", 4 | id = oldao.id or "", 5 | _module = oldao._module or "", 6 | authorities = oldao.authorities or {}, 7 | reference = oldao.reference or 0, 8 | outbox = oldao.outbox or 9 | {Output = {}, Messages = {}, Spawns = {}, Assignments = {}}, 10 | nonExtractableTags = { 11 | 'Data-Protocol', 'Variant', 'From-Process', 'From-Module', 'Type', 12 | 'From', 'Owner', 'Anchor', 'Target', 'Data', 'Tags' 13 | }, 14 | nonForwardableTags = { 15 | 'Data-Protocol', 'Variant', 'From-Process', 'From-Module', 'Type', 16 | 'From', 'Owner', 'Anchor', 'Target', 'Tags', 'TagArray', 'Hash-Chain', 17 | 'Timestamp', 'Nonce', 'Epoch', 'Signature', 'Forwarded-By', 18 | 'Pushed-For', 'Read-Only', 'Cron', 'Block-Height', 'Reference', 'Id', 19 | 'Reply-To' 20 | } 21 | } 22 | 23 | local function _includes(list) 24 | return function(key) 25 | local exists = false 26 | for _, listKey in ipairs(list) do 27 | if key == listKey then 28 | exists = true 29 | break 30 | end 31 | end 32 | if not exists then return false end 33 | return true 34 | end 35 | end 36 | 37 | local function isArray(table) 38 | if type(table) == "table" then 39 | local maxIndex = 0 40 | for k, v in pairs(table) do 41 | if type(k) ~= "number" or k < 1 or math.floor(k) ~= k then 42 | return false -- If there's a non-integer key, it's not an array 43 | end 44 | maxIndex = math.max(maxIndex, k) 45 | end 46 | -- If the highest numeric index is equal to the number of elements, it's an array 47 | return maxIndex == #table 48 | end 49 | return false 50 | end 51 | 52 | local function padZero32(num) return string.format("%032d", num) end 53 | 54 | function ao.clone(obj, seen) 55 | -- Handle non-tables and previously-seen tables. 56 | if type(obj) ~= 'table' then return obj end 57 | if seen and seen[obj] then return seen[obj] end 58 | 59 | -- New table; mark it as seen and copy recursively. 60 | local s = seen or {} 61 | local res = {} 62 | s[obj] = res 63 | for k, v in pairs(obj) do res[ao.clone(k, s)] = ao.clone(v, s) end 64 | return setmetatable(res, getmetatable(obj)) 65 | end 66 | 67 | function ao.normalize(msg) 68 | for _, o in ipairs(msg.Tags) do 69 | if not _includes(ao.nonExtractableTags)(o.name) then 70 | msg[o.name] = o.value 71 | end 72 | end 73 | return msg 74 | end 75 | 76 | function ao.sanitize(msg) 77 | local newMsg = ao.clone(msg) 78 | 79 | for k, _ in pairs(newMsg) do 80 | if _includes(ao.nonForwardableTags)(k) then newMsg[k] = nil end 81 | end 82 | 83 | return newMsg 84 | end 85 | 86 | function ao.init(env) 87 | if ao.id == "" then ao.id = env.Process.Id end 88 | 89 | if ao._module == "" then 90 | for _, o in ipairs(env.Process.Tags) do 91 | if o.name == "Module" then ao._module = o.value end 92 | end 93 | end 94 | 95 | if #ao.authorities < 1 then 96 | for _, o in ipairs(env.Process.Tags) do 97 | if o.name == "Authority" then 98 | table.insert(ao.authorities, o.value) 99 | end 100 | end 101 | end 102 | 103 | ao.outbox = {Output = {}, Messages = {}, Spawns = {}, Assignments = {}} 104 | ao.env = env 105 | 106 | end 107 | 108 | function ao.log(txt) 109 | if type(ao.outbox.Output) == 'string' then 110 | ao.outbox.Output = {ao.outbox.Output} 111 | end 112 | table.insert(ao.outbox.Output, txt) 113 | end 114 | 115 | -- clears outbox 116 | function ao.clearOutbox() 117 | ao.outbox = {Output = {}, Messages = {}, Spawns = {}, Assignments = {}} 118 | end 119 | 120 | function ao.send(msg) 121 | assert(type(msg) == 'table', 'msg should be a table') 122 | ao.reference = ao.reference + 1 123 | local referenceString = tostring(ao.reference) 124 | 125 | local message = { 126 | Target = msg.Target, 127 | Data = msg.Data, 128 | Anchor = padZero32(ao.reference), 129 | Tags = { 130 | {name = "Data-Protocol", value = "ao"}, 131 | {name = "Variant", value = "ao.TN.1"}, 132 | {name = "Type", value = "Message"}, 133 | {name = "Reference", value = referenceString} 134 | } 135 | } 136 | 137 | -- if custom tags in root move them to tags 138 | for k, v in pairs(msg) do 139 | if not _includes({"Target", "Data", "Anchor", "Tags", "From"})(k) then 140 | table.insert(message.Tags, {name = k, value = v}) 141 | end 142 | end 143 | 144 | if msg.Tags then 145 | if isArray(msg.Tags) then 146 | for _, o in ipairs(msg.Tags) do 147 | table.insert(message.Tags, o) 148 | end 149 | else 150 | for k, v in pairs(msg.Tags) do 151 | table.insert(message.Tags, {name = k, value = v}) 152 | end 153 | end 154 | end 155 | 156 | -- If running in an environment without the AOS Handlers module, do not add 157 | -- the onReply and receive functions to the message. 158 | if not Handlers then return message end 159 | 160 | -- clone message info and add to outbox 161 | local extMessage = {} 162 | for k, v in pairs(message) do extMessage[k] = v end 163 | 164 | -- add message to outbox 165 | table.insert(ao.outbox.Messages, extMessage) 166 | 167 | -- add callback for onReply handler(s) 168 | message.onReply = 169 | function(...) -- Takes either (AddressThatWillReply, handler(s)) or (handler(s)) 170 | local from, resolver 171 | if select("#", ...) == 2 then 172 | from = select(1, ...) 173 | resolver = select(2, ...) 174 | else 175 | from = message.Target 176 | resolver = select(1, ...) 177 | end 178 | 179 | -- Add a one-time callback that runs the user's (matching) resolver on reply 180 | Handlers.once({From = from, ["X-Reference"] = referenceString}, 181 | resolver) 182 | end 183 | 184 | message.receive = function(...) 185 | local from = message.Target 186 | if select("#", ...) == 1 then from = select(1, ...) end 187 | return 188 | Handlers.receive({From = from, ["X-Reference"] = referenceString}) 189 | end 190 | 191 | return message 192 | end 193 | 194 | function ao.spawn(module, msg) 195 | assert(type(module) == "string", "Module source id is required!") 196 | assert(type(msg) == 'table', 'Message must be a table') 197 | -- inc spawn reference 198 | ao.reference = ao.reference + 1 199 | local spawnRef = tostring(ao.reference) 200 | 201 | local spawn = { 202 | Data = msg.Data or "NODATA", 203 | Anchor = padZero32(ao.reference), 204 | Tags = { 205 | {name = "Data-Protocol", value = "ao"}, 206 | {name = "Variant", value = "ao.TN.1"}, 207 | {name = "Type", value = "Process"}, 208 | {name = "From-Process", value = ao.id}, 209 | {name = "From-Module", value = ao._module}, 210 | {name = "Module", value = module}, 211 | {name = "Reference", value = spawnRef} 212 | } 213 | } 214 | 215 | -- if custom tags in root move them to tags 216 | for k, v in pairs(msg) do 217 | if not _includes({"Target", "Data", "Anchor", "Tags", "From"})(k) then 218 | table.insert(spawn.Tags, {name = k, value = v}) 219 | end 220 | end 221 | 222 | if msg.Tags then 223 | if isArray(msg.Tags) then 224 | for _, o in ipairs(msg.Tags) do 225 | table.insert(spawn.Tags, o) 226 | end 227 | else 228 | for k, v in pairs(msg.Tags) do 229 | table.insert(spawn.Tags, {name = k, value = v}) 230 | end 231 | end 232 | end 233 | 234 | -- If running in an environment without the AOS Handlers module, do not add 235 | -- the after and receive functions to the spawn. 236 | if not Handlers then return spawn end 237 | 238 | -- clone spawn info and add to outbox 239 | local extSpawn = {} 240 | for k, v in pairs(spawn) do extSpawn[k] = v end 241 | 242 | table.insert(ao.outbox.Spawns, extSpawn) 243 | 244 | -- add 'after' callback to returned table 245 | -- local result = {} 246 | spawn.onReply = function(callback) 247 | Handlers.once({ 248 | Action = "Spawned", 249 | From = ao.id, 250 | ["Reference"] = spawnRef 251 | }, callback) 252 | end 253 | 254 | spawn.receive = function() 255 | return Handlers.receive({ 256 | Action = "Spawned", 257 | From = ao.id, 258 | ["Reference"] = spawnRef 259 | }) 260 | 261 | end 262 | 263 | return spawn 264 | end 265 | 266 | function ao.assign(assignment) 267 | assert(type(assignment) == 'table', 'assignment should be a table') 268 | assert(type(assignment.Processes) == 'table', 'Processes should be a table') 269 | assert(type(assignment.Message) == "string", "Message should be a string") 270 | table.insert(ao.outbox.Assignments, assignment) 271 | end 272 | 273 | -- The default security model of AOS processes: Trust all and *only* those 274 | -- on the ao.authorities list. 275 | function ao.isTrusted(msg) 276 | for _, authority in ipairs(ao.authorities) do 277 | if msg.From == authority then return true end 278 | if msg.Owner == authority then return true end 279 | end 280 | return false 281 | end 282 | 283 | function ao.result(result) 284 | -- if error then only send the Error to CU 285 | if ao.outbox.Error or result.Error then 286 | return {Error = result.Error or ao.outbox.Error} 287 | end 288 | return { 289 | Output = result.Output or ao.outbox.Output, 290 | Messages = ao.outbox.Messages, 291 | Spawns = ao.outbox.Spawns, 292 | Assignments = ao.outbox.Assignments 293 | } 294 | end 295 | 296 | return ao 297 | -------------------------------------------------------------------------------- /container/src/aolibc/Makefile: -------------------------------------------------------------------------------- 1 | # Builds a patched libc.a, customized with the override functions found in aostdlib.c 2 | 3 | # Path to the Emscripten SDK 4 | EMSDK_PATH ?= $(shell echo $$EMSDK) 5 | LIBC_A_PATH ?= $(shell find $(EMSDK_PATH) -name libc.a) 6 | PATCHED_LIBC_A_PATH ?= $(shell pwd)/libc.ao.a 7 | 8 | # Path to your custom stdlib object file 9 | CUSTOM_LIB_OBJ = aostdio.o 10 | 11 | .PHONY: build 12 | build: aolibc.a 13 | 14 | aolibc.a: $(CUSTOM_LIB_OBJ) 15 | emar r aolibc.a $(CUSTOM_LIB_OBJ) 16 | 17 | CUSTOM_LIB_OBJ: aostdio.c 18 | emcc -s -c aostdio.c -o aostdio.o 19 | 20 | # Default target 21 | install: build-patched update-libc 22 | 23 | # Target to update libc.a 24 | build-patched: $(CUSTOM_LIB_OBJ) 25 | @# Ensure libc.a path is not empty 26 | @if [ -z "$(LIBC_A_PATH)" ]; then \ 27 | echo "libc.a not found, check your EMSDK_PATH"; \ 28 | exit 1; \ 29 | fi 30 | @cp $(LIBC_A_PATH) $(PATCHED_LIBC_A_PATH) 31 | @echo "Updating libc.a at $(PATCHED_LIBC_A_PATH)..." 32 | @# Extract function names from the custom object file 33 | $(eval FUNCTIONS_TO_REMOVE=$(shell emnm $(CUSTOM_LIB_OBJ) | grep ' T ' | cut -d' ' -f3 | sed 's/$$/.o/')) 34 | @echo "Removing functions: $(FUNCTIONS_TO_REMOVE)" 35 | @# Remove existing function implementations from libc.a 36 | emar d $(PATCHED_LIBC_A_PATH) $(FUNCTIONS_TO_REMOVE) 37 | @# Add custom stdlib object file to libc.a 38 | emar r $(PATCHED_LIBC_A_PATH) $(CUSTOM_LIB_OBJ) 39 | @echo "libc.a updated successfully." 40 | 41 | # Clean up 42 | clean: 43 | @echo "Cleaning up..." 44 | @rm -f $(CUSTOM_LIB_OBJ) 45 | @echo "Clean complete." -------------------------------------------------------------------------------- /container/src/aolibc/aostdio.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #if 0 9 | #define AO_LOG(...) fprintf(stderr, __VA_ARGS__) 10 | #else 11 | #define AO_LOG(...) 12 | #endif 13 | 14 | // WeaveDrive async wrapper functions. These allow us to call the WeaveDrive 15 | // async JS code from C. 16 | EM_ASYNC_JS(int, weavedrive_open, (const char* c_filename, const char* mode), { 17 | const filename = UTF8ToString(Number(c_filename)); 18 | if (!Module.WeaveDrive) { 19 | return Promise.resolve(null) 20 | } 21 | 22 | const drive = Module.WeaveDrive(Module, FS); 23 | return await drive.open(filename); 24 | }); 25 | 26 | EM_ASYNC_JS(int, weavedrive_read, (int fd, int *dst_ptr, size_t length), { 27 | const drive = Module.WeaveDrive(Module, FS); 28 | return Promise.resolve(await drive.read(fd, dst_ptr, length)); 29 | }); 30 | 31 | EM_ASYNC_JS(int, weavedrive_close, (int fd), { 32 | const drive = Module.WeaveDrive(Module, FS); 33 | return drive.close(fd); 34 | }); 35 | 36 | FILE* fopen(const char* filename, const char* mode) { 37 | AO_LOG( "AO: Called fopen: %s, %s\n", filename, mode); 38 | int fd = weavedrive_open(filename, mode); 39 | AO_LOG( "AO: weavedrive_open returned fd: %d\n", fd); 40 | // If we get a file desciptor, we return a FILE*, else 0. 41 | if (!fd) { 42 | return 0; 43 | } 44 | return fdopen(fd, mode); 45 | } 46 | 47 | size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream) { 48 | int fd = fileno(stream); 49 | weavedrive_read(fd, ptr, size * nmemb); 50 | return nmemb; 51 | } 52 | 53 | int fclose(FILE* stream) { 54 | AO_LOG( "AO: fclose called\n"); 55 | int fd = fileno(stream); 56 | weavedrive_close(fd); 57 | return 0; // Returning success, adjust as necessary 58 | } 59 | 60 | void* realloc(void* ptr, size_t size) { 61 | void* new_ptr = memalign(16, size); 62 | memcpy(new_ptr, ptr, size); 63 | free(ptr); 64 | //AO_LOG("DBG: Realloc called: %p -> %p, size: %zu\n", ptr, new_ptr, size); 65 | return new_ptr; 66 | } 67 | 68 | // Emscripten malloc does not align to 16 bytes correctly, which causes some 69 | // programs that use aligned memory (for example, those that use SIMD...) to 70 | // crash. So we need to use the aligned allocator. 71 | void* malloc(size_t size) { 72 | return memalign(16, size); 73 | } 74 | 75 | int madvise(void* addr, size_t length, int advice) { 76 | AO_LOG("AO: madvise called with addr: %p, length: %zu, advice: %d\n", addr, length, advice); 77 | return 0; 78 | } 79 | 80 | void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset) { 81 | AO_LOG("AO: mmap called with addr: %p, length: %zu, prot: %d, flags: %d, fd: %d, offset: %d\n", addr, length, prot, flags, fd, offset); 82 | // Allocate a buffer that fits with emscripten's normal allignments 83 | void* buffer = memalign(65536, length); 84 | AO_LOG("AO: mmap: Reading from arweave to: %p, length: %zu\n", buffer, length); 85 | weavedrive_read(fd, buffer, length); 86 | AO_LOG("AO: mmap returned: %p\n", buffer); 87 | return buffer; 88 | } 89 | 90 | /* 91 | int munmap(void* addr, size_t length) { 92 | AO_LOG("AO: munmap called with addr: %p, length: %zu\n", addr, length); 93 | return 0; 94 | } 95 | */ 96 | -------------------------------------------------------------------------------- /container/src/definition.yml: -------------------------------------------------------------------------------- 1 | functions: 2 | handle: 3 | return: string 4 | args: 5 | - string 6 | - string 7 | 8 | entry_file: loader.lua 9 | output_file: process.js 10 | -------------------------------------------------------------------------------- /container/src/emcc-lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import os 5 | import glob 6 | import re 7 | import shutil 8 | from shlex import quote 9 | import subprocess 10 | 11 | sys.path.append('/usr/local/emcc-lua') 12 | 13 | from emcc_lua_lib.definition import Definition 14 | from emcc_lua_lib.file import LuaFile, ModuleFile, BundleFile 15 | from emcc_lua_lib.helper import is_lua_source_file, is_binary_library, encode_hex_literals, shell_exec, debug_print 16 | 17 | 18 | CC = os.environ.get('CC', 'cc') 19 | NM = os.environ.get('NM', 'nm') 20 | LUAROCKS_LOCAL_MODULE_DIR = '/src/modules' # os.path.abspath('/src/modules') 21 | LUAROCKS_LOCAL_MODULE_PREFIX_RE = re.compile(re.escape(LUAROCKS_LOCAL_MODULE_DIR) + '\/share\/lua\/\d+.\d+/') 22 | 23 | def __get_uname(): 24 | uname = '' 25 | try: 26 | uname, _ = shell_exec('uname', '-s') 27 | except(subprocess.CalledProcessError): 28 | uname = 'Unknown' 29 | 30 | return uname 31 | 32 | def __get_output(output, entry_file): 33 | out_file = os.path.basename(entry_file) 34 | if output.get('file'): 35 | out_file = output.get('file') 36 | 37 | tpl = os.path.splitext(out_file)[1] 38 | if not tpl[1]: 39 | tpl[1] = 'html' 40 | 41 | return '{}.{}'.format(tpl[0], tpl[1]) 42 | 43 | def main(): 44 | uname = __get_uname() 45 | 46 | lua_files = [] 47 | library_files = [] 48 | link_libraries = [] 49 | dependency_libraries = [] 50 | #libdl_option = '' 51 | 52 | # To absolute 53 | # Module, function setting file must exist at same directory 54 | # definition = Definition(os.path.join(os.getcwd(), 'definition.yml')) 55 | definition = Definition('/opt/definition.yml') 56 | #entry_file = definition.get_entry_file() 57 | entry_file = '/opt/loader.lua' 58 | 59 | if not is_lua_source_file(entry_file): 60 | print('main file of {} must be lua script.'.format(entry_file)) 61 | return 62 | 63 | definition.install_dependencies(LUAROCKS_LOCAL_MODULE_DIR) 64 | 65 | local_include_dir = os.path.join(os.path.dirname(entry_file), 'src') 66 | local_include_prefix_re = re.compile(re.escape(local_include_dir + '/')) 67 | 68 | # Detect bundles 69 | bundle_files = glob.glob('/src/**/*.lua', recursive=True) 70 | 71 | # Optional dependencies 72 | bundle_files += glob.glob(local_include_dir + '/**/*.lua', recursive=True) 73 | bundle_files += glob.glob(local_include_dir + '/**/*.so', recursive=True) 74 | bundle_files += glob.glob(LUAROCKS_LOCAL_MODULE_DIR + '/lib/lua/**/*.so', recursive=True) 75 | bundle_files += glob.glob(LUAROCKS_LOCAL_MODULE_DIR + '/share/lua/**/*.lua', recursive=True) 76 | debug_print('Start to factory and distinguish module files') 77 | 78 | for bundle in bundle_files: 79 | if is_lua_source_file(bundle): 80 | basename = re.sub(LUAROCKS_LOCAL_MODULE_PREFIX_RE, '', bundle) 81 | basename = re.sub(local_include_prefix_re, '', basename) 82 | lua_files.append(LuaFile(bundle, basename)) 83 | continue 84 | 85 | if is_binary_library(bundle): 86 | try: 87 | nm, _ = shell_exec(NM, bundle) 88 | is_module = False 89 | if re.search(r'T _?luaL_newstate', nm): 90 | if re.search(r'U _?dlopen', nm): 91 | if uname in ['Linux', 'SunOS', 'Darwin']: 92 | libdl_option = '-ldl' 93 | 94 | else: 95 | for luaopen in re.finditer(r'[^dD] _?luaopen_([0-9a-zA-Z!"#\$%&\'\(\)\*\+,\-\.\/:;\<=\>\?@\[\]^_`\{\|\}~]+)', nm): 96 | debug_print('luaopen_{} function found. add to library in {}'.format(luaopen.group(1), bundle)) 97 | library_files.append(ModuleFile(bundle, luaopen.group(1))) 98 | is_module = True 99 | 100 | 101 | if is_module: 102 | link_libraries.append(BundleFile(bundle)) 103 | else: 104 | dependency_libraries.append(BundleFile(bundle)) 105 | 106 | except(subprocess.CalledProcessError): 107 | print(NM + ' command failed') 108 | return 109 | 110 | debug_print('===== Bundle Lua files ======') 111 | debug_print('\n'.join([v.filepath for v in lua_files])) 112 | debug_print('===== Library files =====') 113 | debug_print('\n'.join([v.filepath for v in library_files])) 114 | debug_print('===== Link libraries =====') 115 | debug_print('\n'.join([v.filepath for v in link_libraries])) 116 | debug_print('===== Dependency libraries =====') 117 | debug_print('\n'.join([v.filepath for v in dependency_libraries])) 118 | 119 | 120 | #Generate compile target file 121 | debug_print('Start to generate complie.c') 122 | 123 | with open('/opt/main.lua', mode='r') as lua: 124 | lua_program = lua.read() 125 | 126 | with open('/opt/main.c', mode='r') as c: 127 | c_program = c.read() 128 | c_program = c_program.replace('__LUA_BASE__', encode_hex_literals(lua_program)) 129 | with open(entry_file, mode='r') as main_file: 130 | p = main_file.read() 131 | c_program = c_program.replace('__LUA_MAIN__', encode_hex_literals(p)) 132 | 133 | inject_lua_files = [] 134 | for i, f in enumerate(lua_files): 135 | with open(f.filepath, mode='r') as l: 136 | lines = l.readlines() 137 | # check first line has control command 138 | if lines[0].find("\xef\xbb\xbf") != -1: 139 | # UTF-8 byte order mark 140 | lines[0] = lines[0][4:] 141 | elif lines[0][0] == '#': 142 | # shebang 143 | lines = lines[1:] 144 | 145 | inject_lua_files.extend([ 146 | ' static const unsigned char lua_require_{}[] = {{{}}};'.format(i, encode_hex_literals('\n'.join(lines))), 147 | ' lua_pushlstring(L, (const char*)lua_require_{}, sizeof(lua_require_{}));'.format(i, i), 148 | ' lua_setfield(L, -2, "{}");\n'.format(f.module_name) 149 | ]) 150 | 151 | 152 | for f in library_files: 153 | inject_lua_files.extend([ 154 | ' int luaopen_{}(lua_State* L);'.format(f.module_name), 155 | ' lua_pushcfunction(L, luaopen_{});'.format(f.module_name), 156 | ' lua_setfield(L, -2, "{}");'.format(f.basename) 157 | ]) 158 | 159 | c_program = c_program.replace('__LUA_FUNCTION_DECLARATIONS__', definition.make_function_delarations()) 160 | c_program = c_program.replace('__INJECT_LUA_FILES__', '\n'.join(inject_lua_files)) 161 | 162 | with open('/opt/compile.c', mode='w') as build: 163 | build.write(c_program) 164 | 165 | # Finally, compile to wasm 166 | debug_print('Start to compile as WASM') 167 | cmd = ['emcc', '-O3'] 168 | cmd.extend([ 169 | '-g2', 170 | '-s', 'MEMORY64=1', 171 | '-s', 'ASYNCIFY=1', 172 | '-s', 'STACK_SIZE=41943040', 173 | '-s', 'ASYNCIFY_STACK_SIZE=41943040', 174 | '-s', 'ALLOW_MEMORY_GROWTH=1', 175 | '-s', 'INITIAL_MEMORY=83886080', 176 | '-s', 'MAXIMUM_MEMORY=17179869184', 177 | '-s', 'WASM=1', 178 | '-s', 'MODULARIZE', 179 | # '-s', 'FILESYSTEM=0', 180 | '-s', 'DETERMINISTIC=1', 181 | '-s', 'NODERAWFS=0', 182 | '-s', 'FORCE_FILESYSTEM=1', 183 | '-msimd128' 184 | ]) 185 | cmd.extend(['-L/opt/aolibc', '-l:aolibc.a']) 186 | cmd.extend(['-s', 'ASSERTIONS=1']) 187 | cmd.extend(['--pre-js', '/opt/pre.js']) 188 | cmd.extend(definition.get_extra_args()) 189 | cmd.extend(['-I', quote('/lua-{}/src'.format(os.environ.get('LUA_VERSION')))]) 190 | # cmd.extend(['-I', '/opt']) 191 | # cmd.extend(['/opt/lsqlite3.c', '/opt/sqlite3.c']) 192 | cmd.extend(['/opt/compile.c', quote('/lua-{}/src/liblua.a'.format(os.environ.get('LUA_VERSION')))]) 193 | cmd.extend(['/opt/lsqlite3.c', '/opt/sqlite3.c']) 194 | 195 | # cmd.extend(['-I', '/opt']) 196 | cmd.extend([quote(v.filepath) for v in link_libraries]) 197 | cmd.extend(['-s', 'EXPORTED_FUNCTIONS=["_malloc"]']) 198 | 199 | cmd.extend(['-lm', '-ldl', '-o', definition.get_output_file(), '-s', 'EXPORTED_RUNTIME_METHODS=["cwrap"]']) 200 | print('Compile command is {}'.format(' '.join(cmd))) 201 | shell_exec(*cmd) 202 | 203 | # add metering library 204 | # meter_cmd = ['node', '/opt/node/apply-metering.cjs'] 205 | # shell_exec(*meter_cmd) 206 | shell_exec(*['rm', '/src/process.js']) 207 | 208 | if __name__ == '__main__': 209 | main() 210 | 211 | # cleanup temporary module directory 212 | if os.path.isdir(LUAROCKS_LOCAL_MODULE_DIR): 213 | shutil.rmtree(LUAROCKS_LOCAL_MODULE_DIR) -------------------------------------------------------------------------------- /container/src/emcc_lua_lib/definition.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import yaml 4 | from .helper import shell_exec, debug_print 5 | 6 | class Definition(): 7 | dependencies = [] 8 | functions = [] 9 | entry_file = '' 10 | output_file = '' 11 | extra_compile_arguments = {} 12 | pre_js = '' 13 | 14 | def __init__(self, definition_file): 15 | if not os.path.isfile(definition_file): 16 | print('{} does not exist. need to place it'.format(definition_file)) 17 | sys.exit(1) 18 | 19 | with open(definition_file, mode='r') as definition: 20 | data = yaml.safe_load(definition) 21 | self.dependencies = data.get('dependencies', []) 22 | self.functions = data.get('functions', []) 23 | self.entry_file = data.get('entry_file', '') 24 | self.output_file = data.get('output_file', '') 25 | self.extra_compile_arguments = { 26 | **data.get('extra_args', {}) 27 | } 28 | self.extra_compile_sqlite_arguments = [ 29 | 'SQLITE_ENABLE_MATH_FUNCTIONS', 30 | 'SQLITE_ENABLE_GEOPOLY', 31 | 'SQLITE_ENABLE_FTS5', 32 | 'SQLITE_ENABLE_API_ARMOR', 33 | ] 34 | self.output_file = data.get('output_file', '') 35 | 36 | self.pre_js = data.get('pre_js', '') 37 | 38 | def get_entry_file(self): 39 | if not self.entry_file: 40 | return os.path.join(os.getcwd(), 'main.lua') 41 | 42 | return os.path.abspath(self.entry_file) 43 | 44 | def get_extra_args(self): 45 | args = [] 46 | for key, val in self.extra_compile_arguments.items(): 47 | args.extend(['-s', '{}={}'.format(key, val)]) 48 | for key in self.extra_compile_sqlite_arguments: 49 | args.extend(['-D{}'.format(key)]) 50 | 51 | if self.pre_js: 52 | if not os.path.isfile(self.pre_js): 53 | print('pre_js: {} does not exist. need to place it'.format(self.pre_js)) 54 | sys.exit(1) 55 | 56 | args.extend(['--pre-js', self.pre_js]) 57 | 58 | return args 59 | 60 | 61 | def get_output_file(self): 62 | if self.output_file: 63 | return self.output_file 64 | 65 | return '{}.html'.format(os.path.splitext(os.path.basename(self.entry_file))[0]) 66 | 67 | 68 | def install_dependencies(self, local_module_dir): 69 | for mod in self.dependencies: 70 | print('Install module {} via luarocks...'.format(mod)) 71 | # install locally 72 | shell_exec('luarocks', '--tree={}'.format(local_module_dir), '--deps-mode=one', 'install', mod) 73 | 74 | 75 | def make_function_delarations(self): 76 | template = ''' 77 | EMSCRIPTEN_KEEPALIVE 78 | {} {}({}) {{ 79 | if (wasm_lua_state == NULL) {{ 80 | wasm_lua_state = luaL_newstate(); 81 | boot_lua(wasm_lua_state); 82 | }} 83 | // Push arguments 84 | lua_getglobal(wasm_lua_state, "{}"); 85 | if (!lua_isfunction(wasm_lua_state, -1)) {{ 86 | printf("function {} is not defined globaly in lua runtime\\n"); 87 | lua_settop(wasm_lua_state, 0); 88 | {} 89 | }} 90 | {} 91 | 92 | // Call lua function 93 | if (lua_pcall(wasm_lua_state, {}, 1, 0)) {{ 94 | printf("failed to call {} function\\n"); 95 | printf("error: %s\\n", lua_tostring(wasm_lua_state, -1)); 96 | lua_settop(wasm_lua_state, 0); 97 | {} 98 | }} 99 | // Handle return values 100 | {} 101 | }} 102 | ''' 103 | wasm_functions = [] 104 | for name, config in self.functions.items(): 105 | arguments = [] 106 | push_arguments = [] 107 | return_type = config.get('return', '') 108 | for i, arg in enumerate(config.get('args', [])): 109 | if arg == 'int': 110 | arguments.append('int arg_{}'.format(i)) 111 | push_arguments.append(' lua_pushnumber(wasm_lua_state, arg_{});'.format(i)) 112 | elif arg == 'string': 113 | arguments.append('const char* arg_{}'.format(i)) 114 | push_arguments.append(' lua_pushstring(wasm_lua_state, arg_{});'.format(i)) 115 | 116 | failed_return_value = '' 117 | capture_return_value = '' 118 | if return_type == 'int': 119 | failed_return_value = 'return 0;' 120 | capture_return_value = ''' if (lua_isinteger(wasm_lua_state, -1)) { 121 | int return_value = lua_tointeger(wasm_lua_state, -1); 122 | lua_settop(wasm_lua_state, 0); 123 | return return_value; 124 | } 125 | return 0;''' 126 | elif return_type == 'string': 127 | failed_return_value = 'return "";' 128 | capture_return_value = ''' if (lua_isstring(wasm_lua_state, -1)) { 129 | const char* return_value = lua_tostring(wasm_lua_state, -1); 130 | lua_settop(wasm_lua_state, 0); 131 | return return_value; 132 | } 133 | return "";''' 134 | return_type = 'const char* ' 135 | 136 | function = template.format( 137 | return_type, 138 | name, 139 | ', '.join(arguments), 140 | name, 141 | name, 142 | failed_return_value, 143 | '\n'.join(push_arguments), 144 | len(push_arguments), 145 | name, 146 | failed_return_value, 147 | capture_return_value) 148 | debug_print(function) 149 | wasm_functions.append(function) 150 | 151 | return '\n'.join(wasm_functions) 152 | -------------------------------------------------------------------------------- /container/src/emcc_lua_lib/file.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | class LuaFile(): 4 | def __init__(self, filepath, basename=None): 5 | self.filepath = filepath 6 | if basename: 7 | self.basename = basename 8 | else: 9 | self.basename = os.path.basename(filepath) 10 | 11 | module_name = os.path.splitext(self.basename)[0].replace('/', '.') 12 | if module_name.startswith('.src'): 13 | module_name = module_name.replace('.src', '', 1) 14 | self.module_name = module_name 15 | 16 | 17 | class ModuleFile(): 18 | def __init__(self, filepath, luaopen_name): 19 | self.filepath = filepath 20 | self.module_name = luaopen_name 21 | self.basename = luaopen_name.replace('_', '.') 22 | 23 | 24 | class BundleFile(LuaFile): 25 | def __init__(self, filepath): 26 | super().__init__(filepath) 27 | self.filepath = os.path.relpath(filepath) 28 | -------------------------------------------------------------------------------- /container/src/emcc_lua_lib/helper.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | 4 | IS_DEBUG = os.environ.get('DEBUG', '') 5 | 6 | def encode_hex_literals(source): 7 | return ', '.join([r'0x{:02x}'.format(x) for x in source.encode('utf-8')]) 8 | 9 | 10 | def get_extention(file): 11 | basename = os.path.basename(file) 12 | return os.path.splitext(basename)[1][1:] 13 | 14 | 15 | def is_lua_source_file(file): 16 | ext = get_extention(file) 17 | return ext == 'lua' or ext == 'luac' 18 | 19 | 20 | def is_binary_library(file): 21 | ext = get_extention(file) 22 | return ext == 'o' or ext == 'a' or ext == 'so' or ext == 'dylib' 23 | 24 | 25 | def shell_exec(*cmd_args): 26 | proc = subprocess.run(list(cmd_args), stdout=subprocess.PIPE) 27 | return proc.stdout.decode('utf-8').strip('\n'), proc.returncode 28 | 29 | def debug_print(message): 30 | if IS_DEBUG: 31 | print(message) 32 | -------------------------------------------------------------------------------- /container/src/json.lua: -------------------------------------------------------------------------------- 1 | local json = {_version = "0.2.0"} 2 | 3 | ------------------------------------------------------------------------------- 4 | -- Encode 5 | ------------------------------------------------------------------------------- 6 | 7 | local encode 8 | 9 | local escape_char_map = { 10 | ["\\"] = "\\", 11 | ["\""] = "\"", 12 | ["\b"] = "b", 13 | ["\f"] = "f", 14 | ["\n"] = "n", 15 | ["\r"] = "r", 16 | ["\t"] = "t" 17 | } 18 | 19 | local escape_char_map_inv = {["/"] = "/"} 20 | for k, v in pairs(escape_char_map) do escape_char_map_inv[v] = k end 21 | 22 | local function escape_char(c) 23 | return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte())) 24 | end 25 | 26 | local function encode_nil(val) return "null" end 27 | 28 | local function encode_table(val, stack) 29 | local res = {} 30 | stack = stack or {} 31 | 32 | -- Circular reference? 33 | if stack[val] then error("circular reference") end 34 | 35 | stack[val] = true 36 | 37 | if rawget(val, 1) ~= nil or next(val) == nil then 38 | -- Treat as array -- check keys are valid and it is not sparse 39 | local n = 0 40 | for k in pairs(val) do 41 | if type(k) ~= "number" then 42 | error("invalid table: mixed or invalid key types") 43 | end 44 | n = n + 1 45 | end 46 | if n ~= #val then error("invalid table: sparse array") end 47 | -- Encode 48 | for i = 1, #val do res[i] = encode(val[i], stack) end 49 | stack[val] = nil 50 | return "[" .. table.concat(res, ",") .. "]" 51 | else 52 | -- Treat as an object 53 | local i = 1 54 | for k, v in pairs(val) do 55 | if type(k) ~= "string" then 56 | error("invalid table: mixed or invalid key types") 57 | end 58 | if type(v) ~= "function" then 59 | res[i] = encode(k, stack) .. ":" .. encode(v, stack) 60 | i = i + 1 61 | end 62 | end 63 | stack[val] = nil 64 | return "{" .. table.concat(res, ",") .. "}" 65 | end 66 | end 67 | 68 | local function encode_string(val) 69 | return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"' 70 | end 71 | 72 | local function encode_number(val) 73 | -- Check for NaN, -inf and inf 74 | if val ~= val or val <= -math.huge or val >= math.huge then 75 | error("unexpected number value '" .. tostring(val) .. "'") 76 | end 77 | return string.format("%.14g", val) 78 | end 79 | 80 | local type_func_map = { 81 | ["nil"] = encode_nil, 82 | ["table"] = encode_table, 83 | ["string"] = encode_string, 84 | ["number"] = encode_number, 85 | ["boolean"] = tostring 86 | } 87 | 88 | encode = function(val, stack) 89 | local t = type(val) 90 | local f = type_func_map[t] 91 | if f then return f(val, stack) end 92 | error("unexpected type '" .. t .. "'") 93 | end 94 | 95 | function json.encode(val) return (encode(val)) end 96 | 97 | ------------------------------------------------------------------------------- 98 | -- Decode 99 | ------------------------------------------------------------------------------- 100 | 101 | local parse 102 | 103 | local function create_set(...) 104 | local res = {} 105 | for i = 1, select("#", ...) do res[select(i, ...)] = true end 106 | return res 107 | end 108 | 109 | local space_chars = create_set(" ", "\t", "\r", "\n") 110 | local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",") 111 | local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u") 112 | local literals = create_set("true", "false", "null") 113 | 114 | local literal_map = {["true"] = true, ["false"] = false, ["null"] = nil} 115 | 116 | local function next_char(str, idx, set, negate) 117 | for i = idx, #str do if set[str:sub(i, i)] ~= negate then return i end end 118 | return #str + 1 119 | end 120 | 121 | local function decode_error(str, idx, msg) 122 | local line_count = 1 123 | local col_count = 1 124 | for i = 1, idx - 1 do 125 | col_count = col_count + 1 126 | if str:sub(i, i) == "\n" then 127 | line_count = line_count + 1 128 | col_count = 1 129 | end 130 | end 131 | error(string.format("%s at line %d col %d", msg, line_count, col_count)) 132 | end 133 | 134 | local function codepoint_to_utf8(n) 135 | local f = math.floor 136 | if n <= 0x7f then 137 | return string.char(n) 138 | elseif n <= 0x7ff then 139 | return string.char(f(n / 64) + 192, n % 64 + 128) 140 | elseif n <= 0xffff then 141 | return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, 142 | n % 64 + 128) 143 | elseif n <= 0x10ffff then 144 | return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128, 145 | f(n % 4096 / 64) + 128, n % 64 + 128) 146 | end 147 | error(string.format("invalid unicode codepoint '%x'", n)) 148 | end 149 | 150 | local function parse_unicode_escape(s) 151 | local n1 = tonumber(s:sub(1, 4), 16) 152 | local n2 = tonumber(s:sub(7, 10), 16) 153 | if n2 then 154 | return 155 | codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000) 156 | else 157 | return codepoint_to_utf8(n1) 158 | end 159 | end 160 | 161 | local function parse_string(str, i) 162 | local res = {} 163 | local j = i + 1 164 | local k = j 165 | 166 | while j <= #str do 167 | local x = str:byte(j) 168 | 169 | if x < 32 then 170 | decode_error(str, j, "control character in string") 171 | elseif x == 92 then -- `\`: Escape 172 | res[#res + 1] = str:sub(k, j - 1) 173 | j = j + 1 174 | local c = str:sub(j, j) 175 | if c == "u" then 176 | local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1) or 177 | str:match("^%x%x%x%x", j + 1) or 178 | decode_error(str, j - 1, 179 | "invalid unicode escape in string") 180 | res[#res + 1] = parse_unicode_escape(hex) 181 | j = j + #hex 182 | else 183 | if not escape_chars[c] then 184 | decode_error(str, j - 1, 185 | "invalid escape char '" .. c .. "' in string") 186 | end 187 | res[#res + 1] = escape_char_map_inv[c] 188 | end 189 | k = j + 1 190 | elseif x == 34 then -- `"`: End of string 191 | res[#res + 1] = str:sub(k, j - 1) 192 | return table.concat(res), j + 1 193 | end 194 | j = j + 1 195 | end 196 | 197 | decode_error(str, i, "expected closing quote for string") 198 | end 199 | 200 | local function parse_number(str, i) 201 | local x = next_char(str, i, delim_chars) 202 | local s = str:sub(i, x - 1) 203 | local n = tonumber(s) 204 | if not n then decode_error(str, i, "invalid number '" .. s .. "'") end 205 | return n, x 206 | end 207 | 208 | local function parse_literal(str, i) 209 | local x = next_char(str, i, delim_chars) 210 | local word = str:sub(i, x - 1) 211 | if not literals[word] then 212 | decode_error(str, i, "invalid literal '" .. word .. "'") 213 | end 214 | return literal_map[word], x 215 | end 216 | 217 | local function parse_array(str, i) 218 | local res = {} 219 | local n = 1 220 | i = i + 1 221 | while true do 222 | local x 223 | i = next_char(str, i, space_chars, true) 224 | if str:sub(i, i) == "]" then 225 | i = i + 1 226 | break 227 | end 228 | x, i = parse(str, i) 229 | res[n] = x 230 | n = n + 1 231 | i = next_char(str, i, space_chars, true) 232 | local chr = str:sub(i, i) 233 | i = i + 1 234 | if chr == "]" then break end 235 | if chr ~= "," then decode_error(str, i, "expected ']' or ','") end 236 | end 237 | return res, i 238 | end 239 | 240 | local function parse_object(str, i) 241 | local res = {} 242 | i = i + 1 243 | while true do 244 | local key, val 245 | i = next_char(str, i, space_chars, true) 246 | if str:sub(i, i) == "}" then 247 | i = i + 1 248 | break 249 | end 250 | if str:sub(i, i) ~= '"' then 251 | decode_error(str, i, "expected string for key") 252 | end 253 | key, i = parse(str, i) 254 | i = next_char(str, i, space_chars, true) 255 | if str:sub(i, i) ~= ":" then 256 | decode_error(str, i, "expected ':' after key") 257 | end 258 | i = next_char(str, i + 1, space_chars, true) 259 | val, i = parse(str, i) 260 | res[key] = val 261 | i = next_char(str, i, space_chars, true) 262 | local chr = str:sub(i, i) 263 | i = i + 1 264 | if chr == "}" then break end 265 | if chr ~= "," then decode_error(str, i, "expected '}' or ','") end 266 | end 267 | return res, i 268 | end 269 | 270 | local char_func_map = { 271 | ['"'] = parse_string, 272 | ["0"] = parse_number, 273 | ["1"] = parse_number, 274 | ["2"] = parse_number, 275 | ["3"] = parse_number, 276 | ["4"] = parse_number, 277 | ["5"] = parse_number, 278 | ["6"] = parse_number, 279 | ["7"] = parse_number, 280 | ["8"] = parse_number, 281 | ["9"] = parse_number, 282 | ["-"] = parse_number, 283 | ["t"] = parse_literal, 284 | ["f"] = parse_literal, 285 | ["n"] = parse_literal, 286 | ["["] = parse_array, 287 | ["{"] = parse_object 288 | } 289 | 290 | parse = function(str, idx) 291 | local chr = str:sub(idx, idx) 292 | local f = char_func_map[chr] 293 | if f then return f(str, idx) end 294 | decode_error(str, idx, "unexpected character '" .. chr .. "'") 295 | end 296 | 297 | function json.decode(str) 298 | if type(str) ~= "string" then 299 | error("expected argument of type string, got " .. type(str)) 300 | end 301 | local res, idx = parse(str, next_char(str, 1, space_chars, true)) 302 | idx = next_char(str, idx, space_chars, true) 303 | if idx <= #str then decode_error(str, idx, "trailing garbage") end 304 | return res 305 | end 306 | 307 | return json 308 | -------------------------------------------------------------------------------- /container/src/loader.lua: -------------------------------------------------------------------------------- 1 | local json = require "json" 2 | ao = require "ao" 3 | 4 | function handle(msgJSON, aoJSON) 5 | local process = require ".process" 6 | -- decode inputs 7 | local msg = json.decode(msgJSON) 8 | local env = json.decode(aoJSON) 9 | ao.init(env) 10 | -- relocate custom tags to root message 11 | msg = ao.normalize(msg) 12 | -- handle process 13 | -- 14 | -- The process may throw an error, either intentionally or unintentionally 15 | -- So we need to be able to catch these unhandled errors and bubble them 16 | -- across the interop with some indication that it was unhandled 17 | -- 18 | -- To do this, we wrap the process.handle with pcall(), and return both the status 19 | -- and response as JSON. The caller can examine the status boolean and decide how to 20 | -- handle the error 21 | -- 22 | -- See pcall https://www.lua.org/pil/8.4.html 23 | local status, response = pcall(function() 24 | return (process.handle(msg, ao)) 25 | end) 26 | 27 | -- encode output 28 | local responseJSON = json.encode({ok = status, response = response}) 29 | return responseJSON 30 | end -------------------------------------------------------------------------------- /container/src/lsqlite3.c: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * lsqlite3 * 3 | * Copyright (C) 2002-2016 Tiago Dionizio, Doug Currie * 4 | * All rights reserved. * 5 | * Author : Tiago Dionizio * 6 | * Author : Doug Currie * 7 | * Library : lsqlite3 - an SQLite 3 database binding for Lua 5 * 8 | * * 9 | * Permission is hereby granted, free of charge, to any person obtaining * 10 | * a copy of this software and associated documentation files (the * 11 | * "Software"), to deal in the Software without restriction, including * 12 | * without limitation the rights to use, copy, modify, merge, publish, * 13 | * distribute, sublicense, and/or sell copies of the Software, and to * 14 | * permit persons to whom the Software is furnished to do so, subject to * 15 | * the following conditions: * 16 | * * 17 | * The above copyright notice and this permission notice shall be * 18 | * included in all copies or substantial portions of the Software. * 19 | * * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.* 23 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * 24 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * 25 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * 26 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 27 | ************************************************************************/ 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | #define LUA_LIB 34 | #include "lua.h" 35 | #include "lauxlib.h" 36 | 37 | #if LUA_VERSION_NUM > 501 38 | /* 39 | ** Lua 5.2 40 | */ 41 | #ifndef lua_strlen 42 | #define lua_strlen lua_rawlen 43 | #endif 44 | /* luaL_typerror always used with arg at ndx == NULL */ 45 | #define luaL_typerror(L,ndx,str) luaL_error(L,"bad argument %d (%s expected, got nil)",ndx,str) 46 | /* luaL_register used once, so below expansion is OK for this case */ 47 | #define luaL_register(L,name,reg) lua_newtable(L);luaL_setfuncs(L,reg,0) 48 | /* luaL_openlib always used with name == NULL */ 49 | #define luaL_openlib(L,name,reg,nup) luaL_setfuncs(L,reg,nup) 50 | 51 | #if LUA_VERSION_NUM > 502 52 | /* 53 | ** Lua 5.3 54 | */ 55 | #define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n))) 56 | #endif 57 | #endif 58 | 59 | #include "sqlite3.h" 60 | 61 | /* compile time features */ 62 | #if !defined(SQLITE_OMIT_PROGRESS_CALLBACK) 63 | #define SQLITE_OMIT_PROGRESS_CALLBACK 0 64 | #endif 65 | #if !defined(LSQLITE_OMIT_UPDATE_HOOK) 66 | #define LSQLITE_OMIT_UPDATE_HOOK 0 67 | #endif 68 | #if defined(LSQLITE_OMIT_OPEN_V2) 69 | #define SQLITE3_OPEN(L,filename,flags) sqlite3_open(L,filename) 70 | #else 71 | #define SQLITE3_OPEN(L,filename,flags) sqlite3_open_v2(L,filename,flags,NULL) 72 | #endif 73 | 74 | typedef struct sdb sdb; 75 | typedef struct sdb_vm sdb_vm; 76 | typedef struct sdb_bu sdb_bu; 77 | typedef struct sdb_func sdb_func; 78 | 79 | /* to use as C user data so i know what function sqlite is calling */ 80 | struct sdb_func { 81 | /* references to associated lua values */ 82 | int fn_step; 83 | int fn_finalize; 84 | int udata; 85 | 86 | sdb *db; 87 | char aggregate; 88 | 89 | sdb_func *next; 90 | }; 91 | 92 | /* information about database */ 93 | struct sdb { 94 | /* associated lua state */ 95 | lua_State *L; 96 | /* sqlite database handle */ 97 | sqlite3 *db; 98 | 99 | /* sql functions stack usage */ 100 | sdb_func *func; /* top SQL function being called */ 101 | 102 | /* references */ 103 | int busy_cb; /* busy callback */ 104 | int busy_udata; 105 | 106 | int progress_cb; /* progress handler */ 107 | int progress_udata; 108 | 109 | int trace_cb; /* trace callback */ 110 | int trace_udata; 111 | 112 | #if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK 113 | 114 | int update_hook_cb; /* update_hook callback */ 115 | int update_hook_udata; 116 | 117 | int commit_hook_cb; /* commit_hook callback */ 118 | int commit_hook_udata; 119 | 120 | int rollback_hook_cb; /* rollback_hook callback */ 121 | int rollback_hook_udata; 122 | 123 | #endif 124 | }; 125 | 126 | static const char *sqlite_meta = ":sqlite3"; 127 | static const char *sqlite_vm_meta = ":sqlite3:vm"; 128 | static const char *sqlite_bu_meta = ":sqlite3:bu"; 129 | static const char *sqlite_ctx_meta = ":sqlite3:ctx"; 130 | static int sqlite_ctx_meta_ref; 131 | 132 | /* Lua 5.3 introduced an integer type, but depending on the implementation, it could be 32 133 | ** or 64 bits (or something else?). This helper macro tries to do "the right thing." 134 | */ 135 | 136 | #if LUA_VERSION_NUM > 502 137 | #define PUSH_INT64(L,i64in,fallback) \ 138 | do { \ 139 | sqlite_int64 i64 = i64in; \ 140 | lua_Integer i = (lua_Integer )i64; \ 141 | if (i == i64) lua_pushinteger(L, i);\ 142 | else { \ 143 | lua_Number n = (lua_Number)i64; \ 144 | if (n == i64) lua_pushnumber(L, n); \ 145 | else fallback; \ 146 | } \ 147 | } while (0) 148 | #else 149 | #define PUSH_INT64(L,i64in,fallback) \ 150 | do { \ 151 | sqlite_int64 i64 = i64in; \ 152 | lua_Number n = (lua_Number)i64; \ 153 | if (n == i64) lua_pushnumber(L, n); \ 154 | else fallback; \ 155 | } while (0) 156 | #endif 157 | 158 | /* 159 | ** ======================================================= 160 | ** Database Virtual Machine Operations 161 | ** ======================================================= 162 | */ 163 | 164 | static void vm_push_column(lua_State *L, sqlite3_stmt *vm, int idx) { 165 | switch (sqlite3_column_type(vm, idx)) { 166 | case SQLITE_INTEGER: 167 | PUSH_INT64(L, sqlite3_column_int64(vm, idx) 168 | , lua_pushlstring(L, (const char*)sqlite3_column_text(vm, idx) 169 | , sqlite3_column_bytes(vm, idx))); 170 | break; 171 | case SQLITE_FLOAT: 172 | lua_pushnumber(L, sqlite3_column_double(vm, idx)); 173 | break; 174 | case SQLITE_TEXT: 175 | lua_pushlstring(L, (const char*)sqlite3_column_text(vm, idx), sqlite3_column_bytes(vm, idx)); 176 | break; 177 | case SQLITE_BLOB: 178 | lua_pushlstring(L, sqlite3_column_blob(vm, idx), sqlite3_column_bytes(vm, idx)); 179 | break; 180 | case SQLITE_NULL: 181 | lua_pushnil(L); 182 | break; 183 | default: 184 | lua_pushnil(L); 185 | break; 186 | } 187 | } 188 | 189 | /* virtual machine information */ 190 | struct sdb_vm { 191 | sdb *db; /* associated database handle */ 192 | sqlite3_stmt *vm; /* virtual machine */ 193 | 194 | /* sqlite3_step info */ 195 | int columns; /* number of columns in result */ 196 | char has_values; /* true when step succeeds */ 197 | 198 | char temp; /* temporary vm used in db:rows */ 199 | }; 200 | 201 | /* called with db,sql text on the lua stack */ 202 | static sdb_vm *newvm(lua_State *L, sdb *db) { 203 | sdb_vm *svm = (sdb_vm*)lua_newuserdata(L, sizeof(sdb_vm)); /* db sql svm_ud -- */ 204 | 205 | luaL_getmetatable(L, sqlite_vm_meta); 206 | lua_setmetatable(L, -2); /* set metatable */ 207 | 208 | svm->db = db; 209 | svm->columns = 0; 210 | svm->has_values = 0; 211 | svm->vm = NULL; 212 | svm->temp = 0; 213 | 214 | /* add an entry on the database table: svm -> db to keep db live while svm is live */ 215 | lua_pushlightuserdata(L, db); /* db sql svm_ud db_lud -- */ 216 | lua_rawget(L, LUA_REGISTRYINDEX); /* db sql svm_ud reg[db_lud] -- */ 217 | lua_pushlightuserdata(L, svm); /* db sql svm_ud reg[db_lud] svm_lud -- */ 218 | lua_pushvalue(L, -5); /* db sql svm_ud reg[db_lud] svm_lud db -- */ 219 | lua_rawset(L, -3); /* (reg[db_lud])[svm_lud] = db ; set the db for this vm */ 220 | lua_pop(L, 1); /* db sql svm_ud -- */ 221 | 222 | return svm; 223 | } 224 | 225 | static int cleanupvm(lua_State *L, sdb_vm *svm) { 226 | 227 | /* remove entry in database table - no harm if not present in the table */ 228 | lua_pushlightuserdata(L, svm->db); 229 | lua_rawget(L, LUA_REGISTRYINDEX); 230 | lua_pushlightuserdata(L, svm); 231 | lua_pushnil(L); 232 | lua_rawset(L, -3); 233 | lua_pop(L, 1); 234 | 235 | svm->columns = 0; 236 | svm->has_values = 0; 237 | 238 | if (!svm->vm) return 0; 239 | 240 | lua_pushinteger(L, sqlite3_finalize(svm->vm)); 241 | svm->vm = NULL; 242 | return 1; 243 | } 244 | 245 | static int stepvm(lua_State *L, sdb_vm *svm) { 246 | (void)L; 247 | return sqlite3_step(svm->vm); 248 | } 249 | 250 | static sdb_vm *lsqlite_getvm(lua_State *L, int index) { 251 | sdb_vm *svm = (sdb_vm*)luaL_checkudata(L, index, sqlite_vm_meta); 252 | if (svm == NULL) luaL_argerror(L, index, "bad sqlite virtual machine"); 253 | return svm; 254 | } 255 | 256 | static sdb_vm *lsqlite_checkvm(lua_State *L, int index) { 257 | sdb_vm *svm = lsqlite_getvm(L, index); 258 | if (svm->vm == NULL) luaL_argerror(L, index, "attempt to use closed sqlite virtual machine"); 259 | return svm; 260 | } 261 | 262 | static int dbvm_isopen(lua_State *L) { 263 | sdb_vm *svm = lsqlite_getvm(L, 1); 264 | lua_pushboolean(L, svm->vm != NULL ? 1 : 0); 265 | return 1; 266 | } 267 | 268 | static int dbvm_tostring(lua_State *L) { 269 | char buff[39]; 270 | sdb_vm *svm = lsqlite_getvm(L, 1); 271 | if (svm->vm == NULL) 272 | strcpy(buff, "closed"); 273 | else 274 | sprintf(buff, "%p", svm); 275 | lua_pushfstring(L, "sqlite virtual machine (%s)", buff); 276 | return 1; 277 | } 278 | 279 | static int dbvm_gc(lua_State *L) { 280 | sdb_vm *svm = lsqlite_getvm(L, 1); 281 | if (svm->vm != NULL) /* ignore closed vms */ 282 | cleanupvm(L, svm); 283 | return 0; 284 | } 285 | 286 | static int dbvm_step(lua_State *L) { 287 | int result; 288 | sdb_vm *svm = lsqlite_checkvm(L, 1); 289 | 290 | result = stepvm(L, svm); 291 | svm->has_values = result == SQLITE_ROW ? 1 : 0; 292 | svm->columns = sqlite3_data_count(svm->vm); 293 | 294 | lua_pushinteger(L, result); 295 | return 1; 296 | } 297 | 298 | static int dbvm_finalize(lua_State *L) { 299 | sdb_vm *svm = lsqlite_checkvm(L, 1); 300 | return cleanupvm(L, svm); 301 | } 302 | 303 | static int dbvm_reset(lua_State *L) { 304 | sdb_vm *svm = lsqlite_checkvm(L, 1); 305 | sqlite3_reset(svm->vm); 306 | lua_pushinteger(L, sqlite3_errcode(svm->db->db)); 307 | return 1; 308 | } 309 | 310 | static void dbvm_check_contents(lua_State *L, sdb_vm *svm) { 311 | if (!svm->has_values) { 312 | luaL_error(L, "misuse of function"); 313 | } 314 | } 315 | 316 | static void dbvm_check_index(lua_State *L, sdb_vm *svm, int index) { 317 | if (index < 0 || index >= svm->columns) { 318 | luaL_error(L, "index out of range [0..%d]", svm->columns - 1); 319 | } 320 | } 321 | 322 | static void dbvm_check_bind_index(lua_State *L, sdb_vm *svm, int index) { 323 | if (index < 1 || index > sqlite3_bind_parameter_count(svm->vm)) { 324 | luaL_error(L, "bind index out of range [1..%d]", sqlite3_bind_parameter_count(svm->vm)); 325 | } 326 | } 327 | 328 | static int dbvm_last_insert_rowid(lua_State *L) { 329 | sdb_vm *svm = lsqlite_checkvm(L, 1); 330 | /* conversion warning: int64 -> luaNumber */ 331 | sqlite_int64 rowid = sqlite3_last_insert_rowid(svm->db->db); 332 | PUSH_INT64(L, rowid, lua_pushfstring(L, "%ll", rowid)); 333 | return 1; 334 | } 335 | 336 | /* 337 | ** ======================================================= 338 | ** Virtual Machine - generic info 339 | ** ======================================================= 340 | */ 341 | static int dbvm_columns(lua_State *L) { 342 | sdb_vm *svm = lsqlite_checkvm(L, 1); 343 | lua_pushinteger(L, sqlite3_column_count(svm->vm)); 344 | return 1; 345 | } 346 | 347 | /* 348 | ** ======================================================= 349 | ** Virtual Machine - getters 350 | ** ======================================================= 351 | */ 352 | 353 | static int dbvm_get_value(lua_State *L) { 354 | sdb_vm *svm = lsqlite_checkvm(L, 1); 355 | int index = luaL_checkint(L, 2); 356 | dbvm_check_contents(L, svm); 357 | dbvm_check_index(L, svm, index); 358 | vm_push_column(L, svm->vm, index); 359 | return 1; 360 | } 361 | 362 | static int dbvm_get_name(lua_State *L) { 363 | sdb_vm *svm = lsqlite_checkvm(L, 1); 364 | int index = luaL_checknumber(L, 2); 365 | dbvm_check_index(L, svm, index); 366 | lua_pushstring(L, sqlite3_column_name(svm->vm, index)); 367 | return 1; 368 | } 369 | 370 | static int dbvm_get_type(lua_State *L) { 371 | sdb_vm *svm = lsqlite_checkvm(L, 1); 372 | int index = luaL_checknumber(L, 2); 373 | dbvm_check_index(L, svm, index); 374 | lua_pushstring(L, sqlite3_column_decltype(svm->vm, index)); 375 | return 1; 376 | } 377 | 378 | static int dbvm_get_values(lua_State *L) { 379 | sdb_vm *svm = lsqlite_checkvm(L, 1); 380 | sqlite3_stmt *vm = svm->vm; 381 | int columns = svm->columns; 382 | int n; 383 | dbvm_check_contents(L, svm); 384 | 385 | lua_createtable(L, columns, 0); 386 | for (n = 0; n < columns;) { 387 | vm_push_column(L, vm, n++); 388 | lua_rawseti(L, -2, n); 389 | } 390 | return 1; 391 | } 392 | 393 | static int dbvm_get_names(lua_State *L) { 394 | sdb_vm *svm = lsqlite_checkvm(L, 1); 395 | sqlite3_stmt *vm = svm->vm; 396 | int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */ 397 | int n; 398 | 399 | lua_createtable(L, columns, 0); 400 | for (n = 0; n < columns;) { 401 | lua_pushstring(L, sqlite3_column_name(vm, n++)); 402 | lua_rawseti(L, -2, n); 403 | } 404 | return 1; 405 | } 406 | 407 | static int dbvm_get_types(lua_State *L) { 408 | sdb_vm *svm = lsqlite_checkvm(L, 1); 409 | sqlite3_stmt *vm = svm->vm; 410 | int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */ 411 | int n; 412 | 413 | lua_createtable(L, columns, 0); 414 | for (n = 0; n < columns;) { 415 | lua_pushstring(L, sqlite3_column_decltype(vm, n++)); 416 | lua_rawseti(L, -2, n); 417 | } 418 | return 1; 419 | } 420 | 421 | static int dbvm_get_uvalues(lua_State *L) { 422 | sdb_vm *svm = lsqlite_checkvm(L, 1); 423 | sqlite3_stmt *vm = svm->vm; 424 | int columns = svm->columns; 425 | int n; 426 | dbvm_check_contents(L, svm); 427 | 428 | lua_checkstack(L, columns); 429 | for (n = 0; n < columns; ++n) 430 | vm_push_column(L, vm, n); 431 | return columns; 432 | } 433 | 434 | static int dbvm_get_unames(lua_State *L) { 435 | sdb_vm *svm = lsqlite_checkvm(L, 1); 436 | sqlite3_stmt *vm = svm->vm; 437 | int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */ 438 | int n; 439 | 440 | lua_checkstack(L, columns); 441 | for (n = 0; n < columns; ++n) 442 | lua_pushstring(L, sqlite3_column_name(vm, n)); 443 | return columns; 444 | } 445 | 446 | static int dbvm_get_utypes(lua_State *L) { 447 | sdb_vm *svm = lsqlite_checkvm(L, 1); 448 | sqlite3_stmt *vm = svm->vm; 449 | int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */ 450 | int n; 451 | 452 | lua_checkstack(L, columns); 453 | for (n = 0; n < columns; ++n) 454 | lua_pushstring(L, sqlite3_column_decltype(vm, n)); 455 | return columns; 456 | } 457 | 458 | static int dbvm_get_named_values(lua_State *L) { 459 | sdb_vm *svm = lsqlite_checkvm(L, 1); 460 | sqlite3_stmt *vm = svm->vm; 461 | int columns = svm->columns; 462 | int n; 463 | dbvm_check_contents(L, svm); 464 | 465 | lua_createtable(L, 0, columns); 466 | for (n = 0; n < columns; ++n) { 467 | lua_pushstring(L, sqlite3_column_name(vm, n)); 468 | vm_push_column(L, vm, n); 469 | lua_rawset(L, -3); 470 | } 471 | return 1; 472 | } 473 | 474 | static int dbvm_get_named_types(lua_State *L) { 475 | sdb_vm *svm = lsqlite_checkvm(L, 1); 476 | sqlite3_stmt *vm = svm->vm; 477 | int columns = sqlite3_column_count(vm); 478 | int n; 479 | 480 | lua_createtable(L, 0, columns); 481 | for (n = 0; n < columns; ++n) { 482 | lua_pushstring(L, sqlite3_column_name(vm, n)); 483 | lua_pushstring(L, sqlite3_column_decltype(vm, n)); 484 | lua_rawset(L, -3); 485 | } 486 | return 1; 487 | } 488 | 489 | /* 490 | ** ======================================================= 491 | ** Virtual Machine - Bind 492 | ** ======================================================= 493 | */ 494 | 495 | static int dbvm_bind_index(lua_State *L, sqlite3_stmt *vm, int index, int lindex) { 496 | switch (lua_type(L, lindex)) { 497 | case LUA_TSTRING: 498 | return sqlite3_bind_text(vm, index, lua_tostring(L, lindex), lua_strlen(L, lindex), SQLITE_TRANSIENT); 499 | case LUA_TNUMBER: 500 | #if LUA_VERSION_NUM > 502 501 | if (lua_isinteger(L, lindex)) 502 | return sqlite3_bind_int64(vm, index, lua_tointeger(L, lindex)); 503 | #endif 504 | return sqlite3_bind_double(vm, index, lua_tonumber(L, lindex)); 505 | case LUA_TBOOLEAN: 506 | return sqlite3_bind_int(vm, index, lua_toboolean(L, lindex) ? 1 : 0); 507 | case LUA_TNONE: 508 | case LUA_TNIL: 509 | return sqlite3_bind_null(vm, index); 510 | default: 511 | luaL_error(L, "index (%d) - invalid data type for bind (%s)", index, lua_typename(L, lua_type(L, lindex))); 512 | return SQLITE_MISUSE; /*!*/ 513 | } 514 | } 515 | 516 | 517 | static int dbvm_bind_parameter_count(lua_State *L) { 518 | sdb_vm *svm = lsqlite_checkvm(L, 1); 519 | lua_pushinteger(L, sqlite3_bind_parameter_count(svm->vm)); 520 | return 1; 521 | } 522 | 523 | static int dbvm_bind_parameter_name(lua_State *L) { 524 | sdb_vm *svm = lsqlite_checkvm(L, 1); 525 | int index = luaL_checknumber(L, 2); 526 | dbvm_check_bind_index(L, svm, index); 527 | lua_pushstring(L, sqlite3_bind_parameter_name(svm->vm, index)); 528 | return 1; 529 | } 530 | 531 | static int dbvm_bind(lua_State *L) { 532 | sdb_vm *svm = lsqlite_checkvm(L, 1); 533 | sqlite3_stmt *vm = svm->vm; 534 | int index = luaL_checkint(L, 2); 535 | int result; 536 | 537 | dbvm_check_bind_index(L, svm, index); 538 | result = dbvm_bind_index(L, vm, index, 3); 539 | 540 | lua_pushinteger(L, result); 541 | return 1; 542 | } 543 | 544 | static int dbvm_bind_blob(lua_State *L) { 545 | sdb_vm *svm = lsqlite_checkvm(L, 1); 546 | int index = luaL_checkint(L, 2); 547 | const char *value = luaL_checkstring(L, 3); 548 | int len = lua_strlen(L, 3); 549 | 550 | lua_pushinteger(L, sqlite3_bind_blob(svm->vm, index, value, len, SQLITE_TRANSIENT)); 551 | return 1; 552 | } 553 | 554 | static int dbvm_bind_values(lua_State *L) { 555 | sdb_vm *svm = lsqlite_checkvm(L, 1); 556 | sqlite3_stmt *vm = svm->vm; 557 | int top = lua_gettop(L); 558 | int result, n; 559 | 560 | if (top - 1 != sqlite3_bind_parameter_count(vm)) 561 | luaL_error(L, 562 | "incorrect number of parameters to bind (%d given, %d to bind)", 563 | top - 1, 564 | sqlite3_bind_parameter_count(vm) 565 | ); 566 | 567 | for (n = 2; n <= top; ++n) { 568 | if ((result = dbvm_bind_index(L, vm, n - 1, n)) != SQLITE_OK) { 569 | lua_pushinteger(L, result); 570 | return 1; 571 | } 572 | } 573 | 574 | lua_pushinteger(L, SQLITE_OK); 575 | return 1; 576 | } 577 | 578 | static int dbvm_bind_table_fields (lua_State *L, int idx, int n, sqlite3_stmt *vm) { 579 | const char *name; 580 | int result, i; 581 | 582 | for ( i = 1; i <= n; ++i ) { 583 | name = sqlite3_bind_parameter_name(vm, i ); 584 | if (name && (name[0] == ':' || name[0] == '$')) { 585 | lua_pushstring(L, ++name); 586 | lua_gettable(L, idx); 587 | result = dbvm_bind_index(L, vm, i, -1); 588 | lua_pop(L, 1); 589 | } 590 | else { 591 | lua_pushinteger(L, i ); 592 | lua_gettable(L, idx); 593 | result = dbvm_bind_index(L, vm, i, -1); 594 | lua_pop(L, 1); 595 | } 596 | 597 | if (result != SQLITE_OK) { 598 | return result; 599 | } 600 | } 601 | return SQLITE_OK; 602 | } 603 | 604 | static int dbvm_bind_names(lua_State *L) { 605 | sdb_vm *svm = lsqlite_checkvm(L, 1); 606 | sqlite3_stmt *vm = svm->vm; 607 | int count = sqlite3_bind_parameter_count(vm); 608 | int result; 609 | luaL_checktype(L, 2, LUA_TTABLE); 610 | 611 | result = dbvm_bind_table_fields (L, 2, count, vm); 612 | lua_pushinteger(L, result); 613 | return 1; 614 | } 615 | 616 | /* 617 | ** ======================================================= 618 | ** Database (internal management) 619 | ** ======================================================= 620 | */ 621 | 622 | /* 623 | ** When creating database handles, always creates a `closed' database handle 624 | ** before opening the actual database; so, if there is a memory error, the 625 | ** database is not left opened. 626 | ** 627 | ** Creates a new 'table' and leaves it in the stack 628 | */ 629 | static sdb *newdb (lua_State *L) { 630 | sdb *db = (sdb*)lua_newuserdata(L, sizeof(sdb)); 631 | db->L = L; 632 | db->db = NULL; /* database handle is currently `closed' */ 633 | db->func = NULL; 634 | 635 | db->busy_cb = 636 | db->busy_udata = 637 | db->progress_cb = 638 | db->progress_udata = 639 | db->trace_cb = 640 | db->trace_udata = 641 | #if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK 642 | db->update_hook_cb = 643 | db->update_hook_udata = 644 | db->commit_hook_cb = 645 | db->commit_hook_udata = 646 | db->rollback_hook_cb = 647 | db->rollback_hook_udata = 648 | #endif 649 | LUA_NOREF; 650 | 651 | luaL_getmetatable(L, sqlite_meta); 652 | lua_setmetatable(L, -2); /* set metatable */ 653 | 654 | /* to keep track of 'open' virtual machines */ 655 | lua_pushlightuserdata(L, db); 656 | lua_newtable(L); 657 | lua_rawset(L, LUA_REGISTRYINDEX); 658 | 659 | return db; 660 | } 661 | 662 | static int cleanupdb(lua_State *L, sdb *db) { 663 | sdb_func *func; 664 | sdb_func *func_next; 665 | int top; 666 | int result; 667 | 668 | /* free associated virtual machines */ 669 | lua_pushlightuserdata(L, db); 670 | lua_rawget(L, LUA_REGISTRYINDEX); 671 | 672 | /* close all used handles */ 673 | top = lua_gettop(L); 674 | lua_pushnil(L); 675 | while (lua_next(L, -2)) { 676 | sdb_vm *svm = lua_touserdata(L, -2); /* key: vm; val: sql text */ 677 | cleanupvm(L, svm); 678 | 679 | lua_settop(L, top); 680 | lua_pushnil(L); 681 | } 682 | 683 | lua_pop(L, 1); /* pop vm table */ 684 | 685 | /* remove entry in lua registry table */ 686 | lua_pushlightuserdata(L, db); 687 | lua_pushnil(L); 688 | lua_rawset(L, LUA_REGISTRYINDEX); 689 | 690 | /* 'free' all references */ 691 | luaL_unref(L, LUA_REGISTRYINDEX, db->busy_cb); 692 | luaL_unref(L, LUA_REGISTRYINDEX, db->busy_udata); 693 | luaL_unref(L, LUA_REGISTRYINDEX, db->progress_cb); 694 | luaL_unref(L, LUA_REGISTRYINDEX, db->progress_udata); 695 | luaL_unref(L, LUA_REGISTRYINDEX, db->trace_cb); 696 | luaL_unref(L, LUA_REGISTRYINDEX, db->trace_udata); 697 | #if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK 698 | luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_cb); 699 | luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_udata); 700 | luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_cb); 701 | luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_udata); 702 | luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_cb); 703 | luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_udata); 704 | #endif 705 | 706 | /* close database */ 707 | result = sqlite3_close(db->db); 708 | db->db = NULL; 709 | 710 | /* free associated memory with created functions */ 711 | func = db->func; 712 | while (func) { 713 | func_next = func->next; 714 | luaL_unref(L, LUA_REGISTRYINDEX, func->fn_step); 715 | luaL_unref(L, LUA_REGISTRYINDEX, func->fn_finalize); 716 | luaL_unref(L, LUA_REGISTRYINDEX, func->udata); 717 | free(func); 718 | func = func_next; 719 | } 720 | db->func = NULL; 721 | return result; 722 | } 723 | 724 | static sdb *lsqlite_getdb(lua_State *L, int index) { 725 | sdb *db = (sdb*)luaL_checkudata(L, index, sqlite_meta); 726 | if (db == NULL) luaL_typerror(L, index, "sqlite database"); 727 | return db; 728 | } 729 | 730 | static sdb *lsqlite_checkdb(lua_State *L, int index) { 731 | sdb *db = lsqlite_getdb(L, index); 732 | if (db->db == NULL) luaL_argerror(L, index, "attempt to use closed sqlite database"); 733 | return db; 734 | } 735 | 736 | 737 | /* 738 | ** ======================================================= 739 | ** User Defined Functions - Context Methods 740 | ** ======================================================= 741 | */ 742 | typedef struct { 743 | sqlite3_context *ctx; 744 | int ud; 745 | } lcontext; 746 | 747 | static lcontext *lsqlite_make_context(lua_State *L) { 748 | lcontext *ctx = (lcontext*)lua_newuserdata(L, sizeof(lcontext)); 749 | lua_rawgeti(L, LUA_REGISTRYINDEX, sqlite_ctx_meta_ref); 750 | lua_setmetatable(L, -2); 751 | ctx->ctx = NULL; 752 | ctx->ud = LUA_NOREF; 753 | return ctx; 754 | } 755 | 756 | static lcontext *lsqlite_getcontext(lua_State *L, int index) { 757 | lcontext *ctx = (lcontext*)luaL_checkudata(L, index, sqlite_ctx_meta); 758 | if (ctx == NULL) luaL_typerror(L, index, "sqlite context"); 759 | return ctx; 760 | } 761 | 762 | static lcontext *lsqlite_checkcontext(lua_State *L, int index) { 763 | lcontext *ctx = lsqlite_getcontext(L, index); 764 | if (ctx->ctx == NULL) luaL_argerror(L, index, "invalid sqlite context"); 765 | return ctx; 766 | } 767 | 768 | static int lcontext_tostring(lua_State *L) { 769 | char buff[39]; 770 | lcontext *ctx = lsqlite_getcontext(L, 1); 771 | if (ctx->ctx == NULL) 772 | strcpy(buff, "closed"); 773 | else 774 | sprintf(buff, "%p", ctx->ctx); 775 | lua_pushfstring(L, "sqlite function context (%s)", buff); 776 | return 1; 777 | } 778 | 779 | static void lcontext_check_aggregate(lua_State *L, lcontext *ctx) { 780 | sdb_func *func = (sdb_func*)sqlite3_user_data(ctx->ctx); 781 | if (!func->aggregate) { 782 | luaL_error(L, "attempt to call aggregate method from scalar function"); 783 | } 784 | } 785 | 786 | static int lcontext_user_data(lua_State *L) { 787 | lcontext *ctx = lsqlite_checkcontext(L, 1); 788 | sdb_func *func = (sdb_func*)sqlite3_user_data(ctx->ctx); 789 | lua_rawgeti(L, LUA_REGISTRYINDEX, func->udata); 790 | return 1; 791 | } 792 | 793 | static int lcontext_get_aggregate_context(lua_State *L) { 794 | lcontext *ctx = lsqlite_checkcontext(L, 1); 795 | lcontext_check_aggregate(L, ctx); 796 | lua_rawgeti(L, LUA_REGISTRYINDEX, ctx->ud); 797 | return 1; 798 | } 799 | 800 | static int lcontext_set_aggregate_context(lua_State *L) { 801 | lcontext *ctx = lsqlite_checkcontext(L, 1); 802 | lcontext_check_aggregate(L, ctx); 803 | lua_settop(L, 2); 804 | luaL_unref(L, LUA_REGISTRYINDEX, ctx->ud); 805 | ctx->ud = luaL_ref(L, LUA_REGISTRYINDEX); 806 | return 0; 807 | } 808 | 809 | static int lcontext_aggregate_count(lua_State *L) { 810 | lcontext *ctx = lsqlite_checkcontext(L, 1); 811 | lcontext_check_aggregate(L, ctx); 812 | lua_pushinteger(L, sqlite3_aggregate_count(ctx->ctx)); 813 | return 1; 814 | } 815 | 816 | #if 0 817 | void *sqlite3_get_auxdata(sqlite3_context*, int); 818 | void sqlite3_set_auxdata(sqlite3_context*, int, void*, void (*)(void*)); 819 | #endif 820 | 821 | static int lcontext_result(lua_State *L) { 822 | lcontext *ctx = lsqlite_checkcontext(L, 1); 823 | switch (lua_type(L, 2)) { 824 | case LUA_TNUMBER: 825 | #if LUA_VERSION_NUM > 502 826 | if (lua_isinteger(L, 2)) 827 | sqlite3_result_int64(ctx->ctx, luaL_checkinteger(L, 2)); 828 | else 829 | #endif 830 | sqlite3_result_double(ctx->ctx, luaL_checknumber(L, 2)); 831 | break; 832 | case LUA_TSTRING: 833 | sqlite3_result_text(ctx->ctx, luaL_checkstring(L, 2), lua_strlen(L, 2), SQLITE_TRANSIENT); 834 | break; 835 | case LUA_TNIL: 836 | case LUA_TNONE: 837 | sqlite3_result_null(ctx->ctx); 838 | break; 839 | default: 840 | luaL_error(L, "invalid result type %s", lua_typename(L, 2)); 841 | break; 842 | } 843 | 844 | return 0; 845 | } 846 | 847 | static int lcontext_result_blob(lua_State *L) { 848 | lcontext *ctx = lsqlite_checkcontext(L, 1); 849 | const char *blob = luaL_checkstring(L, 2); 850 | int size = lua_strlen(L, 2); 851 | sqlite3_result_blob(ctx->ctx, (const void*)blob, size, SQLITE_TRANSIENT); 852 | return 0; 853 | } 854 | 855 | static int lcontext_result_double(lua_State *L) { 856 | lcontext *ctx = lsqlite_checkcontext(L, 1); 857 | double d = luaL_checknumber(L, 2); 858 | sqlite3_result_double(ctx->ctx, d); 859 | return 0; 860 | } 861 | 862 | static int lcontext_result_error(lua_State *L) { 863 | lcontext *ctx = lsqlite_checkcontext(L, 1); 864 | const char *err = luaL_checkstring(L, 2); 865 | int size = lua_strlen(L, 2); 866 | sqlite3_result_error(ctx->ctx, err, size); 867 | return 0; 868 | } 869 | 870 | static int lcontext_result_int(lua_State *L) { 871 | lcontext *ctx = lsqlite_checkcontext(L, 1); 872 | int i = luaL_checkint(L, 2); 873 | sqlite3_result_int(ctx->ctx, i); 874 | return 0; 875 | } 876 | 877 | static int lcontext_result_null(lua_State *L) { 878 | lcontext *ctx = lsqlite_checkcontext(L, 1); 879 | sqlite3_result_null(ctx->ctx); 880 | return 0; 881 | } 882 | 883 | static int lcontext_result_text(lua_State *L) { 884 | lcontext *ctx = lsqlite_checkcontext(L, 1); 885 | const char *text = luaL_checkstring(L, 2); 886 | int size = lua_strlen(L, 2); 887 | sqlite3_result_text(ctx->ctx, text, size, SQLITE_TRANSIENT); 888 | return 0; 889 | } 890 | 891 | /* 892 | ** ======================================================= 893 | ** Database Methods 894 | ** ======================================================= 895 | */ 896 | 897 | static int db_isopen(lua_State *L) { 898 | sdb *db = lsqlite_getdb(L, 1); 899 | lua_pushboolean(L, db->db != NULL ? 1 : 0); 900 | return 1; 901 | } 902 | 903 | static int db_last_insert_rowid(lua_State *L) { 904 | sdb *db = lsqlite_checkdb(L, 1); 905 | /* conversion warning: int64 -> luaNumber */ 906 | sqlite_int64 rowid = sqlite3_last_insert_rowid(db->db); 907 | PUSH_INT64(L, rowid, lua_pushfstring(L, "%ll", rowid)); 908 | return 1; 909 | } 910 | 911 | static int db_changes(lua_State *L) { 912 | sdb *db = lsqlite_checkdb(L, 1); 913 | lua_pushinteger(L, sqlite3_changes(db->db)); 914 | return 1; 915 | } 916 | 917 | static int db_total_changes(lua_State *L) { 918 | sdb *db = lsqlite_checkdb(L, 1); 919 | lua_pushinteger(L, sqlite3_total_changes(db->db)); 920 | return 1; 921 | } 922 | 923 | static int db_errcode(lua_State *L) { 924 | sdb *db = lsqlite_checkdb(L, 1); 925 | lua_pushinteger(L, sqlite3_errcode(db->db)); 926 | return 1; 927 | } 928 | 929 | static int db_errmsg(lua_State *L) { 930 | sdb *db = lsqlite_checkdb(L, 1); 931 | lua_pushstring(L, sqlite3_errmsg(db->db)); 932 | return 1; 933 | } 934 | 935 | static int db_interrupt(lua_State *L) { 936 | sdb *db = lsqlite_checkdb(L, 1); 937 | sqlite3_interrupt(db->db); 938 | return 0; 939 | } 940 | 941 | static int db_db_filename(lua_State *L) { 942 | sdb *db = lsqlite_checkdb(L, 1); 943 | const char *db_name = luaL_checkstring(L, 2); 944 | /* sqlite3_db_filename may return NULL, in that case Lua pushes nil... */ 945 | lua_pushstring(L, sqlite3_db_filename(db->db, db_name)); 946 | return 1; 947 | } 948 | 949 | /* 950 | ** Registering SQL functions: 951 | */ 952 | 953 | static void db_push_value(lua_State *L, sqlite3_value *value) { 954 | switch (sqlite3_value_type(value)) { 955 | case SQLITE_TEXT: 956 | lua_pushlstring(L, (const char*)sqlite3_value_text(value), sqlite3_value_bytes(value)); 957 | break; 958 | 959 | case SQLITE_INTEGER: 960 | PUSH_INT64(L, sqlite3_value_int64(value) 961 | , lua_pushlstring(L, (const char*)sqlite3_value_text(value) 962 | , sqlite3_value_bytes(value))); 963 | break; 964 | 965 | case SQLITE_FLOAT: 966 | lua_pushnumber(L, sqlite3_value_double(value)); 967 | break; 968 | 969 | case SQLITE_BLOB: 970 | lua_pushlstring(L, sqlite3_value_blob(value), sqlite3_value_bytes(value)); 971 | break; 972 | 973 | case SQLITE_NULL: 974 | lua_pushnil(L); 975 | break; 976 | 977 | default: 978 | /* things done properly (SQLite + Lua SQLite) 979 | ** this should never happen */ 980 | lua_pushnil(L); 981 | break; 982 | } 983 | } 984 | 985 | /* 986 | ** callback functions used when calling registered sql functions 987 | */ 988 | 989 | /* scalar function to be called 990 | ** callback params: context, values... */ 991 | static void db_sql_normal_function(sqlite3_context *context, int argc, sqlite3_value **argv) { 992 | sdb_func *func = (sdb_func*)sqlite3_user_data(context); 993 | lua_State *L = func->db->L; 994 | int n; 995 | lcontext *ctx; 996 | 997 | int top = lua_gettop(L); 998 | 999 | /* ensure there is enough space in the stack */ 1000 | lua_checkstack(L, argc + 3); 1001 | 1002 | lua_rawgeti(L, LUA_REGISTRYINDEX, func->fn_step); /* function to call */ 1003 | 1004 | if (!func->aggregate) { 1005 | ctx = lsqlite_make_context(L); /* push context - used to set results */ 1006 | } 1007 | else { 1008 | /* reuse context userdata value */ 1009 | void *p = sqlite3_aggregate_context(context, 1); 1010 | /* i think it is OK to use assume that using a light user data 1011 | ** as an entry on LUA REGISTRY table will be unique */ 1012 | lua_pushlightuserdata(L, p); 1013 | lua_rawget(L, LUA_REGISTRYINDEX); /* context table */ 1014 | 1015 | if (lua_isnil(L, -1)) { /* not yet created? */ 1016 | lua_pop(L, 1); 1017 | ctx = lsqlite_make_context(L); 1018 | lua_pushlightuserdata(L, p); 1019 | lua_pushvalue(L, -2); 1020 | lua_rawset(L, LUA_REGISTRYINDEX); 1021 | } 1022 | else 1023 | ctx = lsqlite_getcontext(L, -1); 1024 | } 1025 | 1026 | /* push params */ 1027 | for (n = 0; n < argc; ++n) { 1028 | db_push_value(L, argv[n]); 1029 | } 1030 | 1031 | /* set context */ 1032 | ctx->ctx = context; 1033 | 1034 | if (lua_pcall(L, argc + 1, 0, 0)) { 1035 | const char *errmsg = lua_tostring(L, -1); 1036 | int size = lua_strlen(L, -1); 1037 | sqlite3_result_error(context, errmsg, size); 1038 | } 1039 | 1040 | /* invalidate context */ 1041 | ctx->ctx = NULL; 1042 | 1043 | if (!func->aggregate) { 1044 | luaL_unref(L, LUA_REGISTRYINDEX, ctx->ud); 1045 | } 1046 | 1047 | lua_settop(L, top); 1048 | } 1049 | 1050 | static void db_sql_finalize_function(sqlite3_context *context) { 1051 | sdb_func *func = (sdb_func*)sqlite3_user_data(context); 1052 | lua_State *L = func->db->L; 1053 | void *p = sqlite3_aggregate_context(context, 1); /* minimal mem usage */ 1054 | lcontext *ctx; 1055 | int top = lua_gettop(L); 1056 | 1057 | lua_rawgeti(L, LUA_REGISTRYINDEX, func->fn_finalize); /* function to call */ 1058 | 1059 | /* i think it is OK to use assume that using a light user data 1060 | ** as an entry on LUA REGISTRY table will be unique */ 1061 | lua_pushlightuserdata(L, p); 1062 | lua_rawget(L, LUA_REGISTRYINDEX); /* context table */ 1063 | 1064 | if (lua_isnil(L, -1)) { /* not yet created? - shouldn't happen in finalize function */ 1065 | lua_pop(L, 1); 1066 | ctx = lsqlite_make_context(L); 1067 | lua_pushlightuserdata(L, p); 1068 | lua_pushvalue(L, -2); 1069 | lua_rawset(L, LUA_REGISTRYINDEX); 1070 | } 1071 | else 1072 | ctx = lsqlite_getcontext(L, -1); 1073 | 1074 | /* set context */ 1075 | ctx->ctx = context; 1076 | 1077 | if (lua_pcall(L, 1, 0, 0)) { 1078 | sqlite3_result_error(context, lua_tostring(L, -1), -1); 1079 | } 1080 | 1081 | /* invalidate context */ 1082 | ctx->ctx = NULL; 1083 | 1084 | /* cleanup context */ 1085 | luaL_unref(L, LUA_REGISTRYINDEX, ctx->ud); 1086 | /* remove it from registry */ 1087 | lua_pushlightuserdata(L, p); 1088 | lua_pushnil(L); 1089 | lua_rawset(L, LUA_REGISTRYINDEX); 1090 | 1091 | lua_settop(L, top); 1092 | } 1093 | 1094 | /* 1095 | ** Register a normal function 1096 | ** Params: db, function name, number arguments, [ callback | step, finalize], user data 1097 | ** Returns: true on sucess 1098 | ** 1099 | ** Normal function: 1100 | ** Params: context, params 1101 | ** 1102 | ** Aggregate function: 1103 | ** Params of step: context, params 1104 | ** Params of finalize: context 1105 | */ 1106 | static int db_register_function(lua_State *L, int aggregate) { 1107 | sdb *db = lsqlite_checkdb(L, 1); 1108 | const char *name; 1109 | int args; 1110 | int result; 1111 | sdb_func *func; 1112 | 1113 | /* safety measure */ 1114 | if (aggregate) aggregate = 1; 1115 | 1116 | name = luaL_checkstring(L, 2); 1117 | args = luaL_checkint(L, 3); 1118 | luaL_checktype(L, 4, LUA_TFUNCTION); 1119 | if (aggregate) luaL_checktype(L, 5, LUA_TFUNCTION); 1120 | 1121 | /* maybe an alternative way to allocate memory should be used/avoided */ 1122 | func = (sdb_func*)malloc(sizeof(sdb_func)); 1123 | if (func == NULL) { 1124 | luaL_error(L, "out of memory"); 1125 | } 1126 | 1127 | result = sqlite3_create_function( 1128 | db->db, name, args, SQLITE_UTF8, func, 1129 | aggregate ? NULL : db_sql_normal_function, 1130 | aggregate ? db_sql_normal_function : NULL, 1131 | aggregate ? db_sql_finalize_function : NULL 1132 | ); 1133 | 1134 | if (result == SQLITE_OK) { 1135 | /* safety measures for userdata field to be present in the stack */ 1136 | lua_settop(L, 5 + aggregate); 1137 | 1138 | /* save registered function in db function list */ 1139 | func->db = db; 1140 | func->aggregate = aggregate; 1141 | func->next = db->func; 1142 | db->func = func; 1143 | 1144 | /* save the setp/normal function callback */ 1145 | lua_pushvalue(L, 4); 1146 | func->fn_step = luaL_ref(L, LUA_REGISTRYINDEX); 1147 | /* save user data */ 1148 | lua_pushvalue(L, 5+aggregate); 1149 | func->udata = luaL_ref(L, LUA_REGISTRYINDEX); 1150 | 1151 | if (aggregate) { 1152 | lua_pushvalue(L, 5); 1153 | func->fn_finalize = luaL_ref(L, LUA_REGISTRYINDEX); 1154 | } 1155 | else 1156 | func->fn_finalize = LUA_NOREF; 1157 | } 1158 | else { 1159 | /* free allocated memory */ 1160 | free(func); 1161 | } 1162 | 1163 | lua_pushboolean(L, result == SQLITE_OK ? 1 : 0); 1164 | return 1; 1165 | } 1166 | 1167 | static int db_create_function(lua_State *L) { 1168 | return db_register_function(L, 0); 1169 | } 1170 | 1171 | static int db_create_aggregate(lua_State *L) { 1172 | return db_register_function(L, 1); 1173 | } 1174 | 1175 | /* create_collation; contributed by Thomas Lauer 1176 | */ 1177 | 1178 | typedef struct { 1179 | lua_State *L; 1180 | int ref; 1181 | } scc; 1182 | 1183 | static int collwrapper(scc *co,int l1,const void *p1, 1184 | int l2,const void *p2) { 1185 | int res=0; 1186 | lua_State *L=co->L; 1187 | lua_rawgeti(L,LUA_REGISTRYINDEX,co->ref); 1188 | lua_pushlstring(L,p1,l1); 1189 | lua_pushlstring(L,p2,l2); 1190 | if (lua_pcall(L,2,1,0)==0) res=(int)lua_tonumber(L,-1); 1191 | lua_pop(L,1); 1192 | return res; 1193 | } 1194 | 1195 | static void collfree(scc *co) { 1196 | if (co) { 1197 | luaL_unref(co->L,LUA_REGISTRYINDEX,co->ref); 1198 | free(co); 1199 | } 1200 | } 1201 | 1202 | static int db_create_collation(lua_State *L) { 1203 | sdb *db=lsqlite_checkdb(L,1); 1204 | const char *collname=luaL_checkstring(L,2); 1205 | scc *co=NULL; 1206 | int (*collfunc)(scc *,int,const void *,int,const void *)=NULL; 1207 | lua_settop(L,3); /* default args to nil, and exclude extras */ 1208 | if (lua_isfunction(L,3)) collfunc=collwrapper; 1209 | else if (!lua_isnil(L,3)) 1210 | luaL_error(L,"create_collation: function or nil expected"); 1211 | if (collfunc != NULL) { 1212 | co=(scc *)malloc(sizeof(scc)); /* userdata is a no-no as it 1213 | will be garbage-collected */ 1214 | if (co) { 1215 | co->L=L; 1216 | /* lua_settop(L,3) above means we don't need: lua_pushvalue(L,3); */ 1217 | co->ref=luaL_ref(L,LUA_REGISTRYINDEX); 1218 | } 1219 | else luaL_error(L,"create_collation: could not allocate callback"); 1220 | } 1221 | sqlite3_create_collation_v2(db->db, collname, SQLITE_UTF8, 1222 | (void *)co, 1223 | (int(*)(void*,int,const void*,int,const void*))collfunc, 1224 | (void(*)(void*))collfree); 1225 | return 0; 1226 | } 1227 | 1228 | /* Thanks to Wolfgang Oertl... 1229 | */ 1230 | static int db_load_extension(lua_State *L) { 1231 | sdb *db=lsqlite_checkdb(L,1); 1232 | const char *extname=luaL_optstring(L,2,NULL); 1233 | const char *entrypoint=luaL_optstring(L,3,NULL); 1234 | int result; 1235 | char *errmsg = NULL; 1236 | 1237 | if (extname == NULL) { 1238 | result = sqlite3_enable_load_extension(db->db,0); /* disable extension loading */ 1239 | } 1240 | else { 1241 | sqlite3_enable_load_extension(db->db,1); /* enable extension loading */ 1242 | result = sqlite3_load_extension(db->db,extname,entrypoint,&errmsg); 1243 | } 1244 | 1245 | if (result == SQLITE_OK) { 1246 | lua_pushboolean(L,1); 1247 | return 1; 1248 | } 1249 | 1250 | lua_pushboolean(L,0); /* so, assert(load_extension(...)) works */ 1251 | lua_pushstring(L,errmsg); 1252 | sqlite3_free(errmsg); 1253 | return 2; 1254 | } 1255 | 1256 | /* 1257 | ** trace callback: 1258 | ** Params: database, callback function, userdata 1259 | ** 1260 | ** callback function: 1261 | ** Params: userdata, sql 1262 | */ 1263 | static void db_trace_callback(void *user, const char *sql) { 1264 | sdb *db = (sdb*)user; 1265 | lua_State *L = db->L; 1266 | int top = lua_gettop(L); 1267 | 1268 | /* setup lua callback call */ 1269 | lua_rawgeti(L, LUA_REGISTRYINDEX, db->trace_cb); /* get callback */ 1270 | lua_rawgeti(L, LUA_REGISTRYINDEX, db->trace_udata); /* get callback user data */ 1271 | lua_pushstring(L, sql); /* traced sql statement */ 1272 | 1273 | /* call lua function */ 1274 | lua_pcall(L, 2, 0, 0); 1275 | /* ignore any error generated by this function */ 1276 | 1277 | lua_settop(L, top); 1278 | } 1279 | 1280 | static int db_trace(lua_State *L) { 1281 | sdb *db = lsqlite_checkdb(L, 1); 1282 | 1283 | if (lua_gettop(L) < 2 || lua_isnil(L, 2)) { 1284 | luaL_unref(L, LUA_REGISTRYINDEX, db->trace_cb); 1285 | luaL_unref(L, LUA_REGISTRYINDEX, db->trace_udata); 1286 | 1287 | db->trace_cb = 1288 | db->trace_udata = LUA_NOREF; 1289 | 1290 | /* clear trace handler */ 1291 | sqlite3_trace(db->db, NULL, NULL); 1292 | } 1293 | else { 1294 | luaL_checktype(L, 2, LUA_TFUNCTION); 1295 | 1296 | /* make sure we have an userdata field (even if nil) */ 1297 | lua_settop(L, 3); 1298 | 1299 | luaL_unref(L, LUA_REGISTRYINDEX, db->trace_cb); 1300 | luaL_unref(L, LUA_REGISTRYINDEX, db->trace_udata); 1301 | 1302 | db->trace_udata = luaL_ref(L, LUA_REGISTRYINDEX); 1303 | db->trace_cb = luaL_ref(L, LUA_REGISTRYINDEX); 1304 | 1305 | /* set trace handler */ 1306 | sqlite3_trace(db->db, db_trace_callback, db); 1307 | } 1308 | 1309 | return 0; 1310 | } 1311 | 1312 | #if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK 1313 | 1314 | /* 1315 | ** update_hook callback: 1316 | ** Params: database, callback function, userdata 1317 | ** 1318 | ** callback function: 1319 | ** Params: userdata, {one of SQLITE_INSERT, SQLITE_DELETE, or SQLITE_UPDATE}, 1320 | ** database name, table name (containing the affected row), rowid of the row 1321 | */ 1322 | static void db_update_hook_callback(void *user, int op, char const *dbname, char const *tblname, sqlite3_int64 rowid) { 1323 | sdb *db = (sdb*)user; 1324 | lua_State *L = db->L; 1325 | int top = lua_gettop(L); 1326 | 1327 | /* setup lua callback call */ 1328 | lua_rawgeti(L, LUA_REGISTRYINDEX, db->update_hook_cb); /* get callback */ 1329 | lua_rawgeti(L, LUA_REGISTRYINDEX, db->update_hook_udata); /* get callback user data */ 1330 | lua_pushinteger(L, op); 1331 | lua_pushstring(L, dbname); /* update_hook database name */ 1332 | lua_pushstring(L, tblname); /* update_hook database name */ 1333 | 1334 | PUSH_INT64(L, rowid, lua_pushfstring(L, "%ll", rowid)); 1335 | 1336 | /* call lua function */ 1337 | lua_pcall(L, 5, 0, 0); 1338 | /* ignore any error generated by this function */ 1339 | 1340 | lua_settop(L, top); 1341 | } 1342 | 1343 | static int db_update_hook(lua_State *L) { 1344 | sdb *db = lsqlite_checkdb(L, 1); 1345 | 1346 | if (lua_gettop(L) < 2 || lua_isnil(L, 2)) { 1347 | luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_cb); 1348 | luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_udata); 1349 | 1350 | db->update_hook_cb = 1351 | db->update_hook_udata = LUA_NOREF; 1352 | 1353 | /* clear update_hook handler */ 1354 | sqlite3_update_hook(db->db, NULL, NULL); 1355 | } 1356 | else { 1357 | luaL_checktype(L, 2, LUA_TFUNCTION); 1358 | 1359 | /* make sure we have an userdata field (even if nil) */ 1360 | lua_settop(L, 3); 1361 | 1362 | luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_cb); 1363 | luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_udata); 1364 | 1365 | db->update_hook_udata = luaL_ref(L, LUA_REGISTRYINDEX); 1366 | db->update_hook_cb = luaL_ref(L, LUA_REGISTRYINDEX); 1367 | 1368 | /* set update_hook handler */ 1369 | sqlite3_update_hook(db->db, db_update_hook_callback, db); 1370 | } 1371 | 1372 | return 0; 1373 | } 1374 | 1375 | /* 1376 | ** commit_hook callback: 1377 | ** Params: database, callback function, userdata 1378 | ** 1379 | ** callback function: 1380 | ** Params: userdata 1381 | ** Returned value: Return false or nil to continue the COMMIT operation normally. 1382 | ** return true (non false, non nil), then the COMMIT is converted into a ROLLBACK. 1383 | */ 1384 | static int db_commit_hook_callback(void *user) { 1385 | sdb *db = (sdb*)user; 1386 | lua_State *L = db->L; 1387 | int top = lua_gettop(L); 1388 | int rollback = 0; 1389 | 1390 | /* setup lua callback call */ 1391 | lua_rawgeti(L, LUA_REGISTRYINDEX, db->commit_hook_cb); /* get callback */ 1392 | lua_rawgeti(L, LUA_REGISTRYINDEX, db->commit_hook_udata); /* get callback user data */ 1393 | 1394 | /* call lua function */ 1395 | if (!lua_pcall(L, 1, 1, 0)) 1396 | rollback = lua_toboolean(L, -1); /* use result if there was no error */ 1397 | 1398 | lua_settop(L, top); 1399 | return rollback; 1400 | } 1401 | 1402 | static int db_commit_hook(lua_State *L) { 1403 | sdb *db = lsqlite_checkdb(L, 1); 1404 | 1405 | if (lua_gettop(L) < 2 || lua_isnil(L, 2)) { 1406 | luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_cb); 1407 | luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_udata); 1408 | 1409 | db->commit_hook_cb = 1410 | db->commit_hook_udata = LUA_NOREF; 1411 | 1412 | /* clear commit_hook handler */ 1413 | sqlite3_commit_hook(db->db, NULL, NULL); 1414 | } 1415 | else { 1416 | luaL_checktype(L, 2, LUA_TFUNCTION); 1417 | 1418 | /* make sure we have an userdata field (even if nil) */ 1419 | lua_settop(L, 3); 1420 | 1421 | luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_cb); 1422 | luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_udata); 1423 | 1424 | db->commit_hook_udata = luaL_ref(L, LUA_REGISTRYINDEX); 1425 | db->commit_hook_cb = luaL_ref(L, LUA_REGISTRYINDEX); 1426 | 1427 | /* set commit_hook handler */ 1428 | sqlite3_commit_hook(db->db, db_commit_hook_callback, db); 1429 | } 1430 | 1431 | return 0; 1432 | } 1433 | 1434 | /* 1435 | ** rollback hook callback: 1436 | ** Params: database, callback function, userdata 1437 | ** 1438 | ** callback function: 1439 | ** Params: userdata 1440 | */ 1441 | static void db_rollback_hook_callback(void *user) { 1442 | sdb *db = (sdb*)user; 1443 | lua_State *L = db->L; 1444 | int top = lua_gettop(L); 1445 | 1446 | /* setup lua callback call */ 1447 | lua_rawgeti(L, LUA_REGISTRYINDEX, db->rollback_hook_cb); /* get callback */ 1448 | lua_rawgeti(L, LUA_REGISTRYINDEX, db->rollback_hook_udata); /* get callback user data */ 1449 | 1450 | /* call lua function */ 1451 | lua_pcall(L, 1, 0, 0); 1452 | /* ignore any error generated by this function */ 1453 | 1454 | lua_settop(L, top); 1455 | } 1456 | 1457 | static int db_rollback_hook(lua_State *L) { 1458 | sdb *db = lsqlite_checkdb(L, 1); 1459 | 1460 | if (lua_gettop(L) < 2 || lua_isnil(L, 2)) { 1461 | luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_cb); 1462 | luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_udata); 1463 | 1464 | db->rollback_hook_cb = 1465 | db->rollback_hook_udata = LUA_NOREF; 1466 | 1467 | /* clear rollback_hook handler */ 1468 | sqlite3_rollback_hook(db->db, NULL, NULL); 1469 | } 1470 | else { 1471 | luaL_checktype(L, 2, LUA_TFUNCTION); 1472 | 1473 | /* make sure we have an userdata field (even if nil) */ 1474 | lua_settop(L, 3); 1475 | 1476 | luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_cb); 1477 | luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_udata); 1478 | 1479 | db->rollback_hook_udata = luaL_ref(L, LUA_REGISTRYINDEX); 1480 | db->rollback_hook_cb = luaL_ref(L, LUA_REGISTRYINDEX); 1481 | 1482 | /* set rollback_hook handler */ 1483 | sqlite3_rollback_hook(db->db, db_rollback_hook_callback, db); 1484 | } 1485 | 1486 | return 0; 1487 | } 1488 | 1489 | #endif /* #if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK */ 1490 | 1491 | #if !defined(SQLITE_OMIT_PROGRESS_CALLBACK) || !SQLITE_OMIT_PROGRESS_CALLBACK 1492 | 1493 | /* 1494 | ** progress handler: 1495 | ** Params: database, number of opcodes, callback function, userdata 1496 | ** 1497 | ** callback function: 1498 | ** Params: userdata 1499 | ** returns: 0 to return immediatly and return SQLITE_ABORT, non-zero to continue 1500 | */ 1501 | static int db_progress_callback(void *user) { 1502 | int result = 1; /* abort by default */ 1503 | sdb *db = (sdb*)user; 1504 | lua_State *L = db->L; 1505 | int top = lua_gettop(L); 1506 | 1507 | lua_rawgeti(L, LUA_REGISTRYINDEX, db->progress_cb); 1508 | lua_rawgeti(L, LUA_REGISTRYINDEX, db->progress_udata); 1509 | 1510 | /* call lua function */ 1511 | if (!lua_pcall(L, 1, 1, 0)) 1512 | result = lua_toboolean(L, -1); 1513 | 1514 | lua_settop(L, top); 1515 | return result; 1516 | } 1517 | 1518 | static int db_progress_handler(lua_State *L) { 1519 | sdb *db = lsqlite_checkdb(L, 1); 1520 | 1521 | if (lua_gettop(L) < 2 || lua_isnil(L, 2)) { 1522 | luaL_unref(L, LUA_REGISTRYINDEX, db->progress_cb); 1523 | luaL_unref(L, LUA_REGISTRYINDEX, db->progress_udata); 1524 | 1525 | db->progress_cb = 1526 | db->progress_udata = LUA_NOREF; 1527 | 1528 | /* clear busy handler */ 1529 | sqlite3_progress_handler(db->db, 0, NULL, NULL); 1530 | } 1531 | else { 1532 | int nop = luaL_checkint(L, 2); /* number of opcodes */ 1533 | luaL_checktype(L, 3, LUA_TFUNCTION); 1534 | 1535 | /* make sure we have an userdata field (even if nil) */ 1536 | lua_settop(L, 4); 1537 | 1538 | luaL_unref(L, LUA_REGISTRYINDEX, db->progress_cb); 1539 | luaL_unref(L, LUA_REGISTRYINDEX, db->progress_udata); 1540 | 1541 | db->progress_udata = luaL_ref(L, LUA_REGISTRYINDEX); 1542 | db->progress_cb = luaL_ref(L, LUA_REGISTRYINDEX); 1543 | 1544 | /* set progress callback */ 1545 | sqlite3_progress_handler(db->db, nop, db_progress_callback, db); 1546 | } 1547 | 1548 | return 0; 1549 | } 1550 | 1551 | #else /* #if !defined(SQLITE_OMIT_PROGRESS_CALLBACK) || !SQLITE_OMIT_PROGRESS_CALLBACK */ 1552 | 1553 | static int db_progress_handler(lua_State *L) { 1554 | lua_pushliteral(L, "progress callback support disabled at compile time"); 1555 | lua_error(L); 1556 | return 0; 1557 | } 1558 | 1559 | #endif /* #if !defined(SQLITE_OMIT_PROGRESS_CALLBACK) || !SQLITE_OMIT_PROGRESS_CALLBACK */ 1560 | 1561 | /* Online Backup API */ 1562 | #if 0 1563 | sqlite3_backup *sqlite3_backup_init( 1564 | sqlite3 *pDest, /* Destination database handle */ 1565 | const char *zDestName, /* Destination database name */ 1566 | sqlite3 *pSource, /* Source database handle */ 1567 | const char *zSourceName /* Source database name */ 1568 | ); 1569 | int sqlite3_backup_step(sqlite3_backup *p, int nPage); 1570 | int sqlite3_backup_finish(sqlite3_backup *p); 1571 | int sqlite3_backup_remaining(sqlite3_backup *p); 1572 | int sqlite3_backup_pagecount(sqlite3_backup *p); 1573 | #endif 1574 | 1575 | struct sdb_bu { 1576 | sqlite3_backup *bu; /* backup structure */ 1577 | }; 1578 | 1579 | static int cleanupbu(lua_State *L, sdb_bu *sbu) { 1580 | 1581 | if (!sbu->bu) return 0; /* already finished */ 1582 | 1583 | /* remove table from registry */ 1584 | lua_pushlightuserdata(L, sbu->bu); 1585 | lua_pushnil(L); 1586 | lua_rawset(L, LUA_REGISTRYINDEX); 1587 | 1588 | lua_pushinteger(L, sqlite3_backup_finish(sbu->bu)); 1589 | sbu->bu = NULL; 1590 | 1591 | return 1; 1592 | } 1593 | 1594 | static int lsqlite_backup_init(lua_State *L) { 1595 | 1596 | sdb *target_db = lsqlite_checkdb(L, 1); 1597 | const char *target_nm = luaL_checkstring(L, 2); 1598 | sdb *source_db = lsqlite_checkdb(L, 3); 1599 | const char *source_nm = luaL_checkstring(L, 4); 1600 | 1601 | sqlite3_backup *bu = sqlite3_backup_init(target_db->db, target_nm, source_db->db, source_nm); 1602 | 1603 | if (NULL != bu) { 1604 | sdb_bu *sbu = (sdb_bu*)lua_newuserdata(L, sizeof(sdb_bu)); 1605 | 1606 | luaL_getmetatable(L, sqlite_bu_meta); 1607 | lua_setmetatable(L, -2); /* set metatable */ 1608 | sbu->bu = bu; 1609 | 1610 | /* create table from registry */ 1611 | /* to prevent referenced databases from being garbage collected while bu is live */ 1612 | lua_pushlightuserdata(L, bu); 1613 | lua_createtable(L, 2, 0); 1614 | /* add source and target dbs to table at indices 1 and 2 */ 1615 | lua_pushvalue(L, 1); /* target db */ 1616 | lua_rawseti(L, -2, 1); 1617 | lua_pushvalue(L, 3); /* source db */ 1618 | lua_rawseti(L, -2, 2); 1619 | /* put table in registry with key lightuserdata bu */ 1620 | lua_rawset(L, LUA_REGISTRYINDEX); 1621 | 1622 | return 1; 1623 | } 1624 | else { 1625 | return 0; 1626 | } 1627 | } 1628 | 1629 | static sdb_bu *lsqlite_getbu(lua_State *L, int index) { 1630 | sdb_bu *sbu = (sdb_bu*)luaL_checkudata(L, index, sqlite_bu_meta); 1631 | if (sbu == NULL) luaL_typerror(L, index, "sqlite database backup"); 1632 | return sbu; 1633 | } 1634 | 1635 | static sdb_bu *lsqlite_checkbu(lua_State *L, int index) { 1636 | sdb_bu *sbu = lsqlite_getbu(L, index); 1637 | if (sbu->bu == NULL) luaL_argerror(L, index, "attempt to use closed sqlite database backup"); 1638 | return sbu; 1639 | } 1640 | 1641 | static int dbbu_gc(lua_State *L) { 1642 | sdb_bu *sbu = lsqlite_getbu(L, 1); 1643 | if (sbu->bu != NULL) { 1644 | cleanupbu(L, sbu); 1645 | lua_pop(L, 1); 1646 | } 1647 | /* else ignore if already finished */ 1648 | return 0; 1649 | } 1650 | 1651 | static int dbbu_step(lua_State *L) { 1652 | sdb_bu *sbu = lsqlite_checkbu(L, 1); 1653 | int nPage = luaL_checkint(L, 2); 1654 | lua_pushinteger(L, sqlite3_backup_step(sbu->bu, nPage)); 1655 | return 1; 1656 | } 1657 | 1658 | static int dbbu_remaining(lua_State *L) { 1659 | sdb_bu *sbu = lsqlite_checkbu(L, 1); 1660 | lua_pushinteger(L, sqlite3_backup_remaining(sbu->bu)); 1661 | return 1; 1662 | } 1663 | 1664 | static int dbbu_pagecount(lua_State *L) { 1665 | sdb_bu *sbu = lsqlite_checkbu(L, 1); 1666 | lua_pushinteger(L, sqlite3_backup_pagecount(sbu->bu)); 1667 | return 1; 1668 | } 1669 | 1670 | static int dbbu_finish(lua_State *L) { 1671 | sdb_bu *sbu = lsqlite_checkbu(L, 1); 1672 | return cleanupbu(L, sbu); 1673 | } 1674 | 1675 | /* end of Online Backup API */ 1676 | 1677 | /* 1678 | ** busy handler: 1679 | ** Params: database, callback function, userdata 1680 | ** 1681 | ** callback function: 1682 | ** Params: userdata, number of tries 1683 | ** returns: 0 to return immediatly and return SQLITE_BUSY, non-zero to try again 1684 | */ 1685 | static int db_busy_callback(void *user, int tries) { 1686 | int retry = 0; /* abort by default */ 1687 | sdb *db = (sdb*)user; 1688 | lua_State *L = db->L; 1689 | int top = lua_gettop(L); 1690 | 1691 | lua_rawgeti(L, LUA_REGISTRYINDEX, db->busy_cb); 1692 | lua_rawgeti(L, LUA_REGISTRYINDEX, db->busy_udata); 1693 | lua_pushinteger(L, tries); 1694 | 1695 | /* call lua function */ 1696 | if (!lua_pcall(L, 2, 1, 0)) 1697 | retry = lua_toboolean(L, -1); 1698 | 1699 | lua_settop(L, top); 1700 | return retry; 1701 | } 1702 | 1703 | static int db_busy_handler(lua_State *L) { 1704 | sdb *db = lsqlite_checkdb(L, 1); 1705 | 1706 | if (lua_gettop(L) < 2 || lua_isnil(L, 2)) { 1707 | luaL_unref(L, LUA_REGISTRYINDEX, db->busy_cb); 1708 | luaL_unref(L, LUA_REGISTRYINDEX, db->busy_udata); 1709 | 1710 | db->busy_cb = 1711 | db->busy_udata = LUA_NOREF; 1712 | 1713 | /* clear busy handler */ 1714 | sqlite3_busy_handler(db->db, NULL, NULL); 1715 | } 1716 | else { 1717 | luaL_checktype(L, 2, LUA_TFUNCTION); 1718 | /* make sure we have an userdata field (even if nil) */ 1719 | lua_settop(L, 3); 1720 | 1721 | luaL_unref(L, LUA_REGISTRYINDEX, db->busy_cb); 1722 | luaL_unref(L, LUA_REGISTRYINDEX, db->busy_udata); 1723 | 1724 | db->busy_udata = luaL_ref(L, LUA_REGISTRYINDEX); 1725 | db->busy_cb = luaL_ref(L, LUA_REGISTRYINDEX); 1726 | 1727 | /* set busy handler */ 1728 | sqlite3_busy_handler(db->db, db_busy_callback, db); 1729 | } 1730 | 1731 | return 0; 1732 | } 1733 | 1734 | static int db_busy_timeout(lua_State *L) { 1735 | sdb *db = lsqlite_checkdb(L, 1); 1736 | int timeout = luaL_checkint(L, 2); 1737 | sqlite3_busy_timeout(db->db, timeout); 1738 | 1739 | /* if there was a timeout callback registered, it is now 1740 | ** invalid/useless. free any references we may have */ 1741 | luaL_unref(L, LUA_REGISTRYINDEX, db->busy_cb); 1742 | luaL_unref(L, LUA_REGISTRYINDEX, db->busy_udata); 1743 | db->busy_cb = 1744 | db->busy_udata = LUA_NOREF; 1745 | 1746 | return 0; 1747 | } 1748 | 1749 | /* 1750 | ** Params: db, sql, callback, user 1751 | ** returns: code [, errmsg] 1752 | ** 1753 | ** Callback: 1754 | ** Params: user, number of columns, values, names 1755 | ** Returns: 0 to continue, other value will cause abort 1756 | */ 1757 | static int db_exec_callback(void* user, int columns, char **data, char **names) { 1758 | int result = SQLITE_ABORT; /* abort by default */ 1759 | lua_State *L = (lua_State*)user; 1760 | int n; 1761 | 1762 | int top = lua_gettop(L); 1763 | 1764 | lua_pushvalue(L, 3); /* function to call */ 1765 | lua_pushvalue(L, 4); /* user data */ 1766 | lua_pushinteger(L, columns); /* total number of rows in result */ 1767 | 1768 | /* column values */ 1769 | lua_pushvalue(L, 6); 1770 | for (n = 0; n < columns;) { 1771 | lua_pushstring(L, data[n++]); 1772 | lua_rawseti(L, -2, n); 1773 | } 1774 | 1775 | /* columns names */ 1776 | lua_pushvalue(L, 5); 1777 | if (lua_isnil(L, -1)) { 1778 | lua_pop(L, 1); 1779 | lua_createtable(L, columns, 0); 1780 | lua_pushvalue(L, -1); 1781 | lua_replace(L, 5); 1782 | for (n = 0; n < columns;) { 1783 | lua_pushstring(L, names[n++]); 1784 | lua_rawseti(L, -2, n); 1785 | } 1786 | } 1787 | 1788 | /* call lua function */ 1789 | if (!lua_pcall(L, 4, 1, 0)) { 1790 | 1791 | #if LUA_VERSION_NUM > 502 1792 | if (lua_isinteger(L, -1)) 1793 | result = lua_tointeger(L, -1); 1794 | else 1795 | #endif 1796 | if (lua_isnumber(L, -1)) 1797 | result = lua_tonumber(L, -1); 1798 | } 1799 | 1800 | lua_settop(L, top); 1801 | return result; 1802 | } 1803 | 1804 | static int db_exec(lua_State *L) { 1805 | sdb *db = lsqlite_checkdb(L, 1); 1806 | const char *sql = luaL_checkstring(L, 2); 1807 | int result; 1808 | 1809 | if (!lua_isnoneornil(L, 3)) { 1810 | /* stack: 1811 | ** 3: callback function 1812 | ** 4: userdata 1813 | ** 5: column names 1814 | ** 6: reusable column values 1815 | */ 1816 | luaL_checktype(L, 3, LUA_TFUNCTION); 1817 | lua_settop(L, 4); /* 'trap' userdata - nil extra parameters */ 1818 | lua_pushnil(L); /* column names not known at this point */ 1819 | lua_newtable(L); /* column values table */ 1820 | 1821 | result = sqlite3_exec(db->db, sql, db_exec_callback, L, NULL); 1822 | } 1823 | else { 1824 | /* no callbacks */ 1825 | result = sqlite3_exec(db->db, sql, NULL, NULL, NULL); 1826 | } 1827 | 1828 | lua_pushinteger(L, result); 1829 | return 1; 1830 | } 1831 | 1832 | /* 1833 | ** Params: db, sql 1834 | ** returns: code, compiled length or error message 1835 | */ 1836 | static int db_prepare(lua_State *L) { 1837 | sdb *db = lsqlite_checkdb(L, 1); 1838 | const char *sql = luaL_checkstring(L, 2); 1839 | int sql_len = lua_strlen(L, 2); 1840 | const char *sqltail; 1841 | sdb_vm *svm; 1842 | lua_settop(L,2); /* db,sql is on top of stack for call to newvm */ 1843 | svm = newvm(L, db); 1844 | 1845 | if (sqlite3_prepare_v2(db->db, sql, sql_len, &svm->vm, &sqltail) != SQLITE_OK) { 1846 | lua_pushnil(L); 1847 | lua_pushinteger(L, sqlite3_errcode(db->db)); 1848 | if (cleanupvm(L, svm) == 1) 1849 | lua_pop(L, 1); /* this should not happen since sqlite3_prepare_v2 will not set ->vm on error */ 1850 | return 2; 1851 | } 1852 | 1853 | /* vm already in the stack */ 1854 | lua_pushstring(L, sqltail); 1855 | return 2; 1856 | } 1857 | 1858 | static int db_do_next_row(lua_State *L, int packed) { 1859 | int result; 1860 | sdb_vm *svm = lsqlite_checkvm(L, 1); 1861 | sqlite3_stmt *vm; 1862 | int columns; 1863 | int i; 1864 | 1865 | result = stepvm(L, svm); 1866 | vm = svm->vm; /* stepvm may change svm->vm if re-prepare is needed */ 1867 | svm->has_values = result == SQLITE_ROW ? 1 : 0; 1868 | svm->columns = columns = sqlite3_data_count(vm); 1869 | 1870 | if (result == SQLITE_ROW) { 1871 | if (packed) { 1872 | if (packed == 1) { 1873 | lua_createtable(L, columns, 0); 1874 | for (i = 0; i < columns;) { 1875 | vm_push_column(L, vm, i); 1876 | lua_rawseti(L, -2, ++i); 1877 | } 1878 | } 1879 | else { 1880 | lua_createtable(L, 0, columns); 1881 | for (i = 0; i < columns; ++i) { 1882 | lua_pushstring(L, sqlite3_column_name(vm, i)); 1883 | vm_push_column(L, vm, i); 1884 | lua_rawset(L, -3); 1885 | } 1886 | } 1887 | return 1; 1888 | } 1889 | else { 1890 | lua_checkstack(L, columns); 1891 | for (i = 0; i < columns; ++i) 1892 | vm_push_column(L, vm, i); 1893 | return svm->columns; 1894 | } 1895 | } 1896 | 1897 | if (svm->temp) { 1898 | /* finalize and check for errors */ 1899 | result = sqlite3_finalize(vm); 1900 | svm->vm = NULL; 1901 | cleanupvm(L, svm); 1902 | } 1903 | else if (result == SQLITE_DONE) { 1904 | result = sqlite3_reset(vm); 1905 | } 1906 | 1907 | if (result != SQLITE_OK) { 1908 | lua_pushstring(L, sqlite3_errmsg(svm->db->db)); 1909 | lua_error(L); 1910 | } 1911 | return 0; 1912 | } 1913 | 1914 | static int db_next_row(lua_State *L) { 1915 | return db_do_next_row(L, 0); 1916 | } 1917 | 1918 | static int db_next_packed_row(lua_State *L) { 1919 | return db_do_next_row(L, 1); 1920 | } 1921 | 1922 | static int db_next_named_row(lua_State *L) { 1923 | return db_do_next_row(L, 2); 1924 | } 1925 | 1926 | static int dbvm_do_rows(lua_State *L, int(*f)(lua_State *)) { 1927 | /* sdb_vm *svm = */ 1928 | lsqlite_checkvm(L, 1); 1929 | lua_pushvalue(L,1); 1930 | lua_pushcfunction(L, f); 1931 | lua_insert(L, -2); 1932 | return 2; 1933 | } 1934 | 1935 | static int dbvm_rows(lua_State *L) { 1936 | return dbvm_do_rows(L, db_next_packed_row); 1937 | } 1938 | 1939 | static int dbvm_nrows(lua_State *L) { 1940 | return dbvm_do_rows(L, db_next_named_row); 1941 | } 1942 | 1943 | static int dbvm_urows(lua_State *L) { 1944 | return dbvm_do_rows(L, db_next_row); 1945 | } 1946 | 1947 | static int db_do_rows(lua_State *L, int(*f)(lua_State *)) { 1948 | sdb *db = lsqlite_checkdb(L, 1); 1949 | const char *sql = luaL_checkstring(L, 2); 1950 | sdb_vm *svm; 1951 | 1952 | int nargs = lua_gettop(L) - 2; 1953 | if (nargs > 0) { 1954 | lua_pushvalue(L, 1); 1955 | lua_pushvalue(L, 2); /* copy db,sql on top of the stack for newvm */ 1956 | } 1957 | 1958 | svm = newvm(L, db); 1959 | svm->temp = 1; 1960 | 1961 | if (sqlite3_prepare_v2(db->db, sql, -1, &svm->vm, NULL) != SQLITE_OK) { 1962 | lua_pushstring(L, sqlite3_errmsg(svm->db->db)); 1963 | if (cleanupvm(L, svm) == 1) 1964 | lua_pop(L, 1); /* this should not happen since sqlite3_prepare_v2 will not set ->vm on error */ 1965 | lua_error(L); 1966 | } 1967 | 1968 | if (nargs > 0) { 1969 | lua_replace(L, 1); 1970 | lua_remove(L, 2); /* stack: vm, args.. */ 1971 | 1972 | if (nargs == 1 && lua_istable(L, 2)) { 1973 | int result; 1974 | if ((result = dbvm_bind_table_fields (L, 2, nargs, svm->vm)) != SQLITE_OK) { 1975 | lua_pushstring(L, sqlite3_errstr(result)); 1976 | cleanupvm(L, svm); 1977 | lua_error(L); 1978 | } 1979 | } else if (nargs == sqlite3_bind_parameter_count(svm->vm)) { 1980 | int result, i; 1981 | for (i = 1; i <= nargs; i++) { 1982 | if ((result = dbvm_bind_index(L, svm->vm, i, i + 1)) != SQLITE_OK) { 1983 | lua_pushstring(L, sqlite3_errstr(result)); 1984 | cleanupvm(L, svm); 1985 | lua_error(L); 1986 | } 1987 | } 1988 | } else { 1989 | luaL_error(L, "Required either %d parameters or a single table, got %d.", 1990 | sqlite3_bind_parameter_count(svm->vm), nargs); 1991 | } 1992 | lua_pop(L, nargs); 1993 | lua_pushvalue(L, 1); 1994 | } 1995 | 1996 | lua_pushcfunction(L, f); 1997 | lua_insert(L, -2); 1998 | return 2; 1999 | } 2000 | 2001 | static int db_rows(lua_State *L) { 2002 | return db_do_rows(L, db_next_packed_row); 2003 | } 2004 | 2005 | static int db_nrows(lua_State *L) { 2006 | return db_do_rows(L, db_next_named_row); 2007 | } 2008 | 2009 | /* unpacked version of db:rows */ 2010 | static int db_urows(lua_State *L) { 2011 | return db_do_rows(L, db_next_row); 2012 | } 2013 | 2014 | static int db_tostring(lua_State *L) { 2015 | char buff[32]; 2016 | sdb *db = lsqlite_getdb(L, 1); 2017 | if (db->db == NULL) 2018 | strcpy(buff, "closed"); 2019 | else 2020 | sprintf(buff, "%p", lua_touserdata(L, 1)); 2021 | lua_pushfstring(L, "sqlite database (%s)", buff); 2022 | return 1; 2023 | } 2024 | 2025 | static int db_close(lua_State *L) { 2026 | sdb *db = lsqlite_checkdb(L, 1); 2027 | lua_pushinteger(L, cleanupdb(L, db)); 2028 | return 1; 2029 | } 2030 | 2031 | static int db_close_vm(lua_State *L) { 2032 | sdb *db = lsqlite_checkdb(L, 1); 2033 | /* cleanup temporary only tables? */ 2034 | int temp = lua_toboolean(L, 2); 2035 | 2036 | /* free associated virtual machines */ 2037 | lua_pushlightuserdata(L, db); 2038 | lua_rawget(L, LUA_REGISTRYINDEX); 2039 | 2040 | /* close all used handles */ 2041 | lua_pushnil(L); 2042 | while (lua_next(L, -2)) { 2043 | sdb_vm *svm = lua_touserdata(L, -2); /* key: vm; val: sql text */ 2044 | 2045 | if ((!temp || svm->temp) && svm->vm) 2046 | { 2047 | sqlite3_finalize(svm->vm); 2048 | svm->vm = NULL; 2049 | } 2050 | 2051 | /* leave key in the stack */ 2052 | lua_pop(L, 1); 2053 | } 2054 | return 0; 2055 | } 2056 | 2057 | /* From: Wolfgang Oertl 2058 | When using lsqlite3 in a multithreaded environment, each thread has a separate Lua 2059 | environment, but full userdata structures can't be passed from one thread to another. 2060 | This is possible with lightuserdata, however. See: lsqlite_open_ptr(). 2061 | */ 2062 | static int db_get_ptr(lua_State *L) { 2063 | sdb *db = lsqlite_checkdb(L, 1); 2064 | lua_pushlightuserdata(L, db->db); 2065 | return 1; 2066 | } 2067 | 2068 | static int db_gc(lua_State *L) { 2069 | sdb *db = lsqlite_getdb(L, 1); 2070 | if (db->db != NULL) /* ignore closed databases */ 2071 | cleanupdb(L, db); 2072 | return 0; 2073 | } 2074 | 2075 | /* 2076 | ** ======================================================= 2077 | ** General library functions 2078 | ** ======================================================= 2079 | */ 2080 | 2081 | static int lsqlite_version(lua_State *L) { 2082 | lua_pushstring(L, sqlite3_libversion()); 2083 | return 1; 2084 | } 2085 | 2086 | static int lsqlite_complete(lua_State *L) { 2087 | const char *sql = luaL_checkstring(L, 1); 2088 | lua_pushboolean(L, sqlite3_complete(sql)); 2089 | return 1; 2090 | } 2091 | 2092 | #ifndef _WIN32 2093 | static int lsqlite_temp_directory(lua_State *L) { 2094 | const char *oldtemp = sqlite3_temp_directory; 2095 | 2096 | if (!lua_isnone(L, 1)) { 2097 | const char *temp = luaL_optstring(L, 1, NULL); 2098 | if (sqlite3_temp_directory) { 2099 | sqlite3_free((char*)sqlite3_temp_directory); 2100 | } 2101 | if (temp) { 2102 | sqlite3_temp_directory = sqlite3_mprintf("%s", temp); 2103 | } 2104 | else { 2105 | sqlite3_temp_directory = NULL; 2106 | } 2107 | } 2108 | lua_pushstring(L, oldtemp); 2109 | return 1; 2110 | } 2111 | #endif 2112 | 2113 | #if defined(SQLITE_ENABLE_CEROD) 2114 | static int lsqlite_activate_cerod(lua_State *L) { 2115 | const char *s = luaL_checkstring(L, 1); 2116 | sqlite3_activate_cerod(s); 2117 | lua_pushnil(L); 2118 | return 0; 2119 | } 2120 | #endif 2121 | 2122 | static int lsqlite_do_open(lua_State *L, const char *filename, int flags) { 2123 | sdb *db = newdb(L); /* create and leave in stack */ 2124 | 2125 | if (SQLITE3_OPEN(filename, &db->db, flags) == SQLITE_OK) { 2126 | /* database handle already in the stack - return it */ 2127 | return 1; 2128 | } 2129 | 2130 | /* failed to open database */ 2131 | lua_pushnil(L); /* push nil */ 2132 | lua_pushinteger(L, sqlite3_errcode(db->db)); 2133 | lua_pushstring(L, sqlite3_errmsg(db->db)); /* push error message */ 2134 | 2135 | /* clean things up */ 2136 | cleanupdb(L, db); 2137 | 2138 | /* return */ 2139 | return 3; 2140 | } 2141 | 2142 | static int lsqlite_open(lua_State *L) { 2143 | const char *filename = luaL_checkstring(L, 1); 2144 | int flags = luaL_optinteger(L, 2, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); 2145 | return lsqlite_do_open(L, filename, flags); 2146 | } 2147 | 2148 | static int lsqlite_open_memory(lua_State *L) { 2149 | return lsqlite_do_open(L, ":memory:", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); 2150 | } 2151 | 2152 | /* From: Wolfgang Oertl 2153 | When using lsqlite3 in a multithreaded environment, each thread has a separate Lua 2154 | environment, but full userdata structures can't be passed from one thread to another. 2155 | This is possible with lightuserdata, however. See: db_get_ptr(). 2156 | */ 2157 | static int lsqlite_open_ptr(lua_State *L) { 2158 | sqlite3 *db_ptr; 2159 | sdb *db; 2160 | int rc; 2161 | 2162 | luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); 2163 | db_ptr = lua_touserdata(L, 1); 2164 | /* This is the only API function that runs sqlite3SafetyCheck regardless of 2165 | * SQLITE_ENABLE_API_ARMOR and does almost nothing (without an SQL 2166 | * statement) */ 2167 | rc = sqlite3_exec(db_ptr, NULL, NULL, NULL, NULL); 2168 | if (rc != SQLITE_OK) 2169 | luaL_argerror(L, 1, "not a valid SQLite3 pointer"); 2170 | 2171 | db = newdb(L); /* create and leave in stack */ 2172 | db->db = db_ptr; 2173 | return 1; 2174 | } 2175 | 2176 | static int lsqlite_newindex(lua_State *L) { 2177 | lua_pushliteral(L, "attempt to change readonly table"); 2178 | lua_error(L); 2179 | return 0; 2180 | } 2181 | 2182 | #ifndef LSQLITE_VERSION 2183 | /* should be defined in rockspec, but just in case... */ 2184 | #define LSQLITE_VERSION "unknown" 2185 | #endif 2186 | 2187 | /* Version number of this library 2188 | */ 2189 | static int lsqlite_lversion(lua_State *L) { 2190 | lua_pushstring(L, LSQLITE_VERSION); 2191 | return 1; 2192 | } 2193 | 2194 | /* 2195 | ** ======================================================= 2196 | ** Register functions 2197 | ** ======================================================= 2198 | */ 2199 | 2200 | #define SC(s) { #s, SQLITE_ ## s }, 2201 | #define LSC(s) { #s, LSQLITE_ ## s }, 2202 | 2203 | static const struct { 2204 | const char* name; 2205 | int value; 2206 | } sqlite_constants[] = { 2207 | /* error codes */ 2208 | SC(OK) SC(ERROR) SC(INTERNAL) SC(PERM) 2209 | SC(ABORT) SC(BUSY) SC(LOCKED) SC(NOMEM) 2210 | SC(READONLY) SC(INTERRUPT) SC(IOERR) SC(CORRUPT) 2211 | SC(NOTFOUND) SC(FULL) SC(CANTOPEN) SC(PROTOCOL) 2212 | SC(EMPTY) SC(SCHEMA) SC(TOOBIG) SC(CONSTRAINT) 2213 | SC(MISMATCH) SC(MISUSE) SC(NOLFS) 2214 | SC(FORMAT) SC(NOTADB) 2215 | 2216 | /* sqlite_step specific return values */ 2217 | SC(RANGE) SC(ROW) SC(DONE) 2218 | 2219 | /* column types */ 2220 | SC(INTEGER) SC(FLOAT) SC(TEXT) SC(BLOB) 2221 | SC(NULL) 2222 | 2223 | /* Authorizer Action Codes */ 2224 | SC(CREATE_INDEX ) 2225 | SC(CREATE_TABLE ) 2226 | SC(CREATE_TEMP_INDEX ) 2227 | SC(CREATE_TEMP_TABLE ) 2228 | SC(CREATE_TEMP_TRIGGER) 2229 | SC(CREATE_TEMP_VIEW ) 2230 | SC(CREATE_TRIGGER ) 2231 | SC(CREATE_VIEW ) 2232 | SC(DELETE ) 2233 | SC(DROP_INDEX ) 2234 | SC(DROP_TABLE ) 2235 | SC(DROP_TEMP_INDEX ) 2236 | SC(DROP_TEMP_TABLE ) 2237 | SC(DROP_TEMP_TRIGGER ) 2238 | SC(DROP_TEMP_VIEW ) 2239 | SC(DROP_TRIGGER ) 2240 | SC(DROP_VIEW ) 2241 | SC(INSERT ) 2242 | SC(PRAGMA ) 2243 | SC(READ ) 2244 | SC(SELECT ) 2245 | SC(TRANSACTION ) 2246 | SC(UPDATE ) 2247 | SC(ATTACH ) 2248 | SC(DETACH ) 2249 | SC(ALTER_TABLE ) 2250 | SC(REINDEX ) 2251 | SC(ANALYZE ) 2252 | SC(CREATE_VTABLE ) 2253 | SC(DROP_VTABLE ) 2254 | SC(FUNCTION ) 2255 | SC(SAVEPOINT ) 2256 | 2257 | /* file open flags */ 2258 | SC(OPEN_READONLY) 2259 | SC(OPEN_READWRITE) 2260 | SC(OPEN_CREATE) 2261 | SC(OPEN_URI) 2262 | SC(OPEN_MEMORY) 2263 | SC(OPEN_NOMUTEX) 2264 | SC(OPEN_FULLMUTEX) 2265 | SC(OPEN_SHAREDCACHE) 2266 | SC(OPEN_PRIVATECACHE) 2267 | 2268 | /* terminator */ 2269 | { NULL, 0 } 2270 | }; 2271 | 2272 | /* ======================================================= */ 2273 | 2274 | static const luaL_Reg dblib[] = { 2275 | {"isopen", db_isopen }, 2276 | {"last_insert_rowid", db_last_insert_rowid }, 2277 | {"changes", db_changes }, 2278 | {"total_changes", db_total_changes }, 2279 | {"errcode", db_errcode }, 2280 | {"error_code", db_errcode }, 2281 | {"errmsg", db_errmsg }, 2282 | {"error_message", db_errmsg }, 2283 | {"interrupt", db_interrupt }, 2284 | {"db_filename", db_db_filename }, 2285 | 2286 | {"create_function", db_create_function }, 2287 | {"create_aggregate", db_create_aggregate }, 2288 | {"create_collation", db_create_collation }, 2289 | {"load_extension", db_load_extension }, 2290 | 2291 | {"trace", db_trace }, 2292 | {"progress_handler", db_progress_handler }, 2293 | {"busy_timeout", db_busy_timeout }, 2294 | {"busy_handler", db_busy_handler }, 2295 | #if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK 2296 | {"update_hook", db_update_hook }, 2297 | {"commit_hook", db_commit_hook }, 2298 | {"rollback_hook", db_rollback_hook }, 2299 | #endif 2300 | 2301 | {"prepare", db_prepare }, 2302 | {"rows", db_rows }, 2303 | {"urows", db_urows }, 2304 | {"nrows", db_nrows }, 2305 | 2306 | {"exec", db_exec }, 2307 | {"execute", db_exec }, 2308 | {"close", db_close }, 2309 | {"close_vm", db_close_vm }, 2310 | {"get_ptr", db_get_ptr }, 2311 | 2312 | {"__tostring", db_tostring }, 2313 | {"__gc", db_gc }, 2314 | 2315 | {NULL, NULL} 2316 | }; 2317 | 2318 | static const luaL_Reg vmlib[] = { 2319 | {"isopen", dbvm_isopen }, 2320 | 2321 | {"step", dbvm_step }, 2322 | {"reset", dbvm_reset }, 2323 | {"finalize", dbvm_finalize }, 2324 | 2325 | {"columns", dbvm_columns }, 2326 | 2327 | {"bind", dbvm_bind }, 2328 | {"bind_values", dbvm_bind_values }, 2329 | {"bind_names", dbvm_bind_names }, 2330 | {"bind_blob", dbvm_bind_blob }, 2331 | {"bind_parameter_count",dbvm_bind_parameter_count}, 2332 | {"bind_parameter_name", dbvm_bind_parameter_name}, 2333 | 2334 | {"get_value", dbvm_get_value }, 2335 | {"get_values", dbvm_get_values }, 2336 | {"get_name", dbvm_get_name }, 2337 | {"get_names", dbvm_get_names }, 2338 | {"get_type", dbvm_get_type }, 2339 | {"get_types", dbvm_get_types }, 2340 | {"get_uvalues", dbvm_get_uvalues }, 2341 | {"get_unames", dbvm_get_unames }, 2342 | {"get_utypes", dbvm_get_utypes }, 2343 | 2344 | {"get_named_values", dbvm_get_named_values }, 2345 | {"get_named_types", dbvm_get_named_types }, 2346 | 2347 | {"rows", dbvm_rows }, 2348 | {"urows", dbvm_urows }, 2349 | {"nrows", dbvm_nrows }, 2350 | 2351 | {"last_insert_rowid", dbvm_last_insert_rowid }, 2352 | 2353 | /* compatibility names (added by request) */ 2354 | {"idata", dbvm_get_values }, 2355 | {"inames", dbvm_get_names }, 2356 | {"itypes", dbvm_get_types }, 2357 | {"data", dbvm_get_named_values }, 2358 | {"type", dbvm_get_named_types }, 2359 | 2360 | {"__tostring", dbvm_tostring }, 2361 | {"__gc", dbvm_gc }, 2362 | 2363 | { NULL, NULL } 2364 | }; 2365 | 2366 | static const luaL_Reg ctxlib[] = { 2367 | {"user_data", lcontext_user_data }, 2368 | 2369 | {"get_aggregate_data", lcontext_get_aggregate_context }, 2370 | {"set_aggregate_data", lcontext_set_aggregate_context }, 2371 | {"aggregate_count", lcontext_aggregate_count }, 2372 | 2373 | {"result", lcontext_result }, 2374 | {"result_null", lcontext_result_null }, 2375 | {"result_number", lcontext_result_double }, 2376 | {"result_double", lcontext_result_double }, 2377 | {"result_int", lcontext_result_int }, 2378 | {"result_text", lcontext_result_text }, 2379 | {"result_blob", lcontext_result_blob }, 2380 | {"result_error", lcontext_result_error }, 2381 | 2382 | {"__tostring", lcontext_tostring }, 2383 | {NULL, NULL} 2384 | }; 2385 | 2386 | static const luaL_Reg dbbulib[] = { 2387 | 2388 | {"step", dbbu_step }, 2389 | {"remaining", dbbu_remaining }, 2390 | {"pagecount", dbbu_pagecount }, 2391 | {"finish", dbbu_finish }, 2392 | 2393 | /* {"__tostring", dbbu_tostring }, */ 2394 | {"__gc", dbbu_gc }, 2395 | {NULL, NULL} 2396 | }; 2397 | 2398 | static const luaL_Reg sqlitelib[] = { 2399 | {"lversion", lsqlite_lversion }, 2400 | {"version", lsqlite_version }, 2401 | {"complete", lsqlite_complete }, 2402 | #ifndef _WIN32 2403 | {"temp_directory", lsqlite_temp_directory }, 2404 | #endif 2405 | #if defined(SQLITE_ENABLE_CEROD) 2406 | {"activate_cerod", lsqlite_activate_cerod }, 2407 | #endif 2408 | {"open", lsqlite_open }, 2409 | {"open_memory", lsqlite_open_memory }, 2410 | {"open_ptr", lsqlite_open_ptr }, 2411 | 2412 | {"backup_init", lsqlite_backup_init }, 2413 | 2414 | {"__newindex", lsqlite_newindex }, 2415 | {NULL, NULL} 2416 | }; 2417 | 2418 | static void create_meta(lua_State *L, const char *name, const luaL_Reg *lib) { 2419 | luaL_newmetatable(L, name); 2420 | lua_pushstring(L, "__index"); 2421 | lua_pushvalue(L, -2); /* push metatable */ 2422 | lua_rawset(L, -3); /* metatable.__index = metatable */ 2423 | 2424 | /* register metatable functions */ 2425 | luaL_openlib(L, NULL, lib, 0); 2426 | 2427 | /* remove metatable from stack */ 2428 | lua_pop(L, 1); 2429 | } 2430 | 2431 | LUALIB_API int luaopen_lsqlite3(lua_State *L) { 2432 | create_meta(L, sqlite_meta, dblib); 2433 | create_meta(L, sqlite_vm_meta, vmlib); 2434 | create_meta(L, sqlite_bu_meta, dbbulib); 2435 | create_meta(L, sqlite_ctx_meta, ctxlib); 2436 | 2437 | luaL_getmetatable(L, sqlite_ctx_meta); 2438 | sqlite_ctx_meta_ref = luaL_ref(L, LUA_REGISTRYINDEX); 2439 | 2440 | /* register (local) sqlite metatable */ 2441 | luaL_register(L, "sqlite3", sqlitelib); 2442 | 2443 | { 2444 | int i = 0; 2445 | /* add constants to global table */ 2446 | while (sqlite_constants[i].name) { 2447 | lua_pushstring(L, sqlite_constants[i].name); 2448 | lua_pushinteger(L, sqlite_constants[i].value); 2449 | lua_rawset(L, -3); 2450 | ++i; 2451 | } 2452 | } 2453 | 2454 | /* set sqlite's metatable to itself - set as readonly (__newindex) */ 2455 | lua_pushvalue(L, -1); 2456 | lua_setmetatable(L, -2); 2457 | 2458 | return 1; 2459 | } 2460 | -------------------------------------------------------------------------------- /container/src/lsqlite3.h: -------------------------------------------------------------------------------- 1 | #ifndef LSQLITE3_H 2 | #define LSQLITE3_H 3 | 4 | #include "lua.h" 5 | 6 | #define LUA_SQLLIBNAME "lsqlite3" 7 | LUAMOD_API int (luaopen_lsqlite3)(lua_State *L); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /container/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "lsqlite3.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | int boot_lua(lua_State* L); 14 | static lua_State *wasm_lua_state = NULL; 15 | 16 | // Pre-compiled lua loader program 17 | static const unsigned char program[] = {__LUA_BASE__}; 18 | // Pre-compiled entry script which user wrote 19 | static const unsigned char lua_main_program[] = {__LUA_MAIN__}; 20 | 21 | // This line will be injected by emcc-lua as export functions to WASM declaration 22 | __LUA_FUNCTION_DECLARATIONS__ 23 | 24 | // This function is for debug to see an C <-> Lua stack values 25 | // void dumpStack(lua_State *L) { 26 | // int i; 27 | // int stackSize = lua_gettop(L); 28 | // for (i = stackSize; i >= 1; i--) { 29 | // int stackType = lua_type(L, i); 30 | // printf("Stack[%2d-%10s]:", i, lua_typename(L, stackType)); 31 | // 32 | // switch (stackType) { 33 | // case LUA_TNUMBER: 34 | // printf("%f", lua_tonumber(L, i)); 35 | // break; 36 | // case LUA_TBOOLEAN: 37 | // if (lua_toboolean(L, i)) { 38 | // printf("true"); 39 | // } else { 40 | // printf("false"); 41 | // } 42 | // break; 43 | // case LUA_TSTRING: 44 | // printf("%s", lua_tostring(L, i)); 45 | // break; 46 | // case LUA_TNIL: 47 | // printf("nil"); 48 | // break; 49 | // default: 50 | // printf("%s", lua_typename(L, stackType)); 51 | // break; 52 | // } 53 | // printf("\n"); 54 | // } 55 | // printf("\n"); 56 | // } 57 | 58 | /* Copied from lua.c */ 59 | 60 | static lua_State *globalL = NULL; 61 | 62 | static void lstop (lua_State *L, lua_Debug *ar) { 63 | (void)ar; /* unused arg. */ 64 | lua_sethook(L, NULL, 0, 0); /* reset hook */ 65 | luaL_error(L, "interrupted!"); 66 | } 67 | 68 | static void laction (int i) { 69 | signal(i, SIG_DFL); /* if another SIGINT happens, terminate process */ 70 | lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1); 71 | } 72 | 73 | static int msghandler (lua_State *L) { 74 | const char *msg = lua_tostring(L, 1); 75 | if (msg == NULL) { /* is error object not a string? */ 76 | if (luaL_callmeta(L, 1, "__tostring") && /* does it have a metamethod */ 77 | lua_type(L, -1) == LUA_TSTRING) /* that produces a string? */ 78 | return 1; /* that is the message */ 79 | else 80 | msg = lua_pushfstring(L, "(error object is a %s value)", 81 | luaL_typename(L, 1)); 82 | } 83 | /* Call debug.traceback() instead of luaL_traceback() for Lua 5.1 compatibility. */ 84 | lua_getglobal(L, "debug"); 85 | lua_getfield(L, -1, "traceback"); 86 | /* debug */ 87 | lua_remove(L, -2); 88 | lua_pushstring(L, msg); 89 | /* original msg */ 90 | lua_remove(L, -3); 91 | lua_pushinteger(L, 2); /* skip this function and traceback */ 92 | lua_call(L, 2, 1); /* call debug.traceback */ 93 | return 1; /* return the traceback */ 94 | } 95 | 96 | static int docall (lua_State *L, int narg, int nres) { 97 | int status; 98 | int base = lua_gettop(L) - narg; /* function index */ 99 | lua_pushcfunction(L, msghandler); /* push message handler */ 100 | lua_insert(L, base); /* put it under function and args */ 101 | globalL = L; /* to be available to 'laction' */ 102 | signal(SIGINT, laction); /* set C-signal handler */ 103 | status = lua_pcall(L, narg, nres, base); 104 | signal(SIGINT, SIG_DFL); /* reset C-signal handler */ 105 | lua_remove(L, base); /* remove message handler from the stack */ 106 | return status; 107 | } 108 | 109 | // Boot function 110 | int main(void) { 111 | if (wasm_lua_state != NULL) { 112 | return 0; 113 | } 114 | wasm_lua_state = luaL_newstate(); 115 | if (boot_lua(wasm_lua_state)) { 116 | printf("failed to boot lua runtime\\n"); 117 | lua_close(wasm_lua_state); 118 | return 1; 119 | } 120 | //printf("Boot Lua Webassembly!\n"); 121 | return 0; 122 | } 123 | 124 | // boot lua runtime from compiled lua source 125 | int boot_lua(lua_State* L) { 126 | luaL_openlibs(L); 127 | 128 | // Preload lsqlite3 129 | luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); 130 | lua_pushcfunction(L, luaopen_lsqlite3); 131 | lua_setfield(L, -2, LUA_SQLLIBNAME); 132 | lua_pop(L, 1); // remove PRELOAD table 133 | 134 | if (luaL_loadbuffer(L, (const char*)program, sizeof(program), "main")) { 135 | fprintf(stderr, "error on luaL_loadbuffer()\n"); 136 | return 1; 137 | } 138 | lua_newtable(L); 139 | lua_pushlstring(L, (const char*)lua_main_program, sizeof(lua_main_program)); 140 | lua_setfield(L, -2, "__lua_webassembly__"); 141 | 142 | // This place will be injected by emcc-lua 143 | __INJECT_LUA_FILES__ 144 | 145 | if (docall(L, 1, LUA_MULTRET)) { 146 | const char *errmsg = lua_tostring(L, 1); 147 | if (errmsg) { 148 | fprintf(stderr, "%s\n", errmsg); 149 | } 150 | lua_close(L); 151 | return 1; 152 | } 153 | return 0; 154 | } -------------------------------------------------------------------------------- /container/src/main.lua: -------------------------------------------------------------------------------- 1 | -- This script is bridge program for WASM 2 | local args = {...} 3 | local lua_bundle = args[1] 4 | 5 | math.random = function() 6 | return 0.5 -- Replace with any value you want 7 | end 8 | -- Inline loader 9 | -- In WASM Lua, all Lua scripted will be compiled as byte string and set to lua_bundle table. 10 | -- Then, this loader will resolve by module name and evaluate it. 11 | local function _inline_loader(name) 12 | local mod = lua_bundle[name] or lua_bundle[name .. '.init'] 13 | if not mod then return ("module %s not found"):format(name) end 14 | if type(mod) == 'string' then 15 | local chunk, err = load(mod, name) 16 | if chunk then 17 | return chunk 18 | else 19 | error(("error loading module %s: %s"):format(name, err), 0) 20 | end 21 | elseif type(mod) == 'function' then 22 | return mod 23 | end 24 | end 25 | 26 | table.insert(package.loaders or package.searchers, 2, _inline_loader) 27 | 28 | -- The __lua_webassembly__ module will be inject via C program. 29 | local main = _inline_loader('__lua_webassembly__') 30 | main() 31 | 32 | -- Export function call wrapper 33 | -- function lua_call(function_name, ...) 34 | -- for k, v in pairs(exports) do 35 | -- print(k) 36 | -- end 37 | -- print(exports['hello_world']) 38 | -- print(#function_name) 39 | -- print(function_name == 'hello_world') 40 | -- print(function_name == 'hello_world') 41 | -- print(type(function_name)) 42 | -- 43 | -- local mod = exports[function_name] 44 | -- if not mod then 45 | -- print(("Module %s isn't exported"):format(function_name)) 46 | -- return 47 | -- elseif type(mod) ~= 'table' then 48 | -- print(("Module %s exported but not an table"):format(function_name)) 49 | -- return 50 | -- end 51 | -- 52 | -- local fn = mod.fn 53 | -- if not fn then 54 | -- print(("Module %s.fn is not defined"):format(function_name)) 55 | -- return 56 | -- elseif type(fn) ~= 'function' then 57 | -- print(("Module %s.fn is not a function"):format(function_name)) 58 | -- return 59 | -- end 60 | -- -- Call exported function 61 | -- local arguments = {select(1, ...)} 62 | -- return fn(table.unpack(arguments)) 63 | -- end -------------------------------------------------------------------------------- /container/src/node/apply-metering.cjs: -------------------------------------------------------------------------------- 1 | // this step will need to be invoked after the wasm file is compiled and it will load it and add 2 | // the metering functions to the wasm and replace it. 3 | const fs = require('fs') 4 | const metering = require('@permaweb/wasm-metering') 5 | const wasm = fs.readFileSync('/src/process.wasm') 6 | fs.writeFileSync('/src/process.wasm', metering.meterWASM(wasm)) -------------------------------------------------------------------------------- /container/src/node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "@permaweb/wasm-metering": "^0.2.2" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /container/src/node/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@permaweb/wasm-json-toolkit@^0.2.9": 6 | version "0.2.9" 7 | resolved "https://registry.yarnpkg.com/@permaweb/wasm-json-toolkit/-/wasm-json-toolkit-0.2.9.tgz#241cdf37c1690a751a73dd05987336142e5576d1" 8 | integrity sha512-CGCeUwS+UeqUdvORiyG0LykkQXLTwS5TWc590CUkDfOYyBUSPv8pse0sJStvTC9LKAzuNx3ELBvmqHCI4muUAA== 9 | dependencies: 10 | buffer-pipe "0.0.3" 11 | leb128 "0.0.4" 12 | safe-buffer "^5.1.2" 13 | 14 | "@permaweb/wasm-metering@^0.2.2": 15 | version "0.2.2" 16 | resolved "https://registry.yarnpkg.com/@permaweb/wasm-metering/-/wasm-metering-0.2.2.tgz#a854c485d9ddbbefb4a17a3692822b696d54a2c7" 17 | integrity sha512-xM2MbPkHc4rzhTR6VH5eXtfC+liaYSuNCa0kPRaqSZO2gr1SirJWnzUBDa5VOfTBTgIlIVv5RW+Mkbt/VuK+oA== 18 | dependencies: 19 | "@permaweb/wasm-json-toolkit" "^0.2.9" 20 | leb128 "^0.0.4" 21 | 22 | bn.js@^4.11.6: 23 | version "4.12.0" 24 | resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" 25 | integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== 26 | 27 | buffer-pipe@0.0.0: 28 | version "0.0.0" 29 | resolved "https://registry.yarnpkg.com/buffer-pipe/-/buffer-pipe-0.0.0.tgz#186ec257d696e8e74c3051160a0e9e9a9811a387" 30 | integrity sha512-PvKbsvQOH4dcUyUEvQQSs3CIkkuPcOHt3gKnXwf4HsPKFDxSN7bkmICVIWgOmW/jx/fAEGGn4mIayIJPLs7G8g== 31 | dependencies: 32 | safe-buffer "^5.1.1" 33 | 34 | buffer-pipe@0.0.3: 35 | version "0.0.3" 36 | resolved "https://registry.yarnpkg.com/buffer-pipe/-/buffer-pipe-0.0.3.tgz#242197681d4591e7feda213336af6c07a5ce2409" 37 | integrity sha512-GlxfuD/NrKvCNs0Ut+7b1IHjylfdegMBxQIlZHj7bObKVQBxB5S84gtm2yu1mQ8/sSggceWBDPY0cPXgvX2MuA== 38 | dependencies: 39 | safe-buffer "^5.1.2" 40 | 41 | leb128@0.0.4, leb128@^0.0.4: 42 | version "0.0.4" 43 | resolved "https://registry.yarnpkg.com/leb128/-/leb128-0.0.4.tgz#f96d698cf3ba5b677423abfe50b7e9b2df1463ff" 44 | integrity sha512-2zejk0fCIgY8RVcc/KzvyfpDio5Oo8HgPZmkrOmdwmbk0KpKpgD+JKwikxKk8cZYkANIhwHK50SNukkCm3XkCQ== 45 | dependencies: 46 | bn.js "^4.11.6" 47 | buffer-pipe "0.0.0" 48 | 49 | safe-buffer@^5.1.1, safe-buffer@^5.1.2: 50 | version "5.2.1" 51 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" 52 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 53 | -------------------------------------------------------------------------------- /container/src/pack.lua: -------------------------------------------------------------------------------- 1 | -- pack.lua 2 | -- released under the Romantic WTF Public License 3 | -- http://getmoai.com/wiki/index.php?title=User:Pygy/Romantic_WTF_Public_License 4 | 5 | args = {...} 6 | --require"luarocks.loader" 7 | fs = require"lfs" 8 | files = {} 9 | 10 | root = args[1]:gsub( "/$", "" ) 11 | :gsub( "\\$", "" ) 12 | 13 | function scandir (root, path) 14 | -- adapted from http://keplerproject.github.com/luafilesystem/examples.html 15 | path = path or "" 16 | for file in fs.dir( root..path ) do 17 | if file ~= "." and file ~= ".." then 18 | local f = path..'/'..file 19 | local attr = lfs.attributes( root..f ) 20 | assert (type( attr ) == "table") 21 | if attr.mode == "directory" then 22 | scandir( root, f ) 23 | else 24 | if file:find"%.lua$" then 25 | hndl = (f:gsub( "%.lua$", "" ) 26 | :gsub( "/", "." ) 27 | :gsub( "\\", "." ) 28 | :gsub( "^%.", "" ) 29 | ):gsub( "%.init$", "" ) 30 | files[hndl] = io.open( root..f ):read"*a" 31 | end 32 | end 33 | end 34 | end 35 | end 36 | 37 | scandir( root ) 38 | 39 | acc={} 40 | 41 | local wrapper = { "\n--------------------------------------\npackage.preload['" 42 | , nil, "'] = function (...)\n", nil, "\nend\n" } 43 | for k,v in pairs( files ) do 44 | wrapper[2], wrapper[4] = k, v 45 | table.insert( acc, table.concat(wrapper) ) 46 | end 47 | 48 | table.insert(acc, [[ 49 | ----------------------------------------------- 50 | 51 | do 52 | if not package.__loadfile then 53 | local original_loadfile = loadfile 54 | local function lf (file) 55 | local hndl = file:gsub( "%.lua$", "" ) 56 | :gsub( "/", "." ) 57 | :gsub( "\\", "." ) 58 | :gsub( "%.init$", "" ) 59 | return package.preload[hndl] or original_loadfile( name ) 60 | end 61 | 62 | function dofile (name) 63 | return lf( name )() 64 | end 65 | 66 | loadfile, package.__loadfile = lf, loadfile 67 | end 68 | end 69 | ]]) 70 | if files.main then table.insert( acc, '\ndofile"main.lua"' ) end 71 | print( table.concat( acc ) ) -------------------------------------------------------------------------------- /container/src/pre.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | Module.locateFile = (url) => { 4 | //console.log('file', url) 5 | return url 6 | } 7 | 8 | // Module.getBinaryPromise = (f) => { 9 | // conosle.log(f) 10 | // console.log('hyperbeam', Module['hyperbeam']) 11 | // return Promise.resolve('foo') 12 | // } -------------------------------------------------------------------------------- /deploy.js: -------------------------------------------------------------------------------- 1 | import { TurboFactory, ArweaveSigner } from '@ardrive/turbo-sdk'; 2 | import fs from "fs"; 3 | import path from 'path'; 4 | 5 | // load Arweave wallet 6 | const wallet = JSON.parse( 7 | fs.readFileSync(process.env.WALLET, 'utf-8') 8 | ); 9 | 10 | const main = async () => { 11 | const signer = new ArweaveSigner(wallet); 12 | const turbo = TurboFactory.authenticated({ signer }); 13 | const _file = path.resolve('./aos/process/process.wasm') 14 | const receipt = await turbo.uploadFile({ 15 | fileSizeFactory: () => fs.statSync(_file).size, 16 | fileStreamFactory: () => fs.createReadStream(_file), 17 | dataItemOpts: { 18 | tags: [ 19 | { name: 'Content-Type', value: 'application/wasm' }, 20 | { name: 'Data-Protocol', value: 'ao' }, 21 | { name: 'Type', value: 'Module' }, 22 | { name: 'Variant', value: 'ao.TN.1' }, 23 | { name: 'Module-Format', value: 'wasm64-unknown-emscripten-draft_2024_02_15' }, 24 | { name: 'Input-Encoding', value: 'JSON-1' }, 25 | { name: 'Output-Encoding', value: 'JSON-1' }, 26 | { name: 'Memory-Limit', value: '1-gb' }, 27 | { name: 'Compute-Limit', value: '9000000000000' } 28 | ] 29 | } 30 | }); 31 | console.log(receipt); 32 | console.log('ModuleID: ', receipt.id) 33 | } 34 | 35 | main() -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aos-sqlite", 3 | "type": "module", 4 | "version": "1.0.0", 5 | "main": "index.js", 6 | "repository": "https://github.com/twilson63/aos-sqlite.git", 7 | "author": "Tom Wilson ", 8 | "license": "MIT", 9 | "scripts": { 10 | "deploy": "node deploy.js" 11 | }, 12 | "devDependencies": { 13 | "@ar.io/sdk": "^2.0.2", 14 | "@ardrive/turbo-sdk": "^1.9.0", 15 | "@irys/sdk": "^0.1.24" 16 | } 17 | } 18 | --------------------------------------------------------------------------------