├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── alta-sanitise ├── main.go └── public │ ├── advanced.bmp │ ├── help.bmp │ ├── images │ ├── ad_banner.gif │ ├── av_advanced.gif │ ├── av_help.gif │ ├── av_logo.gif │ ├── av_personal (copy).gif │ ├── av_personal.gif │ ├── av_prefs.gif │ ├── av_simple.gif │ ├── digital-logo.gif │ ├── nav.gif │ ├── nav_on.gif │ ├── next_lb.gif │ ├── pixel.gif │ ├── powered_by.gif │ ├── prev_lb.gif │ ├── search.gif │ └── static_banner.gif │ ├── index.html │ ├── main.bmp │ ├── simple.bmp │ └── surprise.bmp ├── blog-gopher-bridge └── main.go ├── blog ├── blog.md ├── bsod.png ├── demo.png ├── example-search.png ├── flow.png ├── grafana.png ├── index-built.png ├── installer.png ├── random-crash.gif ├── rewritten-search.png ├── simple-search.png ├── tty-my-blog.gif └── tty-of-gopher.gif ├── crawler ├── crawl.go ├── database.go └── main.go ├── filesystem-gen ├── main.go └── mkfs.sh ├── go.mod ├── go.sum ├── rfc1436 ├── gopher.go ├── parse.go └── uri.go ├── vendor ├── github.com │ └── mattn │ │ └── go-sqlite3 │ │ ├── .codecov.yml │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── backup.go │ │ ├── callback.go │ │ ├── convert.go │ │ ├── doc.go │ │ ├── error.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── sqlite3-binding.c │ │ ├── sqlite3-binding.h │ │ ├── sqlite3.go │ │ ├── sqlite3_context.go │ │ ├── sqlite3_func_crypt.go │ │ ├── sqlite3_go18.go │ │ ├── sqlite3_libsqlite3.go │ │ ├── sqlite3_load_extension.go │ │ ├── sqlite3_load_extension_omit.go │ │ ├── sqlite3_opt_allow_uri_authority.go │ │ ├── sqlite3_opt_app_armor.go │ │ ├── sqlite3_opt_column_metadata.go │ │ ├── sqlite3_opt_foreign_keys.go │ │ ├── sqlite3_opt_fts5.go │ │ ├── sqlite3_opt_icu.go │ │ ├── sqlite3_opt_introspect.go │ │ ├── sqlite3_opt_json1.go │ │ ├── sqlite3_opt_preupdate.go │ │ ├── sqlite3_opt_preupdate_hook.go │ │ ├── sqlite3_opt_preupdate_omit.go │ │ ├── sqlite3_opt_secure_delete.go │ │ ├── sqlite3_opt_secure_delete_fast.go │ │ ├── sqlite3_opt_stat4.go │ │ ├── sqlite3_opt_unlock_notify.c │ │ ├── sqlite3_opt_unlock_notify.go │ │ ├── sqlite3_opt_userauth.go │ │ ├── sqlite3_opt_userauth_omit.go │ │ ├── sqlite3_opt_vacuum_full.go │ │ ├── sqlite3_opt_vacuum_incr.go │ │ ├── sqlite3_opt_vtable.go │ │ ├── sqlite3_other.go │ │ ├── sqlite3_solaris.go │ │ ├── sqlite3_trace.go │ │ ├── sqlite3_type.go │ │ ├── sqlite3_usleep_windows.go │ │ ├── sqlite3_windows.go │ │ ├── sqlite3ext.h │ │ └── static_mock.go └── modules.txt └── vm-harness └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | /.GOPATH 26 | /bin 27 | /data 28 | crawl.db 29 | crawl.db* 30 | *.tar 31 | blog-gopher-bridge/blog-gopher-bridge 32 | vm-harness/vm-harness 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ben Cox 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # The import path is where your repository can be found. 2 | # To import subpackages, always prepend the full import path. 3 | # If you change this, run `make clean`. Read more: https://git.io/vM7zV 4 | IMPORT_PATH := github.com/benjojo/gophervista 5 | 6 | # V := 1 # When V is set, print commands and build progress. 7 | 8 | # Space separated patterns of packages to skip in list, test, format. 9 | IGNORED_PACKAGES := /vendor/ 10 | 11 | .PHONY: build 12 | build: .GOPATH/.ok 13 | $Q go build $(if $V,-v) $(VERSION_FLAGS) -o bin/crawler $(IMPORT_PATH)/crawler 14 | $Q go build $(if $V,-v) $(VERSION_FLAGS) -o bin/filesystem-gen $(IMPORT_PATH)/filesystem-gen 15 | $Q go build $(if $V,-v) $(VERSION_FLAGS) -o bin/alta-sanitise $(IMPORT_PATH)/alta-sanitise 16 | $Q go build $(if $V,-v) $(VERSION_FLAGS) -o bin/blog-gopher-bridge $(IMPORT_PATH)/blog-gopher-bridge 17 | $Q go build $(if $V,-v) $(VERSION_FLAGS) -o bin/vm-harness $(IMPORT_PATH)/vm-harness 18 | 19 | 20 | ### Code not in the repository root? Another binary? Add to the path like this. 21 | # .PHONY: otherbin 22 | # otherbin: .GOPATH/.ok 23 | # $Q go install $(if $V,-v) $(VERSION_FLAGS) $(IMPORT_PATH)/cmd/otherbin 24 | 25 | ##### ^^^^^^ EDIT ABOVE ^^^^^^ ##### 26 | 27 | ##### =====> Utility targets <===== ##### 28 | 29 | .PHONY: clean test list cover format 30 | 31 | clean: 32 | $Q rm -rf bin .GOPATH 33 | 34 | test: .GOPATH/.ok 35 | $Q go test $(if $V,-v) -i -race $(allpackages) # install -race libs to speed up next run 36 | ifndef CI 37 | $Q go vet $(allpackages) 38 | $Q GODEBUG=cgocheck=2 go test -race $(allpackages) 39 | else 40 | $Q ( go vet $(allpackages); echo $$? ) | \ 41 | tee .GOPATH/test/vet.txt | sed '$$ d'; exit $$(tail -1 .GOPATH/test/vet.txt) 42 | $Q ( GODEBUG=cgocheck=2 go test -v -race $(allpackages); echo $$? ) | \ 43 | tee .GOPATH/test/output.txt | sed '$$ d'; exit $$(tail -1 .GOPATH/test/output.txt) 44 | endif 45 | 46 | list: .GOPATH/.ok 47 | @echo $(allpackages) 48 | 49 | cover: bin/gocovmerge .GOPATH/.ok 50 | @echo "NOTE: make cover does not exit 1 on failure, don't use it to check for tests success!" 51 | $Q rm -f .GOPATH/cover/*.out .GOPATH/cover/all.merged 52 | $(if $V,@echo "-- go test -coverpkg=./... -coverprofile=.GOPATH/cover/... ./...") 53 | @for MOD in $(allpackages); do \ 54 | go test -coverpkg=`echo $(allpackages)|tr " " ","` \ 55 | -coverprofile=.GOPATH/cover/unit-`echo $$MOD|tr "/" "_"`.out \ 56 | $$MOD 2>&1 | grep -v "no packages being tested depend on"; \ 57 | done 58 | $Q ./bin/gocovmerge .GOPATH/cover/*.out > .GOPATH/cover/all.merged 59 | ifndef CI 60 | $Q go tool cover -html .GOPATH/cover/all.merged 61 | else 62 | $Q go tool cover -html .GOPATH/cover/all.merged -o .GOPATH/cover/all.html 63 | endif 64 | @echo "" 65 | @echo "=====> Total test coverage: <=====" 66 | @echo "" 67 | $Q go tool cover -func .GOPATH/cover/all.merged 68 | 69 | format: bin/goimports .GOPATH/.ok 70 | $Q find .GOPATH/src/$(IMPORT_PATH)/ -iname \*.go | grep -v \ 71 | -e "^$$" $(addprefix -e ,$(IGNORED_PACKAGES)) | xargs ./bin/goimports -w 72 | 73 | ##### =====> Internals <===== ##### 74 | 75 | .PHONY: setup 76 | setup: clean .GOPATH/.ok 77 | 78 | 79 | VERSION := $(shell git describe --tags --always --dirty="-dev") 80 | DATE := $(shell date -u '+%Y-%m-%d-%H%M UTC') 81 | #VERSION_FLAGS := -ldflags='-extldflags=-static -X "main.Version=$(VERSION)" -X "main.BuildTime=$(DATE)"' 82 | VERSION_FLAGS := -ldflags='-X "main.Version=$(VERSION)" -X "main.BuildTime=$(DATE)"' 83 | NOSTATIC_VERSION_FLAGS := -ldflags='-X "main.Version=$(VERSION)" -X "main.BuildTime=$(DATE)"' 84 | 85 | # cd into the GOPATH to workaround ./... not following symlinks 86 | _allpackages = $(shell ( cd $(CURDIR)/.GOPATH/src/$(IMPORT_PATH) && \ 87 | GOPATH=$(CURDIR)/.GOPATH go list ./... 2>&1 1>&3 | \ 88 | grep -v -e "^$$" $(addprefix -e ,$(IGNORED_PACKAGES)) 1>&2 ) 3>&1 | \ 89 | grep -v -e "^$$" $(addprefix -e ,$(IGNORED_PACKAGES))) 90 | 91 | # memoize allpackages, so that it's executed only once and only if used 92 | allpackages = $(if $(__allpackages),,$(eval __allpackages := $$(_allpackages)))$(__allpackages) 93 | 94 | Q := $(if $V,,@) 95 | 96 | .GOPATH/.ok: 97 | 98 | # Based on https://github.com/cloudflare/hellogopher - v1.1 - MIT License 99 | # 100 | # Copyright (c) 2017 Cloudflare 101 | # 102 | # Permission is hereby granted, free of charge, to any person obtaining a copy 103 | # of this software and associated documentation files (the "Software"), to deal 104 | # in the Software without restriction, including without limitation the rights 105 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 106 | # copies of the Software, and to permit persons to whom the Software is 107 | # furnished to do so, subject to the following conditions: 108 | # 109 | # The above copyright notice and this permission notice shall be included in all 110 | # copies or substantial portions of the Software. 111 | # 112 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 113 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 114 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 115 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 116 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 117 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 118 | # SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | GopherVista ![powered-by-altavista](alta-sanitise/public/images/powered_by.gif) 2 | === 3 | 4 | A system/search engine infrastructure to run a search index for the gopher protocol. 5 | 6 | ![ExampleScreenshot](blog/rewritten-search.png) 7 | 8 | You can realistically find out more information about this over at the blog post: https://blog.benjojo.co.uk/post/building-a-search-engine-for-gopher 9 | 10 | -------------------------------------------------------------------------------- /alta-sanitise/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/tls" 5 | "database/sql" 6 | "flag" 7 | "fmt" 8 | "io/ioutil" 9 | "net/http" 10 | "net/url" 11 | "regexp" 12 | "strconv" 13 | "strings" 14 | 15 | "github.com/benjojo/gophervista/rfc1436" 16 | _ "github.com/mattn/go-sqlite3" 17 | ) 18 | 19 | var ( 20 | listen = flag.String("listen", ":9982", "Where to listen on") 21 | backend = flag.String("backend", "localhost:5555", "Where to listen on") 22 | dbpath = flag.String("dbpath", "../crawl.db", "Where to find the database") 23 | ) 24 | 25 | func main() { 26 | flag.Parse() 27 | 28 | http.Handle("/", http.FileServer(http.Dir("./public"))) 29 | http.HandleFunc("/query", backendSearchEntry) 30 | http.ListenAndServe(*listen, http.DefaultServeMux) 31 | } 32 | 33 | var titlematcher = regexp.MustCompile(`(\d+)\.(?:TXT|txt)`) 34 | var urlmatcher = regexp.MustCompile("href=(http[^>]+)") 35 | var url2matcher = regexp.MustCompile(`([DdeE]:\\\d+\\\d+\.(?:TXT|txt))`) 36 | var url3matcher = regexp.MustCompile("
]+)") 37 | 38 | func backendSearchEntry(rw http.ResponseWriter, req *http.Request) { 39 | if req.URL.Query().Get("q") == "" { 40 | http.Redirect(rw, req, "/", http.StatusTemporaryRedirect) 41 | return 42 | } 43 | 44 | stq := 0 45 | 46 | if req.URL.Query().Get("stq") != "" { 47 | i, err := strconv.ParseInt(req.URL.Query().Get("stq"), 10, 64) 48 | if err == nil { 49 | stq = int(i) 50 | } 51 | } 52 | 53 | tr := &http.Transport{ 54 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 55 | } 56 | client := &http.Client{Transport: tr} 57 | resp, err := client.Get(fmt.Sprintf("https://%s/?pg=q&what=0&q=%s&stq=%d", *backend, url.QueryEscape(req.URL.Query().Get("q")), stq)) 58 | if err != nil { 59 | http.Error(rw, "failed to get back out of the backend, may god help you", http.StatusInternalServerError) 60 | return 61 | } 62 | b, err := ioutil.ReadAll(resp.Body) 63 | if err != nil { 64 | http.Error(rw, "backend stopped serving halfway though, god help you and me", http.StatusInternalServerError) 65 | return 66 | } 67 | 68 | db, err := sql.Open("sqlite3", *dbpath) 69 | if err != nil { 70 | http.Error(rw, "db failed, god help you", http.StatusInternalServerError) 71 | return 72 | } 73 | 74 | err = db.Ping() // database do odd things 75 | if err != nil { 76 | http.Error(rw, "db is broken, god help me", http.StatusInternalServerError) 77 | return 78 | } 79 | 80 | htmlpage := string(b) 81 | // Fix up some of the URL and titles 82 | htmlpage = strings.Replace(htmlpage, "file:///C:\\Program Files\\DIGITAL\\AltaVista Search\\My Computer\\images\\", "/images/", -1) 83 | htmlpage = strings.Replace(htmlpage, "action=\"http://127.0.0.1:6688\"", "action=\"/query\"", -1) 84 | htmlpage = strings.Replace(htmlpage, "AltaVista Personal 97", "GopherVista 97 - The gopher search engine", -1) 85 | htmlpage = strings.Replace(htmlpage, "

Click here to contact AltaVista Support.", 101 | "\"[Digital", 102 | } 103 | 104 | for _, killstr := range removestrings { 105 | htmlpage = strings.Replace(htmlpage, killstr, "", -1) 106 | } 107 | 108 | // Now to start rewriting things with things that are in the DB 109 | lines := strings.Split(htmlpage, "\n") 110 | for _, line := range lines { 111 | if strings.Contains(line, "

") { 112 | title := titlematcher.FindAllStringSubmatch(line, 1) 113 | if len(title) != 1 { 114 | htmlpage = strings.Replace(htmlpage, line, "", -1) 115 | continue 116 | } 117 | 118 | dbid, _ := strconv.ParseInt(title[0][1], 10, 64) 119 | gopherstr := "" 120 | datatype := 0 121 | err := db.QueryRow("SELECT path,type FROM assets WHERE id=? LIMIT 1", int(dbid)).Scan(&gopherstr, &datatype) 122 | if err != nil { 123 | htmlpage = strings.Replace(htmlpage, line, "", -1) 124 | continue 125 | } 126 | p, hn, port, _ := rfc1436.ParseURI(gopherstr) 127 | gopherstr = fmt.Sprintf("gopher://%s:%d/%d%s", hn, port, datatype, p) 128 | 129 | htmlpage = strings.Replace(htmlpage, fmt.Sprintf("%d.txt", dbid), fmt.Sprintf("%s", gopherstr), -1) 130 | htmlpage = strings.Replace(htmlpage, fmt.Sprintf("%d.TXT", dbid), fmt.Sprintf("%s", gopherstr), -1) 131 | 132 | url1 := url2matcher.FindAllStringSubmatch(line, 1) 133 | if len(url1) != 1 { 134 | htmlpage = strings.Replace(htmlpage, line, "", -1) 135 | continue 136 | } 137 | htmlpage = strings.Replace(htmlpage, url1[0][1], gopherstr, -1) 138 | 139 | urls := urlmatcher.FindAllStringSubmatch(line, 1) 140 | htmlpage = strings.Replace(htmlpage, urls[0][1], gopherstr, 1) 141 | urls = url3matcher.FindAllStringSubmatch(line, 2) 142 | htmlpage = strings.Replace(htmlpage, urls[0][1], fmt.Sprintf("http://gopher.floodgap.com/gopher/gw?%s", url.QueryEscape(gopherstr)), 1) 143 | } 144 | } 145 | rw.Write([]byte(htmlpage)) 146 | } 147 | -------------------------------------------------------------------------------- /alta-sanitise/public/advanced.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/alta-sanitise/public/advanced.bmp -------------------------------------------------------------------------------- /alta-sanitise/public/help.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/alta-sanitise/public/help.bmp -------------------------------------------------------------------------------- /alta-sanitise/public/images/ad_banner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/alta-sanitise/public/images/ad_banner.gif -------------------------------------------------------------------------------- /alta-sanitise/public/images/av_advanced.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/alta-sanitise/public/images/av_advanced.gif -------------------------------------------------------------------------------- /alta-sanitise/public/images/av_help.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/alta-sanitise/public/images/av_help.gif -------------------------------------------------------------------------------- /alta-sanitise/public/images/av_logo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/alta-sanitise/public/images/av_logo.gif -------------------------------------------------------------------------------- /alta-sanitise/public/images/av_personal (copy).gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/alta-sanitise/public/images/av_personal (copy).gif -------------------------------------------------------------------------------- /alta-sanitise/public/images/av_personal.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/alta-sanitise/public/images/av_personal.gif -------------------------------------------------------------------------------- /alta-sanitise/public/images/av_prefs.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/alta-sanitise/public/images/av_prefs.gif -------------------------------------------------------------------------------- /alta-sanitise/public/images/av_simple.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/alta-sanitise/public/images/av_simple.gif -------------------------------------------------------------------------------- /alta-sanitise/public/images/digital-logo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/alta-sanitise/public/images/digital-logo.gif -------------------------------------------------------------------------------- /alta-sanitise/public/images/nav.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/alta-sanitise/public/images/nav.gif -------------------------------------------------------------------------------- /alta-sanitise/public/images/nav_on.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/alta-sanitise/public/images/nav_on.gif -------------------------------------------------------------------------------- /alta-sanitise/public/images/next_lb.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/alta-sanitise/public/images/next_lb.gif -------------------------------------------------------------------------------- /alta-sanitise/public/images/pixel.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/alta-sanitise/public/images/pixel.gif -------------------------------------------------------------------------------- /alta-sanitise/public/images/powered_by.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/alta-sanitise/public/images/powered_by.gif -------------------------------------------------------------------------------- /alta-sanitise/public/images/prev_lb.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/alta-sanitise/public/images/prev_lb.gif -------------------------------------------------------------------------------- /alta-sanitise/public/images/search.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/alta-sanitise/public/images/search.gif -------------------------------------------------------------------------------- /alta-sanitise/public/images/static_banner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/alta-sanitise/public/images/static_banner.gif -------------------------------------------------------------------------------- /alta-sanitise/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GopherVista 97 - The gopher search engine 6 | 7 | 8 |
9 | 10 | [AltaVista] 11 | [Advanced] 12 | [Simple] 13 | [Help] 14 |
15 |

16 |

17 | 18 | 27 | 28 | 31 |
19 |
20 | Search for documents containing
23 |
24 | 25 |
26 |
29 | Help . Preferences . 30 | New Search . Advanced Search
32 | 33 |



34 | 35 | 37 | 38 |
Digital Equipment Corporation 36 |

Copyright 1997 © All Rights Reserved
Powered By AltaVista
41 |
42 |
43 | Outside In Viewer Technology Copyright © 1992-1997 Inso Corporation 44 |
45 | 46 | 47 | -------------------------------------------------------------------------------- /alta-sanitise/public/main.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/alta-sanitise/public/main.bmp -------------------------------------------------------------------------------- /alta-sanitise/public/simple.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/alta-sanitise/public/simple.bmp -------------------------------------------------------------------------------- /alta-sanitise/public/surprise.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/alta-sanitise/public/surprise.bmp -------------------------------------------------------------------------------- /blog-gopher-bridge/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "io/ioutil" 8 | "log" 9 | "net" 10 | "net/http" 11 | "regexp" 12 | "strings" 13 | 14 | "io" 15 | 16 | "github.com/benjojo/gophervista/rfc1436" 17 | ) 18 | 19 | var ( 20 | blogbasepath = flag.String("blogpath", "https://blog.benjojo.co.uk", "Put the blog URL in here") 21 | gopherhostname = flag.String("gopherhost", "gopher.blog.benjojo.co.uk", "The target host of the links generated") 22 | port = flag.Int("port", 70, "port to listen on") 23 | errBlogFail = fmt.Errorf("Failure in blog fetch") 24 | markdownImgRegexp = regexp.MustCompile(`!\[([^\]]+)\]\(([^)]+)\)`) 25 | markdownLinkRegexp = regexp.MustCompile(`\[([^\]]+)\]\(([^)]+)\)`) 26 | listingLinkRegexp = regexp.MustCompile(`(.+)`) 27 | ) 28 | 29 | func main() { 30 | log.Printf("a") 31 | flag.Parse() 32 | l, err := net.Listen("tcp", ":70") 33 | if err != nil { 34 | log.Fatalf("unable to listen on gopher %s", err.Error()) 35 | } 36 | 37 | for { 38 | c, err := l.Accept() 39 | if err != nil { 40 | continue 41 | } 42 | 43 | go handleGopherConnection(c) 44 | } 45 | } 46 | 47 | func handleGopherConnection(c net.Conn) { 48 | defer c.Close() 49 | 50 | bio := bufio.NewReader(c) 51 | request, _, err := bio.ReadLine() 52 | if err != nil { 53 | return 54 | } 55 | 56 | log.Printf("Accepted request from %s for %s", c.RemoteAddr().String(), string(request)) 57 | 58 | if strings.HasPrefix(string(request), "/asset/") { 59 | getAssetContent(string(request), c) 60 | return 61 | } 62 | 63 | if string(request) == "" { 64 | getPostListings(c) 65 | return 66 | } 67 | 68 | blogpost, err := getBlogPost(string(request)) 69 | if err != nil { 70 | c.Write([]byte(rfc1436.TypeErr + `Failed to get that blog post fake fake 0 71 | `)) 72 | log.Printf("Could not get blog post due to %s", err.Error()) 73 | return 74 | } 75 | outputlines := make([]string, 0) 76 | 77 | lines := strings.Split(string(blogpost), "\n") 78 | for _, line := range lines { 79 | if len(markdownImgRegexp.FindAllStringSubmatch(line, -1)) != 0 { 80 | images := markdownImgRegexp.FindAllStringSubmatch(line, -1) 81 | for _, i := range images { 82 | line = strings.Replace(line, i[0], i[1], 1) 83 | path := strings.Replace(i[2], "https://blog.benjojo.co.uk/", "/", 1) 84 | outputlines = append(outputlines, fmt.Sprintf(rfc1436.TypeImage+"%s\t%s\t%s\t%d", i[1], path, *gopherhostname, *port)) 85 | } 86 | } 87 | if len(markdownLinkRegexp.FindAllStringSubmatch(line, -1)) != 0 { 88 | links := markdownLinkRegexp.FindAllStringSubmatch(line, -1) 89 | for _, i := range links { 90 | line = strings.Replace(line, i[0], i[1], 1) 91 | url := "URL:" + strings.Replace(i[2], "https://", "http://", 1) 92 | outputlines = append(outputlines, fmt.Sprintf("h%s\t%s\t%s\t0", i[1], url, url)) 93 | } 94 | } 95 | 96 | words := strings.Split(line, " ") 97 | linechars := 0 98 | linebuffer := "" 99 | for _, word := range words { 100 | if linechars+len(word)+2 > 70 { 101 | linechars = 0 102 | outputlines = append(outputlines, fmt.Sprintf(rfc1436.TypeMessage+"%s\tfake\tfake\t0", linebuffer)) 103 | linebuffer = word 104 | } else { 105 | linechars += len(word) + 2 106 | linebuffer = linebuffer + " " + word 107 | } 108 | } 109 | outputlines = append(outputlines, fmt.Sprintf(rfc1436.TypeMessage+"%s\tfake\tfake\t0", linebuffer)) 110 | } 111 | 112 | for _, ln := range outputlines { 113 | c.Write([]byte(ln + "\r\n")) 114 | } 115 | } 116 | 117 | func getBlogPost(path string) (blog string, err error) { 118 | req, _ := http.NewRequest("GET", fmt.Sprintf("%s/raw%s", *blogbasepath, path), nil) 119 | req.Header.Set("User-Agent", "gopher-blog-bridge") 120 | cl := http.Client{} 121 | res, err := cl.Do(req) 122 | if err != nil { 123 | return "", err 124 | } 125 | 126 | blogbytes, err := ioutil.ReadAll(res.Body) 127 | if err != nil { 128 | return "", err 129 | } 130 | 131 | if res.StatusCode != 200 { 132 | return "", errBlogFail 133 | } 134 | 135 | return string(blogbytes), nil 136 | } 137 | 138 | func getAssetContent(path string, c net.Conn) { 139 | req, _ := http.NewRequest("GET", fmt.Sprintf("%s%s", *blogbasepath, path), nil) 140 | req.Header.Set("User-Agent", "gopher-blog-bridge") 141 | cl := http.Client{} 142 | res, _ := cl.Do(req) 143 | io.Copy(c, res.Body) 144 | } 145 | 146 | func getPostListings(c net.Conn) { 147 | req, _ := http.NewRequest("GET", *blogbasepath, nil) 148 | req.Header.Set("User-Agent", "gopher-blog-bridge") 149 | cl := http.Client{} 150 | res, err := cl.Do(req) 151 | if err != nil { 152 | return 153 | } 154 | 155 | blogbytes, err := ioutil.ReadAll(res.Body) 156 | if err != nil { 157 | return 158 | } 159 | 160 | c.Write([]byte(fmt.Sprintf(rfc1436.TypeMessage + "Welcome to Gopher blog.Benjojo.co.uk\tfake\tfake\t0\r\n"))) 161 | 162 | lines := strings.Split(string(blogbytes), "\n") 163 | for _, ln := range lines { 164 | if len(listingLinkRegexp.FindAllStringSubmatch(ln, -1)) != 0 && 165 | strings.Contains(ln, "") { 166 | 167 | lnk := listingLinkRegexp.FindAllStringSubmatch(ln, -1) 168 | for _, i := range lnk { 169 | translate := strings.Replace(i[1], "/post", "", 1) 170 | i[2] = strings.Replace(i[2], "", "", 1) 171 | c.Write([]byte(fmt.Sprintf(rfc1436.TypeMenuEntity+"%s\t%s\t%s\t%d\r\n", i[2], "/"+translate, *gopherhostname, *port))) 172 | } 173 | } 174 | 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /blog/blog.md: -------------------------------------------------------------------------------- 1 | Building a legacy search engine for a legacy protocol 2 | === 3 | 4 | Most users who use the internet today, are mostly focused on three protocols, HTTP, TLS, and DNS. 5 | 6 | While these users may not care these days how their pages are displayed to them, there was once a competing protocol to the one that we known and love that is HTTP. 7 | 8 | Located just 10 ports down from HTTP is [Gopher](https://www.ietf.org/rfc/rfc1436.txt), on TCP port 70. A protocol that looks a lot like a much more basic HTTP/1.0. 9 | 10 | While a basic HTTP/1.0 request may look like this: 11 | 12 | ``` 13 | 14 | $ nc blog.benjojo.co.uk 80 15 | GET / HTTP/1.0 16 | 17 | 18 | HTTP/1.1 403 Forbidden 19 | Date: Wed, 03 May 2017 19:11:28 GMT 20 | Content-Type: text/html; charset=UTF-8 21 | Connection: close 22 | 23 | ``` 24 | 25 | A gopher request is even more basic: 26 | 27 | ``` 28 | $ echo "/archive" | nc gopher.floodgap.com 70 29 | 1Floodgap Systems gopher root / gopher.floodgap.com 70 30 | i error.host 1 31 | iWelcome to the Floodgap gopher files archive. This contains error.host 1 32 | imirrors of popular or departed archives that we believe useful error.host 1 33 | ``` 34 | 35 | This is great for basic file transfers, as basic bash utilities can be used to download files! 36 | 37 | However in the end, HTTP won out over gopher and it became the protocol that most of us use to do things on the internet. The reasons why are interesting, however others have told that story much better, [you can find a good write up here](https://www.minnpost.com/business/2016/08/rise-and-fall-gopher-protocol). 38 | 39 | Search engines exist for HTTP, and gopher itself has support for searching in the protocol, but all the search engines for gopher are in "gopherspace". None really existed in HTTP (that I could find). 40 | 41 | I had a [friend](https://twitter.com/2sec4u) run a [zmap](https://github.com/zmap/zmap) scan over the internet for port 70, and then filtered the results for real gopher servers: 42 | 43 | ``` 44 | [ben@aura tmp]$ pv gopher.raw | grep 'read":"i' | jq .ip | sort -n | uniq -c | wc -l 45 | 2.14GiB 0:00:08 [ 254MiB/s] [================>] 100% 46 | 370 47 | ``` 48 | 49 | A sad 370 servers are left on the internet that serve gopher. 50 | 51 | ## Building a crawler 52 | 53 | I wrote a simple RFC1436 implementation and crawler, and slowly (there are very old servers behind some of these hosts) began crawling all the menus, known as selectors, and text files I could find. 54 | 55 | At this point I started to explore gopher space itself, and I have to say, it's a wonderful place of just pure content, a far cry away from the modern internet where CSS and adtech are stuffed in every corner. 56 | 57 | ![A ttygif of using gopher](tty-of-gopher.gif) 58 | 59 | ## Indexing the content 60 | 61 | Given that gopher is from the 1990s, it feels only right to use search engine tech from the era. As it happens [AltaVista](https://en.wikipedia.org/wiki/AltaVista) once sold a personal/home version of their search engine for 62 | desktop computers. The issue however is that it's win32 only software. I didn't try running it on wine, instead I aimed for a more authentic experience of running the software: using an already [fantastic guide from NeoZeed](https://virtuallyfun.superglobalmegacorp.com/2017/02/25/personal-altavista-utzoo-reloaded/) I ended up provisioning my very own Windows 98 search "server". 63 | 64 | 65 | 66 | 67 | ![altavista being installed](installer.png) 68 | 69 | The search engine works: 70 | 71 | ![altavista test search](example-search.png) 72 | 73 | As NeoZeed found out, the search interface only listens on loopback (hardcoded), which is very annoying if you want to expose it to the wider world! To solve this [stunnel](https://www.stunnel.org/index.html) 74 | was deployed to listen on * and relay connections back to the local instance, with added SSL! [Using a pretty questionable (but CT logged) default SSL certificate too](https://crt.sh/?id=130496527) [(A)](http://archive.is/WJNYb)! 75 | 76 | ## Providing data to the indexer 77 | 78 | Because the indexer and backend is a Windows 98 QEMU VM, there has to be a way of giving that VM approximately 5GB of tiny files for the AltaVista indexer to see and include in the index. For this, I chose to make a FAT32 file system every 24 hours as a snapshot of the crawled data, and then restart the crawling VM to see the new files. This worked really quite well in testing with a small amount of files, however a few issues became apparent: first of all FAT32 has file limits that need to be paid attention to. For example, FAT32 is not able to have more than 255 files in the root directory, so you have to be sure to spread out your files in folder structures. 79 | 80 | Another issue to keep in mind is that the maximum drive size for FAT32 is 32GB (approx). This means the amount of textual content can't go bigger than that (or we would have to spawn more virtual drives). Fortunately, the size of the crawled content is far below that so this is a non issue. 81 | 82 | While crawling a "production size" data set, the system would reset at a random point during the indexing program running: 83 | 84 | ![gif of windows 98 and the cralwer randomly crashing](random-crash.gif) 85 | 86 | After tweaking the settings involving the disk caches, a slightly more constructive error was obtained: 87 | 88 | ![A blue screen of death](bsod.png) 89 | 90 | This is good! I can search for `exception 05 has occurred at 0028:C2920074`! Right? As it happens, there is very little information about this kind of crash on the internet (it may have existed at some point in the past, but since been removed; after all, the OS is 20 years old), however the one piece of information I could gather from searching is that it was VFAT driver related. Suspecting a bad combo between high IO load and QEMU's [INT_13](https://en.wikipedia.org/wiki/INT_13H) implementation, I went for the only other file system/data input system available, CD/DVD ROM! 91 | 92 | After doing a small 500MB test (a test that FAT32 could not pass) we had a small index! 93 | 94 | ![The test index built](index-built.png) 95 | 96 | At this point we had to scale up the solution to the 300k / 4 GB of files. I discovered that Windows 98 does support DVDs, even though the UI can only display that the drive is 2GB, even if the drive is much larger than that. Despite that, all content was accessible on the drive and an initial index was (slowly) built. 97 | 98 | ## Sanitise the index interface 99 | 100 | The only problem with using a 20 year old indexer, is that it's likely a **very** bad idea to expose directly to the internet. The other issue is that on most of the pages the interface serves references local (as in, `file://`) assets, meaning that a simple reverse proxy would not work. 101 | 102 | In addition, local paths are not very useful to people searching. For this `alta-sanitise` was written to provide a sane front end to it, while still keeping the Windows 98 AltaVista index as it's backend. 103 | 104 | To do this, I produce a file system containing all the files that were downloaded, and name them by their database ID: 105 | 106 | ![A sample search on the unaltered interface](simple-search.png) 107 | 108 | However in alta-sanitise, we use the database we formed using crawling, to rewrite the URL's into something viewable: 109 | 110 | ![A sample search on the production interface](rewritten-search.png) 111 | 112 | To ensure the VM could be used for more than one project, lighttpd was put in front as a reverse proxy of `alta-sanitise`, and Cloudflare used for cache. This leaves the final flow looking like this: 113 | 114 | ![Final flow](flow.png) 115 | 116 | # Monitoring Windows 98 117 | 118 | Most of my servers are monitored using collectd. Unfortunately there is no Windows 98 client for collectd (!?), so I decided to make one. 119 | 120 | A simple Visual Basic 6 application will poll every 10 seconds and output [collectd command strings](https://collectd.org/wiki/index.php/Plain_text_protocol#PUTVAL) over the serial port (where it can be passed on to collectd on the hypervisor): 121 | 122 | ![VB6 and the serial output](demo.png) 123 | 124 | ![Grafana screenshot](grafana.png) 125 | 126 | This code can be found separately at: https://github.com/benjojo/win32-collectd 127 | 128 | (Some troubled soul may find this useful outside of a gopher crawler.) 129 | 130 | # Giving back to the community 131 | 132 | Now that I have a sizeable index of the gopher space, I feel like I should give back to gopher space: 133 | 134 | ![my blog on gopher](tty-my-blog.gif) 135 | 136 | You can now find my blog on gopher at `gopher.blog.benjojo.co.uk` (** only accessible on gopher, Lynx supports gopher if that helps **). 137 | 138 | The search engine can be used at: http://gophervista.benjojo.co.uk 139 | 140 | And the code can be found at: https://github.com/benjojo/gophervista -------------------------------------------------------------------------------- /blog/bsod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/blog/bsod.png -------------------------------------------------------------------------------- /blog/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/blog/demo.png -------------------------------------------------------------------------------- /blog/example-search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/blog/example-search.png -------------------------------------------------------------------------------- /blog/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/blog/flow.png -------------------------------------------------------------------------------- /blog/grafana.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/blog/grafana.png -------------------------------------------------------------------------------- /blog/index-built.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/blog/index-built.png -------------------------------------------------------------------------------- /blog/installer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/blog/installer.png -------------------------------------------------------------------------------- /blog/random-crash.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/blog/random-crash.gif -------------------------------------------------------------------------------- /blog/rewritten-search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/blog/rewritten-search.png -------------------------------------------------------------------------------- /blog/simple-search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/blog/simple-search.png -------------------------------------------------------------------------------- /blog/tty-my-blog.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/blog/tty-my-blog.gif -------------------------------------------------------------------------------- /blog/tty-of-gopher.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/blog/tty-of-gopher.gif -------------------------------------------------------------------------------- /crawler/crawl.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/base32" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "os" 9 | "time" 10 | 11 | "github.com/benjojo/gophervista/rfc1436" 12 | ) 13 | 14 | func crawl(Queue crawlerQueue, datadir string) { 15 | var workers map[string]chan string 16 | workers = make(map[string]chan string) 17 | indexn := 0 18 | skiphost := "impossibleaaaaaaaaaaaaaaaaaa" 19 | 20 | for { 21 | time.Sleep(time.Millisecond * 5) 22 | var asset string 23 | asset, indexn = Queue.GetItemToCrawlFromIndex(indexn, skiphost) 24 | if asset == "" { 25 | log.Print("Nothing to crawl...") 26 | indexn = 0 27 | continue 28 | } 29 | indexn++ 30 | 31 | canCrawl, _ := Queue.CheckItemPerms(asset) 32 | 33 | if !canCrawl { 34 | log.Printf("Can't crawl %s because of restriction", asset) 35 | Queue.FlagItemAsCrawled(asset) 36 | continue 37 | } 38 | 39 | _, hn, _, err := rfc1436.ParseURI(asset) 40 | if err != nil { 41 | log.Printf("Invalid URI, %s", err.Error()) 42 | continue 43 | } 44 | 45 | if workers[hn] == nil { 46 | ch := make(chan string, 1) 47 | workers[hn] = ch 48 | go crawlWorker(ch, Queue, datadir) 49 | go crawlerFeeder(ch, hn, Queue) 50 | } 51 | select { 52 | case workers[hn] <- asset: // ignore if it's full. 53 | skiphost = "impossibleaaaaaaaaaaa" 54 | default: 55 | skiphost = hn 56 | } 57 | 58 | } 59 | } 60 | 61 | func crawlerFeeder(assetChan chan string, hn string, Queue crawlerQueue) { 62 | indexn := 0 63 | totallydry := 0 64 | 65 | for { 66 | time.Sleep(time.Millisecond * 500) 67 | var asset string 68 | asset, indexn = Queue.GetItemToCrawlFromIndexFromHost(indexn, hn) 69 | if asset == "" { 70 | log.Printf("Nothing to crawl, Reseting pointer for %s", hn) 71 | indexn = 0 72 | totallydry++ 73 | if totallydry > 1 { 74 | time.Sleep(time.Minute) 75 | log.Printf("Really lothing left to crawl for %s, Chilling back for a bit", hn) 76 | } 77 | continue 78 | } 79 | indexn++ 80 | 81 | canCrawl, _ := Queue.CheckItemPerms(asset) 82 | 83 | if !canCrawl { 84 | log.Printf("Can't crawl %s because of restriction", asset) 85 | Queue.FlagItemAsCrawled(asset) 86 | continue 87 | } 88 | 89 | assetChan <- asset 90 | totallydry = 0 91 | } 92 | } 93 | 94 | func crawlWorker(assetChan chan string, Queue crawlerQueue, datadir string) { 95 | for asset := range assetChan { 96 | if asset == "" { 97 | log.Print("Nothing to crawl...") 98 | continue 99 | } 100 | 101 | _, canLearn := Queue.CheckItemPerms(asset) 102 | 103 | time.Sleep(time.Second) 104 | 105 | d, ci, err := rfc1436.Get(asset) 106 | log.Printf("Grabbing %s", asset) 107 | if err != nil { 108 | log.Printf("Failed to crawl '%s' - %s", asset, err.Error()) 109 | Queue.FlagItemAsCrawled(asset) 110 | continue 111 | } 112 | 113 | storefolder := fmt.Sprintf("%s/raw/%s-%d/", datadir, ci.Hostname, ci.Port) 114 | err = os.MkdirAll(storefolder, 0755) 115 | if err != nil && err != os.ErrExist { 116 | log.Printf("Unable to make directory structure (%s) to store responce in, %s", storefolder, err.Error()) 117 | } 118 | 119 | ioutil.WriteFile(storefolder+base32shortcut(asset), d.Raw, 0755) 120 | 121 | for _, ln := range d.Items { 122 | if ln.Type == rfc1436.TypeMenuEntity || 123 | ln.Type == rfc1436.TypeTextFile { 124 | newpath := fmt.Sprintf("gopher://%s:%d%s", ln.Host, ln.Port, ln.Path) 125 | if canLearn { 126 | Queue.FlagItem(newpath, ln.Type) 127 | } else { 128 | log.Printf("Can't add %s to crawl list, Domain has restriction in place", newpath) 129 | } 130 | } 131 | } 132 | Queue.FlagItemAsCrawled(asset) 133 | } 134 | } 135 | 136 | func base32shortcut(path string) string { 137 | return base32.StdEncoding.EncodeToString([]byte(path)) 138 | } 139 | -------------------------------------------------------------------------------- /crawler/database.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "log" 7 | "regexp" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | type crawlerQueue struct { 13 | db *sql.DB 14 | dblock sync.Mutex 15 | flagchan chan FlagRequest 16 | } 17 | 18 | func (cq *crawlerQueue) Init(databasepath string) error { 19 | db, err := sql.Open("sqlite3", databasepath) 20 | if err != nil { 21 | return err 22 | } 23 | 24 | err = db.Ping() // database do odd things 25 | if err != nil { 26 | return err 27 | } 28 | 29 | cq.db = db 30 | 31 | cq.maketableifnotexists("assets", 32 | "CREATE TABLE assets (id INTEGER PRIMARY KEY, timestamp NUMERIC, path TEXT, type TEXT, lastcrawled NUMERIC);") 33 | 34 | cq.maketableifnotexists("restrictions", 35 | "CREATE TABLE `restrictions` (`id` INTEGER,`pattern` TEXT,`nolearn` INTEGER DEFAULT 0,`nocrawl` INTEGER DEFAULT 0,PRIMARY KEY(id));") 36 | 37 | cq.flagchan = make(chan FlagRequest, 1) 38 | go cq.FlagChannelConsumer(cq.flagchan) 39 | 40 | return nil 41 | } 42 | 43 | func (cq *crawlerQueue) maketableifnotexists(table, makestring string) { 44 | tablename := "" 45 | cq.db.QueryRow("SELECT name FROM sqlite_master WHERE type='table' AND name=?;", table).Scan(&tablename) 46 | 47 | if tablename != table { 48 | log.Printf("Made a new %s table", table) 49 | cq.db.Exec(makestring) 50 | } 51 | 52 | } 53 | 54 | func (cq *crawlerQueue) GetItemToCrawl() string { 55 | cq.dblock.Lock() 56 | defer cq.dblock.Unlock() 57 | asset := "" 58 | err := cq.db.QueryRow("SELECT path FROM assets WHERE lastcrawled < ? LIMIT 1", time.Now().Unix()-604800).Scan(&asset) 59 | if err != nil { 60 | return "" 61 | } 62 | return asset 63 | } 64 | 65 | func (cq *crawlerQueue) GetItemToCrawlFromIndex(indexin int, skiphost string) (asset string, index int) { 66 | cq.dblock.Lock() 67 | defer cq.dblock.Unlock() 68 | err := cq.db.QueryRow("SELECT path,id FROM assets WHERE lastcrawled < ? AND id > ? AND path NOT LIKE ? LIMIT 1", 69 | time.Now().Unix()-604800, indexin, fmt.Sprintf("%%%s%%", skiphost)).Scan(&asset, &index) 70 | if err != nil { 71 | return "", 0 72 | } 73 | return asset, index 74 | } 75 | 76 | func (cq *crawlerQueue) GetItemToCrawlFromIndexFromHost(indexin int, targethost string) (asset string, index int) { 77 | cq.dblock.Lock() 78 | defer cq.dblock.Unlock() 79 | err := cq.db.QueryRow("SELECT path,id FROM assets WHERE lastcrawled < ? AND id > ? AND path LIKE ? LIMIT 1", 80 | time.Now().Unix()-604800, indexin, fmt.Sprintf("%%%s%%", targethost)).Scan(&asset, &index) 81 | if err != nil { 82 | return "", 0 83 | } 84 | return asset, index 85 | } 86 | 87 | func (cq *crawlerQueue) FlagItemAsCrawled(path string) { 88 | _, err := cq.db.Exec("UPDATE assets SET lastcrawled = ? WHERE path = ?", time.Now().Unix(), path) 89 | if err != nil { 90 | log.Printf("Unable to flag asset as crawled? %s", err.Error()) 91 | if err.Error() == "database is locked" { // aaaa a aaaa aaaaaaaa terrible i'm so sorry 92 | log.Printf("Retrying in a moment due to %s", err.Error()) 93 | go func() { 94 | // Well we are already doing sins here, so what's one more? 95 | time.Sleep(time.Second) 96 | cq.FlagItemAsCrawled(path) 97 | }() 98 | } 99 | } 100 | } 101 | 102 | type FlagRequest struct { 103 | Path string 104 | GTypeStr string 105 | } 106 | 107 | func (cq *crawlerQueue) FlagItem(path string, gtypestr string) { 108 | cq.flagchan <- FlagRequest{ 109 | Path: path, 110 | GTypeStr: gtypestr, 111 | } 112 | } 113 | 114 | func (cq *crawlerQueue) FlagChannelConsumer(in chan FlagRequest) { 115 | for rq := range in { 116 | cq.dblock.Lock() 117 | test := 0 118 | err := cq.db.QueryRow("SELECT COUNT(*) path FROM assets WHERE path = ?;", rq.Path).Scan(&test) 119 | if err != nil { 120 | log.Printf("Huh, unable to see if an item to be should be crawled - %s", err.Error()) 121 | continue 122 | } 123 | 124 | if test == 0 { 125 | log.Printf("Found item to add %s", rq.Path) 126 | 127 | // Fantastic, We can add it 128 | _, err = cq.db.Exec("INSERT INTO `assets` (`id`,`timestamp`,`path`,`type`,`lastcrawled`) VALUES (NULL,?,?,?,0);", time.Now().Unix(), rq.Path, rq.GTypeStr) 129 | if err != nil { 130 | log.Printf("Huh, unable to flag to be should be crawled - %s", err.Error()) 131 | continue 132 | } 133 | } 134 | cq.dblock.Unlock() 135 | } 136 | } 137 | 138 | type permissionRow struct { 139 | Pattern *regexp.Regexp 140 | CanLearn bool 141 | CanCrawl bool 142 | } 143 | 144 | var permissionsCache []permissionRow 145 | 146 | func (cq *crawlerQueue) CheckItemPerms(path string) (canCrawl bool, canLearn bool) { 147 | if len(permissionsCache) == 0 || permissionsCache == nil { 148 | log.Printf("Loading perms") 149 | permissionsCache = make([]permissionRow, 0) 150 | rows, err := cq.db.Query("SELECT pattern,nolearn,nocrawl FROM restrictions;") 151 | if err != nil { 152 | log.Fatalf("Unable to load crawling restrictions/perms, %s", err.Error()) 153 | } 154 | 155 | for rows.Next() { 156 | var iCanLearn, iCanCrawl int 157 | var RERaw string 158 | 159 | err := rows.Scan(&RERaw, &canLearn, &canCrawl) 160 | if err != nil { 161 | log.Fatalf("Unable to process crawling restrictions/perms, %s", err.Error()) 162 | } 163 | 164 | RE := regexp.MustCompile(RERaw) 165 | 166 | n := permissionRow{ 167 | Pattern: RE, 168 | CanCrawl: iCanCrawl == 0, 169 | CanLearn: iCanLearn == 0, 170 | } 171 | 172 | permissionsCache = append(permissionsCache, n) 173 | } 174 | log.Printf("%d perm restrictions loaded", len(permissionsCache)) 175 | } 176 | 177 | // Now that it's all loaded... 178 | 179 | for _, testcase := range permissionsCache { 180 | if testcase.Pattern.MatchString(path) { 181 | return !testcase.CanCrawl, !testcase.CanLearn 182 | } 183 | } 184 | 185 | return true, true 186 | } 187 | -------------------------------------------------------------------------------- /crawler/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "os" 7 | 8 | _ "github.com/mattn/go-sqlite3" 9 | ) 10 | 11 | var ( 12 | databasepath = flag.String("dbpath", "./crawl.db", "Where to store the metadata of crawling") 13 | datapath = flag.String("datadir", "./data", "where to download assets to") 14 | ) 15 | 16 | func main() { 17 | flag.Parse() 18 | 19 | ledger := crawlerQueue{} 20 | err := ledger.Init(*databasepath) 21 | if err != nil { 22 | log.Fatalf("Unable to open database. %s", err.Error()) 23 | } 24 | 25 | crawl(ledger, *datapath) 26 | os.Exit(1) 27 | } 28 | -------------------------------------------------------------------------------- /filesystem-gen/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "encoding/base32" 6 | "flag" 7 | "fmt" 8 | "io" 9 | "log" 10 | "os" 11 | "path/filepath" 12 | 13 | _ "github.com/mattn/go-sqlite3" 14 | ) 15 | 16 | func main() { 17 | dbpath := flag.String("dbpath", "./crawl.db", "Where to find the database") 18 | opath := flag.String("outputpath", "./cds/", "Where to put the folders") 19 | flag.Parse() 20 | log.Printf("Making filesystem...") 21 | 22 | db, err := sql.Open("sqlite3", *dbpath) 23 | if err != nil { 24 | log.Fatalf("unable to open db %s", err.Error()) 25 | } 26 | 27 | err = db.Ping() // database do odd things 28 | if err != nil { 29 | log.Fatalf("unable to check db %s", err.Error()) 30 | } 31 | 32 | filesmoved := 0 33 | cd := 0 34 | bytesOnDisk := 0 35 | 36 | os.MkdirAll(*opath+fmt.Sprint(cd)+"/0/", 0777) 37 | 38 | filepath.Walk("./data", func(path string, info os.FileInfo, err error) error { 39 | if err != nil { 40 | return nil 41 | } 42 | if info.IsDir() { 43 | return nil 44 | } 45 | 46 | encodedfilename := filepath.Base(path) 47 | gopherurl, err := base32.StdEncoding.DecodeString(encodedfilename) 48 | if err != nil { 49 | return nil 50 | } 51 | 52 | var dbid int 53 | err = db.QueryRow("SELECT id FROM assets WHERE path=? LIMIT 1", string(gopherurl)).Scan(&dbid) 54 | 55 | if err != nil || dbid == 0 { 56 | log.Printf("%s", err.Error()) 57 | return nil 58 | } 59 | 60 | if bytesOnDisk > 1900000000 { 61 | bytesOnDisk = 0 62 | cd++ 63 | os.MkdirAll(fmt.Sprintf("%s%d/", *opath, cd), 0777) 64 | } 65 | bytesOnDisk += int(info.Size()) 66 | 67 | if filesmoved%1000 == 0 { 68 | os.MkdirAll(fmt.Sprintf("%s%d/%d/", *opath, cd, filesmoved/1000), 0777) 69 | } 70 | 71 | dstpath := fmt.Sprintf("%s%d/%d/%d.txt", *opath, cd, filesmoved/1000, dbid) 72 | err = copy(path, dstpath) 73 | if err != nil { 74 | log.Printf("failed to copy file over to %s - %s", dstpath, err.Error()) 75 | } 76 | 77 | filesmoved++ 78 | 79 | return nil 80 | }) 81 | } 82 | 83 | func copy(src, dst string) error { 84 | in, err := os.Open(src) 85 | if err != nil { 86 | return err 87 | } 88 | defer in.Close() 89 | out, err := os.Create(dst) 90 | if err != nil { 91 | return err 92 | } 93 | defer out.Close() 94 | _, err = io.Copy(out, in) 95 | cerr := out.Close() 96 | if err != nil { 97 | return err 98 | } 99 | return cerr 100 | } 101 | -------------------------------------------------------------------------------- /filesystem-gen/mkfs.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | set -e 3 | 4 | cp crawl.db snapshot.db 5 | 6 | # if ! df -h | grep -Fq loop0; then 7 | # echo "Need to make FS" 8 | # losetup -d /dev/loop0 || echo "Non fatal, continuing" 9 | # dd if=/dev/zero of=filesystem.img bs=1 count=0 seek=8G 10 | # sfdisk filesystem.img < filesystem-gen/layout.sfdisk 11 | # losetup /dev/loop0 filesystem.img 12 | # mkfs.fat /dev/loop0p1 13 | # mkdir fat32 || echo "Unable to make fat32 dir, probs because it exists" 14 | # mount /dev/loop0p1 ./fat32/ 15 | # fi 16 | 17 | mkdir fat32 || echo "Non fatal, continuing" 18 | 19 | cd fat32 && rm -r * || echo "wasnt able to remove things in the FS, probs because it was empty anyway" 20 | 21 | # # Zero out the drive so that it compresses better 22 | # for i in {1..8} 23 | # do 24 | # dd if=/dev/zero of=$i.stuff bs=1 count=0 seek=1G || echo "." 25 | # done 26 | # rm *.stuff 27 | # cd .. 28 | ./bin/filesystem-gen -dbpath snapshot.db 29 | #umount ./fat32 30 | #losetup -d /dev/loop0 31 | #qemu-img convert -O qcow2 filesystem.img image.qcow2 32 | rm mega.iso || echo "." 33 | xorrisofs -o mega.iso fat32/ -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/benjojo/gophervista 2 | 3 | go 1.16 4 | 5 | require github.com/mattn/go-sqlite3 v1.14.10 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/mattn/go-sqlite3 v1.14.10 h1:MLn+5bFRlWMGoSRmJour3CL1w/qL96mvipqpwQW/Sfk= 2 | github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= 3 | -------------------------------------------------------------------------------- /rfc1436/gopher.go: -------------------------------------------------------------------------------- 1 | package rfc1436 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net" 7 | "time" 8 | ) 9 | 10 | func Get(uri string) (out Document, info GopherConnectionInfo, err error) { 11 | bin, ci, err := GetBinary(uri) 12 | if err != nil { 13 | return out, GopherConnectionInfo{}, err 14 | } 15 | 16 | Doc, err := parseDocument(bin) 17 | if err != nil { 18 | return out, ci, err 19 | } 20 | 21 | return Doc, ci, err 22 | } 23 | 24 | func GetBinary(uri string) (out []byte, info GopherConnectionInfo, err error) { 25 | path, hostname, port, err := ParseURI(uri) 26 | if err != nil { 27 | return out, GopherConnectionInfo{}, err 28 | } 29 | 30 | return requestRaw(path, hostname, port) 31 | } 32 | 33 | func requestRaw(path, hostname string, port int) (out []byte, info GopherConnectionInfo, err error) { 34 | conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", hostname, port), time.Second*5) 35 | 36 | cinfo := GopherConnectionInfo{ 37 | Path: path, 38 | Hostname: hostname, 39 | Port: port, 40 | } 41 | 42 | if err != nil { 43 | return out, cinfo, err 44 | } 45 | conn.SetReadDeadline(time.Now().Add(time.Second * 10)) 46 | 47 | defer conn.Close() 48 | 49 | if path == "/" { 50 | path = "" 51 | } 52 | 53 | payload := fmt.Sprintf("%s\r\n", path) 54 | _, err = conn.Write([]byte(payload)) 55 | if err != nil { 56 | return out, cinfo, err 57 | } 58 | 59 | out, err = ioutil.ReadAll(conn) 60 | return out, cinfo, err 61 | } 62 | -------------------------------------------------------------------------------- /rfc1436/parse.go: -------------------------------------------------------------------------------- 1 | package rfc1436 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | ) 7 | 8 | // Gopher Message type codes 9 | // See: https://www.ietf.org/rfc/rfc1436.txt 10 | const ( 11 | TypeTextFile = "0" 12 | TypeMenuEntity = "1" 13 | TypeCSOProtocol = "2" 14 | TypeErr = "3" 15 | TypeMacintoshBinHex = "4" 16 | TypePCDOS = "5" 17 | Typeuuencoded = "6" 18 | TypeIndexServer = "7" 19 | TypeTelnetSession = "8" 20 | TypeBinaryFile = "9" 21 | TypeDuplicatedServer = "+" 22 | TypeGIF = "g" 23 | TypeImage = "I" 24 | TypeTN3270 = "T" 25 | TypeHTML = "h" 26 | TypeMessage = "i" 27 | TypeWAVSound = "s" 28 | ) 29 | 30 | // Document repersents a Gopher document that has been 31 | // parsed and ready to be processed for either 32 | // reading or rendering 33 | type Document struct { 34 | Raw []byte 35 | Items []Item 36 | } 37 | 38 | type Item struct { 39 | Type string 40 | Content string 41 | Host string 42 | Port int 43 | Path string 44 | } 45 | 46 | func parseDocument(in []byte) (D Document, err error) { 47 | D.Raw = in 48 | D.Items = make([]Item, 0) 49 | 50 | lines := strings.Split(string(in), "\r\n") 51 | for _, line := range lines { 52 | if line == "." { 53 | break 54 | } 55 | 56 | if !strings.Contains(line, "\t") { 57 | continue 58 | } 59 | 60 | if len(line) < 5 { 61 | // too short to be a gopher listing 62 | continue 63 | } 64 | 65 | if !isValidGopherType(string(line[:1])) { 66 | // is unparsable 67 | continue 68 | } 69 | 70 | e := Item{} 71 | e.Type = string(line[:1]) 72 | parts := strings.Split(line[1:], "\t") 73 | if len(parts) != 4 { 74 | // Unexpected amounts of tabs 75 | continue 76 | } 77 | 78 | e.Content = parts[0] 79 | if !strings.HasPrefix(parts[1], "/") { 80 | parts[1] = "/" + parts[1] 81 | } 82 | e.Path = parts[1] 83 | e.Host = parts[2] 84 | pn, err := strconv.ParseInt(parts[3], 10, 16) 85 | if err != nil { 86 | // invalid port number 87 | continue 88 | } 89 | e.Port = int(pn) 90 | 91 | D.Items = append(D.Items, e) 92 | } 93 | return D, nil 94 | } 95 | 96 | func isValidGopherType(in string) bool { 97 | if in == "0" || in == "1" || in == "2" || in == "3" || 98 | in == "4" || in == "5" || in == "6" || in == "7" || 99 | in == "8" || in == "9" || in == "+" || in == "g" || 100 | in == "I" || in == "T" || in == "h" || in == "i" || 101 | in == "s" { 102 | return true 103 | } 104 | return false 105 | } 106 | -------------------------------------------------------------------------------- /rfc1436/uri.go: -------------------------------------------------------------------------------- 1 | package rfc1436 2 | 3 | import ( 4 | "fmt" 5 | "net/url" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | type GopherConnectionInfo struct { 11 | Path string 12 | Hostname string 13 | Port int 14 | } 15 | 16 | func ParseURI(inputURI string) (path, hostname string, port int, err error) { 17 | if strings.HasPrefix(inputURI, "gopher://") { 18 | U, err := url.Parse(inputURI) 19 | if err != nil { 20 | return "", "", 0, err 21 | } 22 | 23 | // Now figure the port out 24 | if strings.Contains(U.Host, ":") { 25 | parts := strings.Split(U.Host, ":") 26 | if len(parts) != 2 { 27 | return "", "", 0, fmt.Errorf("the URI provided has more than one colon") 28 | } 29 | 30 | p, err := strconv.ParseUint(parts[1], 10, 16) 31 | if err != nil { 32 | return "", "", 0, fmt.Errorf("port on URI is invalid") 33 | } 34 | return U.Path, parts[0], int(p), nil 35 | } 36 | return U.Path, U.Host, 70, nil 37 | } 38 | return ParseURI("gopher://" + inputURI) 39 | } 40 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/.codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: off 4 | patch: off 5 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/.gitignore: -------------------------------------------------------------------------------- 1 | *.db 2 | *.exe 3 | *.dll 4 | *.o 5 | 6 | # VSCode 7 | .vscode 8 | 9 | # Exclude from upgrade 10 | upgrade/*.c 11 | upgrade/*.h 12 | 13 | # Exclude upgrade binary 14 | upgrade/upgrade 15 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Yasuhiro Matsumoto 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/README.md: -------------------------------------------------------------------------------- 1 | go-sqlite3 2 | ========== 3 | 4 | [![GoDoc Reference](https://godoc.org/github.com/mattn/go-sqlite3?status.svg)](http://godoc.org/github.com/mattn/go-sqlite3) 5 | [![GitHub Actions](https://github.com/mattn/go-sqlite3/workflows/Go/badge.svg)](https://github.com/mattn/go-sqlite3/actions?query=workflow%3AGo) 6 | [![Financial Contributors on Open Collective](https://opencollective.com/mattn-go-sqlite3/all/badge.svg?label=financial+contributors)](https://opencollective.com/mattn-go-sqlite3) 7 | [![codecov](https://codecov.io/gh/mattn/go-sqlite3/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-sqlite3) 8 | [![Go Report Card](https://goreportcard.com/badge/github.com/mattn/go-sqlite3)](https://goreportcard.com/report/github.com/mattn/go-sqlite3) 9 | 10 | Latest stable version is v1.14 or later, not v2. 11 | 12 | ~~**NOTE:** The increase to v2 was an accident. There were no major changes or features.~~ 13 | 14 | # Description 15 | 16 | A sqlite3 driver that conforms to the built-in database/sql interface. 17 | 18 | Supported Golang version: See [.github/workflows/go.yaml](./.github/workflows/go.yaml). 19 | 20 | This package follows the official [Golang Release Policy](https://golang.org/doc/devel/release.html#policy). 21 | 22 | ### Overview 23 | 24 | - [go-sqlite3](#go-sqlite3) 25 | - [Description](#description) 26 | - [Overview](#overview) 27 | - [Installation](#installation) 28 | - [API Reference](#api-reference) 29 | - [Connection String](#connection-string) 30 | - [DSN Examples](#dsn-examples) 31 | - [Features](#features) 32 | - [Usage](#usage) 33 | - [Feature / Extension List](#feature--extension-list) 34 | - [Compilation](#compilation) 35 | - [Android](#android) 36 | - [ARM](#arm) 37 | - [Cross Compile](#cross-compile) 38 | - [Google Cloud Platform](#google-cloud-platform) 39 | - [Linux](#linux) 40 | - [Alpine](#alpine) 41 | - [Fedora](#fedora) 42 | - [Ubuntu](#ubuntu) 43 | - [Mac OSX](#mac-osx) 44 | - [Windows](#windows) 45 | - [Errors](#errors) 46 | - [User Authentication](#user-authentication) 47 | - [Compile](#compile) 48 | - [Usage](#usage-1) 49 | - [Create protected database](#create-protected-database) 50 | - [Password Encoding](#password-encoding) 51 | - [Available Encoders](#available-encoders) 52 | - [Restrictions](#restrictions) 53 | - [Support](#support) 54 | - [User Management](#user-management) 55 | - [SQL](#sql) 56 | - [Examples](#examples) 57 | - [*SQLiteConn](#sqliteconn) 58 | - [Attached database](#attached-database) 59 | - [Extensions](#extensions) 60 | - [Spatialite](#spatialite) 61 | - [FAQ](#faq) 62 | - [License](#license) 63 | - [Author](#author) 64 | 65 | # Installation 66 | 67 | This package can be installed with the `go get` command: 68 | 69 | go get github.com/mattn/go-sqlite3 70 | 71 | _go-sqlite3_ is *cgo* package. 72 | If you want to build your app using go-sqlite3, you need gcc. 73 | However, after you have built and installed _go-sqlite3_ with `go install github.com/mattn/go-sqlite3` (which requires gcc), you can build your app without relying on gcc in future. 74 | 75 | ***Important: because this is a `CGO` enabled package, you are required to set the environment variable `CGO_ENABLED=1` and have a `gcc` compile present within your path.*** 76 | 77 | # API Reference 78 | 79 | API documentation can be found [here](http://godoc.org/github.com/mattn/go-sqlite3). 80 | 81 | Examples can be found under the [examples](./_example) directory. 82 | 83 | # Connection String 84 | 85 | When creating a new SQLite database or connection to an existing one, with the file name additional options can be given. 86 | This is also known as a DSN (Data Source Name) string. 87 | 88 | Options are append after the filename of the SQLite database. 89 | The database filename and options are separated by an `?` (Question Mark). 90 | Options should be URL-encoded (see [url.QueryEscape](https://golang.org/pkg/net/url/#QueryEscape)). 91 | 92 | This also applies when using an in-memory database instead of a file. 93 | 94 | Options can be given using the following format: `KEYWORD=VALUE` and multiple options can be combined with the `&` ampersand. 95 | 96 | This library supports DSN options of SQLite itself and provides additional options. 97 | 98 | Boolean values can be one of: 99 | * `0` `no` `false` `off` 100 | * `1` `yes` `true` `on` 101 | 102 | | Name | Key | Value(s) | Description | 103 | |------|-----|----------|-------------| 104 | | UA - Create | `_auth` | - | Create User Authentication, for more information see [User Authentication](#user-authentication) | 105 | | UA - Username | `_auth_user` | `string` | Username for User Authentication, for more information see [User Authentication](#user-authentication) | 106 | | UA - Password | `_auth_pass` | `string` | Password for User Authentication, for more information see [User Authentication](#user-authentication) | 107 | | UA - Crypt | `_auth_crypt` |
  • SHA1
  • SSHA1
  • SHA256
  • SSHA256
  • SHA384
  • SSHA384
  • SHA512
  • SSHA512
| Password encoder to use for User Authentication, for more information see [User Authentication](#user-authentication) | 108 | | UA - Salt | `_auth_salt` | `string` | Salt to use if the configure password encoder requires a salt, for User Authentication, for more information see [User Authentication](#user-authentication) | 109 | | Auto Vacuum | `_auto_vacuum` \| `_vacuum` |
  • `0` \| `none`
  • `1` \| `full`
  • `2` \| `incremental`
| For more information see [PRAGMA auto_vacuum](https://www.sqlite.org/pragma.html#pragma_auto_vacuum) | 110 | | Busy Timeout | `_busy_timeout` \| `_timeout` | `int` | Specify value for sqlite3_busy_timeout. For more information see [PRAGMA busy_timeout](https://www.sqlite.org/pragma.html#pragma_busy_timeout) | 111 | | Case Sensitive LIKE | `_case_sensitive_like` \| `_cslike` | `boolean` | For more information see [PRAGMA case_sensitive_like](https://www.sqlite.org/pragma.html#pragma_case_sensitive_like) | 112 | | Defer Foreign Keys | `_defer_foreign_keys` \| `_defer_fk` | `boolean` | For more information see [PRAGMA defer_foreign_keys](https://www.sqlite.org/pragma.html#pragma_defer_foreign_keys) | 113 | | Foreign Keys | `_foreign_keys` \| `_fk` | `boolean` | For more information see [PRAGMA foreign_keys](https://www.sqlite.org/pragma.html#pragma_foreign_keys) | 114 | | Ignore CHECK Constraints | `_ignore_check_constraints` | `boolean` | For more information see [PRAGMA ignore_check_constraints](https://www.sqlite.org/pragma.html#pragma_ignore_check_constraints) | 115 | | Immutable | `immutable` | `boolean` | For more information see [Immutable](https://www.sqlite.org/c3ref/open.html) | 116 | | Journal Mode | `_journal_mode` \| `_journal` |
  • DELETE
  • TRUNCATE
  • PERSIST
  • MEMORY
  • WAL
  • OFF
| For more information see [PRAGMA journal_mode](https://www.sqlite.org/pragma.html#pragma_journal_mode) | 117 | | Locking Mode | `_locking_mode` \| `_locking` |
  • NORMAL
  • EXCLUSIVE
| For more information see [PRAGMA locking_mode](https://www.sqlite.org/pragma.html#pragma_locking_mode) | 118 | | Mode | `mode` |
  • ro
  • rw
  • rwc
  • memory
| Access Mode of the database. For more information see [SQLite Open](https://www.sqlite.org/c3ref/open.html) | 119 | | Mutex Locking | `_mutex` |
  • no
  • full
| Specify mutex mode. | 120 | | Query Only | `_query_only` | `boolean` | For more information see [PRAGMA query_only](https://www.sqlite.org/pragma.html#pragma_query_only) | 121 | | Recursive Triggers | `_recursive_triggers` \| `_rt` | `boolean` | For more information see [PRAGMA recursive_triggers](https://www.sqlite.org/pragma.html#pragma_recursive_triggers) | 122 | | Secure Delete | `_secure_delete` | `boolean` \| `FAST` | For more information see [PRAGMA secure_delete](https://www.sqlite.org/pragma.html#pragma_secure_delete) | 123 | | Shared-Cache Mode | `cache` |
  • shared
  • private
| Set cache mode for more information see [sqlite.org](https://www.sqlite.org/sharedcache.html) | 124 | | Synchronous | `_synchronous` \| `_sync` |
  • 0 \| OFF
  • 1 \| NORMAL
  • 2 \| FULL
  • 3 \| EXTRA
| For more information see [PRAGMA synchronous](https://www.sqlite.org/pragma.html#pragma_synchronous) | 125 | | Time Zone Location | `_loc` | auto | Specify location of time format. | 126 | | Transaction Lock | `_txlock` |
  • immediate
  • deferred
  • exclusive
| Specify locking behavior for transactions. | 127 | | Writable Schema | `_writable_schema` | `Boolean` | When this pragma is on, the SQLITE_MASTER tables in which database can be changed using ordinary UPDATE, INSERT, and DELETE statements. Warning: misuse of this pragma can easily result in a corrupt database file. | 128 | | Cache Size | `_cache_size` | `int` | Maximum cache size; default is 2000K (2M). See [PRAGMA cache_size](https://sqlite.org/pragma.html#pragma_cache_size) | 129 | 130 | 131 | ## DSN Examples 132 | 133 | ``` 134 | file:test.db?cache=shared&mode=memory 135 | ``` 136 | 137 | # Features 138 | 139 | This package allows additional configuration of features available within SQLite3 to be enabled or disabled by golang build constraints also known as build `tags`. 140 | 141 | Click [here](https://golang.org/pkg/go/build/#hdr-Build_Constraints) for more information about build tags / constraints. 142 | 143 | ### Usage 144 | 145 | If you wish to build this library with additional extensions / features, use the following command: 146 | 147 | ```bash 148 | go build --tags "" 149 | ``` 150 | 151 | For available features, see the extension list. 152 | When using multiple build tags, all the different tags should be space delimited. 153 | 154 | Example: 155 | 156 | ```bash 157 | go build --tags "icu json1 fts5 secure_delete" 158 | ``` 159 | 160 | ### Feature / Extension List 161 | 162 | | Extension | Build Tag | Description | 163 | |-----------|-----------|-------------| 164 | | Additional Statistics | sqlite_stat4 | This option adds additional logic to the ANALYZE command and to the query planner that can help SQLite to chose a better query plan under certain situations. The ANALYZE command is enhanced to collect histogram data from all columns of every index and store that data in the sqlite_stat4 table.

The query planner will then use the histogram data to help it make better index choices. The downside of this compile-time option is that it violates the query planner stability guarantee making it more difficult to ensure consistent performance in mass-produced applications.

SQLITE_ENABLE_STAT4 is an enhancement of SQLITE_ENABLE_STAT3. STAT3 only recorded histogram data for the left-most column of each index whereas the STAT4 enhancement records histogram data from all columns of each index.

The SQLITE_ENABLE_STAT3 compile-time option is a no-op and is ignored if the SQLITE_ENABLE_STAT4 compile-time option is used | 165 | | Allow URI Authority | sqlite_allow_uri_authority | URI filenames normally throws an error if the authority section is not either empty or "localhost".

However, if SQLite is compiled with the SQLITE_ALLOW_URI_AUTHORITY compile-time option, then the URI is converted into a Uniform Naming Convention (UNC) filename and passed down to the underlying operating system that way | 166 | | App Armor | sqlite_app_armor | When defined, this C-preprocessor macro activates extra code that attempts to detect misuse of the SQLite API, such as passing in NULL pointers to required parameters or using objects after they have been destroyed.

App Armor is not available under `Windows`. | 167 | | Disable Load Extensions | sqlite_omit_load_extension | Loading of external extensions is enabled by default.

To disable extension loading add the build tag `sqlite_omit_load_extension`. | 168 | | Foreign Keys | sqlite_foreign_keys | This macro determines whether enforcement of foreign key constraints is enabled or disabled by default for new database connections.

Each database connection can always turn enforcement of foreign key constraints on and off and run-time using the foreign_keys pragma.

Enforcement of foreign key constraints is normally off by default, but if this compile-time parameter is set to 1, enforcement of foreign key constraints will be on by default | 169 | | Full Auto Vacuum | sqlite_vacuum_full | Set the default auto vacuum to full | 170 | | Incremental Auto Vacuum | sqlite_vacuum_incr | Set the default auto vacuum to incremental | 171 | | Full Text Search Engine | sqlite_fts5 | When this option is defined in the amalgamation, versions 5 of the full-text search engine (fts5) is added to the build automatically | 172 | | International Components for Unicode | sqlite_icu | This option causes the International Components for Unicode or "ICU" extension to SQLite to be added to the build | 173 | | Introspect PRAGMAS | sqlite_introspect | This option adds some extra PRAGMA statements.
  • PRAGMA function_list
  • PRAGMA module_list
  • PRAGMA pragma_list
| 174 | | JSON SQL Functions | sqlite_json | When this option is defined in the amalgamation, the JSON SQL functions are added to the build automatically | 175 | | Pre Update Hook | sqlite_preupdate_hook | Registers a callback function that is invoked prior to each INSERT, UPDATE, and DELETE operation on a database table. | 176 | | Secure Delete | sqlite_secure_delete | This compile-time option changes the default setting of the secure_delete pragma.

When this option is not used, secure_delete defaults to off. When this option is present, secure_delete defaults to on.

The secure_delete setting causes deleted content to be overwritten with zeros. There is a small performance penalty since additional I/O must occur.

On the other hand, secure_delete can prevent fragments of sensitive information from lingering in unused parts of the database file after it has been deleted. See the documentation on the secure_delete pragma for additional information | 177 | | Secure Delete (FAST) | sqlite_secure_delete_fast | For more information see [PRAGMA secure_delete](https://www.sqlite.org/pragma.html#pragma_secure_delete) | 178 | | Tracing / Debug | sqlite_trace | Activate trace functions | 179 | | User Authentication | sqlite_userauth | SQLite User Authentication see [User Authentication](#user-authentication) for more information. | 180 | 181 | # Compilation 182 | 183 | This package requires the `CGO_ENABLED=1` ennvironment variable if not set by default, and the presence of the `gcc` compiler. 184 | 185 | If you need to add additional CFLAGS or LDFLAGS to the build command, and do not want to modify this package, then this can be achieved by using the `CGO_CFLAGS` and `CGO_LDFLAGS` environment variables. 186 | 187 | ## Android 188 | 189 | This package can be compiled for android. 190 | Compile with: 191 | 192 | ```bash 193 | go build --tags "android" 194 | ``` 195 | 196 | For more information see [#201](https://github.com/mattn/go-sqlite3/issues/201) 197 | 198 | # ARM 199 | 200 | To compile for `ARM` use the following environment: 201 | 202 | ```bash 203 | env CC=arm-linux-gnueabihf-gcc CXX=arm-linux-gnueabihf-g++ \ 204 | CGO_ENABLED=1 GOOS=linux GOARCH=arm GOARM=7 \ 205 | go build -v 206 | ``` 207 | 208 | Additional information: 209 | - [#242](https://github.com/mattn/go-sqlite3/issues/242) 210 | - [#504](https://github.com/mattn/go-sqlite3/issues/504) 211 | 212 | # Cross Compile 213 | 214 | This library can be cross-compiled. 215 | 216 | In some cases you are required to the `CC` environment variable with the cross compiler. 217 | 218 | ## Cross Compiling from MAC OSX 219 | The simplest way to cross compile from OSX is to use [xgo](https://github.com/karalabe/xgo). 220 | 221 | Steps: 222 | - Install [xgo](https://github.com/karalabe/xgo) (`go get github.com/karalabe/xgo`). 223 | - Ensure that your project is within your `GOPATH`. 224 | - Run `xgo local/path/to/project`. 225 | 226 | Please refer to the project's [README](https://github.com/karalabe/xgo/blob/master/README.md) for further information. 227 | 228 | # Google Cloud Platform 229 | 230 | Building on GCP is not possible because Google Cloud Platform does not allow `gcc` to be executed. 231 | 232 | Please work only with compiled final binaries. 233 | 234 | ## Linux 235 | 236 | To compile this package on Linux, you must install the development tools for your linux distribution. 237 | 238 | To compile under linux use the build tag `linux`. 239 | 240 | ```bash 241 | go build --tags "linux" 242 | ``` 243 | 244 | If you wish to link directly to libsqlite3 then you can use the `libsqlite3` build tag. 245 | 246 | ``` 247 | go build --tags "libsqlite3 linux" 248 | ``` 249 | 250 | ### Alpine 251 | 252 | When building in an `alpine` container run the following command before building: 253 | 254 | ``` 255 | apk add --update gcc musl-dev 256 | ``` 257 | 258 | ### Fedora 259 | 260 | ```bash 261 | sudo yum groupinstall "Development Tools" "Development Libraries" 262 | ``` 263 | 264 | ### Ubuntu 265 | 266 | ```bash 267 | sudo apt-get install build-essential 268 | ``` 269 | 270 | ## Mac OSX 271 | 272 | OSX should have all the tools present to compile this package. If not, install XCode to add all the developers tools. 273 | 274 | Required dependency: 275 | 276 | ```bash 277 | brew install sqlite3 278 | ``` 279 | 280 | For OSX, there is an additional package to install which is required if you wish to build the `icu` extension. 281 | 282 | This additional package can be installed with `homebrew`: 283 | 284 | ```bash 285 | brew upgrade icu4c 286 | ``` 287 | 288 | To compile for Mac OSX: 289 | 290 | ```bash 291 | go build --tags "darwin" 292 | ``` 293 | 294 | If you wish to link directly to libsqlite3, use the `libsqlite3` build tag: 295 | 296 | ``` 297 | go build --tags "libsqlite3 darwin" 298 | ``` 299 | 300 | Additional information: 301 | - [#206](https://github.com/mattn/go-sqlite3/issues/206) 302 | - [#404](https://github.com/mattn/go-sqlite3/issues/404) 303 | 304 | ## Windows 305 | 306 | To compile this package on Windows, you must have the `gcc` compiler installed. 307 | 308 | 1) Install a Windows `gcc` toolchain. 309 | 2) Add the `bin` folder to the Windows path, if the installer did not do this by default. 310 | 3) Open a terminal for the TDM-GCC toolchain, which can be found in the Windows Start menu. 311 | 4) Navigate to your project folder and run the `go build ...` command for this package. 312 | 313 | For example the TDM-GCC Toolchain can be found [here](https://jmeubank.github.io/tdm-gcc/). 314 | 315 | ## Errors 316 | 317 | - Compile error: `can not be used when making a shared object; recompile with -fPIC` 318 | 319 | When receiving a compile time error referencing recompile with `-FPIC` then you 320 | are probably using a hardend system. 321 | 322 | You can compile the library on a hardend system with the following command. 323 | 324 | ```bash 325 | go build -ldflags '-extldflags=-fno-PIC' 326 | ``` 327 | 328 | More details see [#120](https://github.com/mattn/go-sqlite3/issues/120) 329 | 330 | - Can't build go-sqlite3 on windows 64bit. 331 | 332 | > Probably, you are using go 1.0, go1.0 has a problem when it comes to compiling/linking on windows 64bit. 333 | > See: [#27](https://github.com/mattn/go-sqlite3/issues/27) 334 | 335 | - `go get github.com/mattn/go-sqlite3` throws compilation error. 336 | 337 | `gcc` throws: `internal compiler error` 338 | 339 | Remove the download repository from your disk and try re-install with: 340 | 341 | ```bash 342 | go install github.com/mattn/go-sqlite3 343 | ``` 344 | 345 | # User Authentication 346 | 347 | This package supports the SQLite User Authentication module. 348 | 349 | ## Compile 350 | 351 | To use the User authentication module, the package has to be compiled with the tag `sqlite_userauth`. See [Features](#features). 352 | 353 | ## Usage 354 | 355 | ### Create protected database 356 | 357 | To create a database protected by user authentication, provide the following argument to the connection string `_auth`. 358 | This will enable user authentication within the database. This option however requires two additional arguments: 359 | 360 | - `_auth_user` 361 | - `_auth_pass` 362 | 363 | When `_auth` is present in the connection string user authentication will be enabled and the provided user will be created 364 | as an `admin` user. After initial creation, the parameter `_auth` has no effect anymore and can be omitted from the connection string. 365 | 366 | Example connection strings: 367 | 368 | Create an user authentication database with user `admin` and password `admin`: 369 | 370 | `file:test.s3db?_auth&_auth_user=admin&_auth_pass=admin` 371 | 372 | Create an user authentication database with user `admin` and password `admin` and use `SHA1` for the password encoding: 373 | 374 | `file:test.s3db?_auth&_auth_user=admin&_auth_pass=admin&_auth_crypt=sha1` 375 | 376 | ### Password Encoding 377 | 378 | The passwords within the user authentication module of SQLite are encoded with the SQLite function `sqlite_cryp`. 379 | This function uses a ceasar-cypher which is quite insecure. 380 | This library provides several additional password encoders which can be configured through the connection string. 381 | 382 | The password cypher can be configured with the key `_auth_crypt`. And if the configured password encoder also requires an 383 | salt this can be configured with `_auth_salt`. 384 | 385 | #### Available Encoders 386 | 387 | - SHA1 388 | - SSHA1 (Salted SHA1) 389 | - SHA256 390 | - SSHA256 (salted SHA256) 391 | - SHA384 392 | - SSHA384 (salted SHA384) 393 | - SHA512 394 | - SSHA512 (salted SHA512) 395 | 396 | ### Restrictions 397 | 398 | Operations on the database regarding user management can only be preformed by an administrator user. 399 | 400 | ### Support 401 | 402 | The user authentication supports two kinds of users: 403 | 404 | - administrators 405 | - regular users 406 | 407 | ### User Management 408 | 409 | User management can be done by directly using the `*SQLiteConn` or by SQL. 410 | 411 | #### SQL 412 | 413 | The following sql functions are available for user management: 414 | 415 | | Function | Arguments | Description | 416 | |----------|-----------|-------------| 417 | | `authenticate` | username `string`, password `string` | Will authenticate an user, this is done by the connection; and should not be used manually. | 418 | | `auth_user_add` | username `string`, password `string`, admin `int` | This function will add an user to the database.
if the database is not protected by user authentication it will enable it. Argument `admin` is an integer identifying if the added user should be an administrator. Only Administrators can add administrators. | 419 | | `auth_user_change` | username `string`, password `string`, admin `int` | Function to modify an user. Users can change their own password, but only an administrator can change the administrator flag. | 420 | | `authUserDelete` | username `string` | Delete an user from the database. Can only be used by an administrator. The current logged in administrator cannot be deleted. This is to make sure their is always an administrator remaining. | 421 | 422 | These functions will return an integer: 423 | 424 | - 0 (SQLITE_OK) 425 | - 23 (SQLITE_AUTH) Failed to perform due to authentication or insufficient privileges 426 | 427 | ##### Examples 428 | 429 | ```sql 430 | // Autheticate user 431 | // Create Admin User 432 | SELECT auth_user_add('admin2', 'admin2', 1); 433 | 434 | // Change password for user 435 | SELECT auth_user_change('user', 'userpassword', 0); 436 | 437 | // Delete user 438 | SELECT user_delete('user'); 439 | ``` 440 | 441 | #### *SQLiteConn 442 | 443 | The following functions are available for User authentication from the `*SQLiteConn`: 444 | 445 | | Function | Description | 446 | |----------|-------------| 447 | | `Authenticate(username, password string) error` | Authenticate user | 448 | | `AuthUserAdd(username, password string, admin bool) error` | Add user | 449 | | `AuthUserChange(username, password string, admin bool) error` | Modify user | 450 | | `AuthUserDelete(username string) error` | Delete user | 451 | 452 | ### Attached database 453 | 454 | When using attached databases, SQLite will use the authentication from the `main` database for the attached database(s). 455 | 456 | # Extensions 457 | 458 | If you want your own extension to be listed here, or you want to add a reference to an extension; please submit an Issue for this. 459 | 460 | ## Spatialite 461 | 462 | Spatialite is available as an extension to SQLite, and can be used in combination with this repository. 463 | For an example, see [shaxbee/go-spatialite](https://github.com/shaxbee/go-spatialite). 464 | 465 | ## extension-functions.c from SQLite3 Contrib 466 | 467 | extension-functions.c is available as an extension to SQLite, and provides the following functions: 468 | 469 | - Math: acos, asin, atan, atn2, atan2, acosh, asinh, atanh, difference, degrees, radians, cos, sin, tan, cot, cosh, sinh, tanh, coth, exp, log, log10, power, sign, sqrt, square, ceil, floor, pi. 470 | - String: replicate, charindex, leftstr, rightstr, ltrim, rtrim, trim, replace, reverse, proper, padl, padr, padc, strfilter. 471 | - Aggregate: stdev, variance, mode, median, lower_quartile, upper_quartile 472 | 473 | For an example, see [dinedal/go-sqlite3-extension-functions](https://github.com/dinedal/go-sqlite3-extension-functions). 474 | 475 | # FAQ 476 | 477 | - Getting insert error while query is opened. 478 | 479 | > You can pass some arguments into the connection string, for example, a URI. 480 | > See: [#39](https://github.com/mattn/go-sqlite3/issues/39) 481 | 482 | - Do you want to cross compile? mingw on Linux or Mac? 483 | 484 | > See: [#106](https://github.com/mattn/go-sqlite3/issues/106) 485 | > See also: http://www.limitlessfx.com/cross-compile-golang-app-for-windows-from-linux.html 486 | 487 | - Want to get time.Time with current locale 488 | 489 | Use `_loc=auto` in SQLite3 filename schema like `file:foo.db?_loc=auto`. 490 | 491 | - Can I use this in multiple routines concurrently? 492 | 493 | Yes for readonly. But not for writable. See [#50](https://github.com/mattn/go-sqlite3/issues/50), [#51](https://github.com/mattn/go-sqlite3/issues/51), [#209](https://github.com/mattn/go-sqlite3/issues/209), [#274](https://github.com/mattn/go-sqlite3/issues/274). 494 | 495 | - Why I'm getting `no such table` error? 496 | 497 | Why is it racy if I use a `sql.Open("sqlite3", ":memory:")` database? 498 | 499 | Each connection to `":memory:"` opens a brand new in-memory sql database, so if 500 | the stdlib's sql engine happens to open another connection and you've only 501 | specified `":memory:"`, that connection will see a brand new database. A 502 | workaround is to use `"file::memory:?cache=shared"` (or `"file:foobar?mode=memory&cache=shared"`). Every 503 | connection to this string will point to the same in-memory database. 504 | 505 | Note that if the last database connection in the pool closes, the in-memory database is deleted. Make sure the [max idle connection limit](https://golang.org/pkg/database/sql/#DB.SetMaxIdleConns) is > 0, and the [connection lifetime](https://golang.org/pkg/database/sql/#DB.SetConnMaxLifetime) is infinite. 506 | 507 | For more information see: 508 | * [#204](https://github.com/mattn/go-sqlite3/issues/204) 509 | * [#511](https://github.com/mattn/go-sqlite3/issues/511) 510 | * https://www.sqlite.org/sharedcache.html#shared_cache_and_in_memory_databases 511 | * https://www.sqlite.org/inmemorydb.html#sharedmemdb 512 | 513 | - Reading from database with large amount of goroutines fails on OSX. 514 | 515 | OS X limits OS-wide to not have more than 1000 files open simultaneously by default. 516 | 517 | For more information, see [#289](https://github.com/mattn/go-sqlite3/issues/289) 518 | 519 | - Trying to execute a `.` (dot) command throws an error. 520 | 521 | Error: `Error: near ".": syntax error` 522 | Dot command are part of SQLite3 CLI, not of this library. 523 | 524 | You need to implement the feature or call the sqlite3 cli. 525 | 526 | More information see [#305](https://github.com/mattn/go-sqlite3/issues/305). 527 | 528 | - Error: `database is locked` 529 | 530 | When you get a database is locked, please use the following options. 531 | 532 | Add to DSN: `cache=shared` 533 | 534 | Example: 535 | ```go 536 | db, err := sql.Open("sqlite3", "file:locked.sqlite?cache=shared") 537 | ``` 538 | 539 | Next, please set the database connections of the SQL package to 1: 540 | 541 | ```go 542 | db.SetMaxOpenConns(1) 543 | ``` 544 | 545 | For more information, see [#209](https://github.com/mattn/go-sqlite3/issues/209). 546 | 547 | ## Contributors 548 | 549 | ### Code Contributors 550 | 551 | This project exists thanks to all the people who [[contribute](CONTRIBUTING.md)]. 552 | 553 | 554 | ### Financial Contributors 555 | 556 | Become a financial contributor and help us sustain our community. [[Contribute here](https://opencollective.com/mattn-go-sqlite3/contribute)]. 557 | 558 | #### Individuals 559 | 560 | 561 | 562 | #### Organizations 563 | 564 | Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/mattn-go-sqlite3/contribute)] 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | # License 578 | 579 | MIT: http://mattn.mit-license.org/2018 580 | 581 | sqlite3-binding.c, sqlite3-binding.h, sqlite3ext.h 582 | 583 | The -binding suffix was added to avoid build failures under gccgo. 584 | 585 | In this repository, those files are an amalgamation of code that was copied from SQLite3. The license of that code is the same as the license of SQLite3. 586 | 587 | # Author 588 | 589 | Yasuhiro Matsumoto (a.k.a mattn) 590 | 591 | G.J.R. Timmer 592 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/backup.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | package sqlite3 7 | 8 | /* 9 | #ifndef USE_LIBSQLITE3 10 | #include "sqlite3-binding.h" 11 | #else 12 | #include 13 | #endif 14 | #include 15 | */ 16 | import "C" 17 | import ( 18 | "runtime" 19 | "unsafe" 20 | ) 21 | 22 | // SQLiteBackup implement interface of Backup. 23 | type SQLiteBackup struct { 24 | b *C.sqlite3_backup 25 | } 26 | 27 | // Backup make backup from src to dest. 28 | func (destConn *SQLiteConn) Backup(dest string, srcConn *SQLiteConn, src string) (*SQLiteBackup, error) { 29 | destptr := C.CString(dest) 30 | defer C.free(unsafe.Pointer(destptr)) 31 | srcptr := C.CString(src) 32 | defer C.free(unsafe.Pointer(srcptr)) 33 | 34 | if b := C.sqlite3_backup_init(destConn.db, destptr, srcConn.db, srcptr); b != nil { 35 | bb := &SQLiteBackup{b: b} 36 | runtime.SetFinalizer(bb, (*SQLiteBackup).Finish) 37 | return bb, nil 38 | } 39 | return nil, destConn.lastError() 40 | } 41 | 42 | // Step to backs up for one step. Calls the underlying `sqlite3_backup_step` 43 | // function. This function returns a boolean indicating if the backup is done 44 | // and an error signalling any other error. Done is returned if the underlying 45 | // C function returns SQLITE_DONE (Code 101) 46 | func (b *SQLiteBackup) Step(p int) (bool, error) { 47 | ret := C.sqlite3_backup_step(b.b, C.int(p)) 48 | if ret == C.SQLITE_DONE { 49 | return true, nil 50 | } else if ret != 0 && ret != C.SQLITE_LOCKED && ret != C.SQLITE_BUSY { 51 | return false, Error{Code: ErrNo(ret)} 52 | } 53 | return false, nil 54 | } 55 | 56 | // Remaining return whether have the rest for backup. 57 | func (b *SQLiteBackup) Remaining() int { 58 | return int(C.sqlite3_backup_remaining(b.b)) 59 | } 60 | 61 | // PageCount return count of pages. 62 | func (b *SQLiteBackup) PageCount() int { 63 | return int(C.sqlite3_backup_pagecount(b.b)) 64 | } 65 | 66 | // Finish close backup. 67 | func (b *SQLiteBackup) Finish() error { 68 | return b.Close() 69 | } 70 | 71 | // Close close backup. 72 | func (b *SQLiteBackup) Close() error { 73 | ret := C.sqlite3_backup_finish(b.b) 74 | 75 | // sqlite3_backup_finish() never fails, it just returns the 76 | // error code from previous operations, so clean up before 77 | // checking and returning an error 78 | b.b = nil 79 | runtime.SetFinalizer(b, nil) 80 | 81 | if ret != 0 { 82 | return Error{Code: ErrNo(ret)} 83 | } 84 | return nil 85 | } 86 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/callback.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | package sqlite3 7 | 8 | // You can't export a Go function to C and have definitions in the C 9 | // preamble in the same file, so we have to have callbackTrampoline in 10 | // its own file. Because we need a separate file anyway, the support 11 | // code for SQLite custom functions is in here. 12 | 13 | /* 14 | #ifndef USE_LIBSQLITE3 15 | #include "sqlite3-binding.h" 16 | #else 17 | #include 18 | #endif 19 | #include 20 | 21 | void _sqlite3_result_text(sqlite3_context* ctx, const char* s); 22 | void _sqlite3_result_blob(sqlite3_context* ctx, const void* b, int l); 23 | */ 24 | import "C" 25 | 26 | import ( 27 | "errors" 28 | "fmt" 29 | "math" 30 | "reflect" 31 | "sync" 32 | "unsafe" 33 | ) 34 | 35 | //export callbackTrampoline 36 | func callbackTrampoline(ctx *C.sqlite3_context, argc int, argv **C.sqlite3_value) { 37 | args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc] 38 | fi := lookupHandle(C.sqlite3_user_data(ctx)).(*functionInfo) 39 | fi.Call(ctx, args) 40 | } 41 | 42 | //export stepTrampoline 43 | func stepTrampoline(ctx *C.sqlite3_context, argc C.int, argv **C.sqlite3_value) { 44 | args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:int(argc):int(argc)] 45 | ai := lookupHandle(C.sqlite3_user_data(ctx)).(*aggInfo) 46 | ai.Step(ctx, args) 47 | } 48 | 49 | //export doneTrampoline 50 | func doneTrampoline(ctx *C.sqlite3_context) { 51 | ai := lookupHandle(C.sqlite3_user_data(ctx)).(*aggInfo) 52 | ai.Done(ctx) 53 | } 54 | 55 | //export compareTrampoline 56 | func compareTrampoline(handlePtr unsafe.Pointer, la C.int, a *C.char, lb C.int, b *C.char) C.int { 57 | cmp := lookupHandle(handlePtr).(func(string, string) int) 58 | return C.int(cmp(C.GoStringN(a, la), C.GoStringN(b, lb))) 59 | } 60 | 61 | //export commitHookTrampoline 62 | func commitHookTrampoline(handle unsafe.Pointer) int { 63 | callback := lookupHandle(handle).(func() int) 64 | return callback() 65 | } 66 | 67 | //export rollbackHookTrampoline 68 | func rollbackHookTrampoline(handle unsafe.Pointer) { 69 | callback := lookupHandle(handle).(func()) 70 | callback() 71 | } 72 | 73 | //export updateHookTrampoline 74 | func updateHookTrampoline(handle unsafe.Pointer, op int, db *C.char, table *C.char, rowid int64) { 75 | callback := lookupHandle(handle).(func(int, string, string, int64)) 76 | callback(op, C.GoString(db), C.GoString(table), rowid) 77 | } 78 | 79 | //export authorizerTrampoline 80 | func authorizerTrampoline(handle unsafe.Pointer, op int, arg1 *C.char, arg2 *C.char, arg3 *C.char) int { 81 | callback := lookupHandle(handle).(func(int, string, string, string) int) 82 | return callback(op, C.GoString(arg1), C.GoString(arg2), C.GoString(arg3)) 83 | } 84 | 85 | //export preUpdateHookTrampoline 86 | func preUpdateHookTrampoline(handle unsafe.Pointer, dbHandle uintptr, op int, db *C.char, table *C.char, oldrowid int64, newrowid int64) { 87 | hval := lookupHandleVal(handle) 88 | data := SQLitePreUpdateData{ 89 | Conn: hval.db, 90 | Op: op, 91 | DatabaseName: C.GoString(db), 92 | TableName: C.GoString(table), 93 | OldRowID: oldrowid, 94 | NewRowID: newrowid, 95 | } 96 | callback := hval.val.(func(SQLitePreUpdateData)) 97 | callback(data) 98 | } 99 | 100 | // Use handles to avoid passing Go pointers to C. 101 | type handleVal struct { 102 | db *SQLiteConn 103 | val interface{} 104 | } 105 | 106 | var handleLock sync.Mutex 107 | var handleVals = make(map[unsafe.Pointer]handleVal) 108 | 109 | func newHandle(db *SQLiteConn, v interface{}) unsafe.Pointer { 110 | handleLock.Lock() 111 | defer handleLock.Unlock() 112 | val := handleVal{db: db, val: v} 113 | var p unsafe.Pointer = C.malloc(C.size_t(1)) 114 | if p == nil { 115 | panic("can't allocate 'cgo-pointer hack index pointer': ptr == nil") 116 | } 117 | handleVals[p] = val 118 | return p 119 | } 120 | 121 | func lookupHandleVal(handle unsafe.Pointer) handleVal { 122 | handleLock.Lock() 123 | defer handleLock.Unlock() 124 | return handleVals[handle] 125 | } 126 | 127 | func lookupHandle(handle unsafe.Pointer) interface{} { 128 | return lookupHandleVal(handle).val 129 | } 130 | 131 | func deleteHandles(db *SQLiteConn) { 132 | handleLock.Lock() 133 | defer handleLock.Unlock() 134 | for handle, val := range handleVals { 135 | if val.db == db { 136 | delete(handleVals, handle) 137 | C.free(handle) 138 | } 139 | } 140 | } 141 | 142 | // This is only here so that tests can refer to it. 143 | type callbackArgRaw C.sqlite3_value 144 | 145 | type callbackArgConverter func(*C.sqlite3_value) (reflect.Value, error) 146 | 147 | type callbackArgCast struct { 148 | f callbackArgConverter 149 | typ reflect.Type 150 | } 151 | 152 | func (c callbackArgCast) Run(v *C.sqlite3_value) (reflect.Value, error) { 153 | val, err := c.f(v) 154 | if err != nil { 155 | return reflect.Value{}, err 156 | } 157 | if !val.Type().ConvertibleTo(c.typ) { 158 | return reflect.Value{}, fmt.Errorf("cannot convert %s to %s", val.Type(), c.typ) 159 | } 160 | return val.Convert(c.typ), nil 161 | } 162 | 163 | func callbackArgInt64(v *C.sqlite3_value) (reflect.Value, error) { 164 | if C.sqlite3_value_type(v) != C.SQLITE_INTEGER { 165 | return reflect.Value{}, fmt.Errorf("argument must be an INTEGER") 166 | } 167 | return reflect.ValueOf(int64(C.sqlite3_value_int64(v))), nil 168 | } 169 | 170 | func callbackArgBool(v *C.sqlite3_value) (reflect.Value, error) { 171 | if C.sqlite3_value_type(v) != C.SQLITE_INTEGER { 172 | return reflect.Value{}, fmt.Errorf("argument must be an INTEGER") 173 | } 174 | i := int64(C.sqlite3_value_int64(v)) 175 | val := false 176 | if i != 0 { 177 | val = true 178 | } 179 | return reflect.ValueOf(val), nil 180 | } 181 | 182 | func callbackArgFloat64(v *C.sqlite3_value) (reflect.Value, error) { 183 | if C.sqlite3_value_type(v) != C.SQLITE_FLOAT { 184 | return reflect.Value{}, fmt.Errorf("argument must be a FLOAT") 185 | } 186 | return reflect.ValueOf(float64(C.sqlite3_value_double(v))), nil 187 | } 188 | 189 | func callbackArgBytes(v *C.sqlite3_value) (reflect.Value, error) { 190 | switch C.sqlite3_value_type(v) { 191 | case C.SQLITE_BLOB: 192 | l := C.sqlite3_value_bytes(v) 193 | p := C.sqlite3_value_blob(v) 194 | return reflect.ValueOf(C.GoBytes(p, l)), nil 195 | case C.SQLITE_TEXT: 196 | l := C.sqlite3_value_bytes(v) 197 | c := unsafe.Pointer(C.sqlite3_value_text(v)) 198 | return reflect.ValueOf(C.GoBytes(c, l)), nil 199 | default: 200 | return reflect.Value{}, fmt.Errorf("argument must be BLOB or TEXT") 201 | } 202 | } 203 | 204 | func callbackArgString(v *C.sqlite3_value) (reflect.Value, error) { 205 | switch C.sqlite3_value_type(v) { 206 | case C.SQLITE_BLOB: 207 | l := C.sqlite3_value_bytes(v) 208 | p := (*C.char)(C.sqlite3_value_blob(v)) 209 | return reflect.ValueOf(C.GoStringN(p, l)), nil 210 | case C.SQLITE_TEXT: 211 | c := (*C.char)(unsafe.Pointer(C.sqlite3_value_text(v))) 212 | return reflect.ValueOf(C.GoString(c)), nil 213 | default: 214 | return reflect.Value{}, fmt.Errorf("argument must be BLOB or TEXT") 215 | } 216 | } 217 | 218 | func callbackArgGeneric(v *C.sqlite3_value) (reflect.Value, error) { 219 | switch C.sqlite3_value_type(v) { 220 | case C.SQLITE_INTEGER: 221 | return callbackArgInt64(v) 222 | case C.SQLITE_FLOAT: 223 | return callbackArgFloat64(v) 224 | case C.SQLITE_TEXT: 225 | return callbackArgString(v) 226 | case C.SQLITE_BLOB: 227 | return callbackArgBytes(v) 228 | case C.SQLITE_NULL: 229 | // Interpret NULL as a nil byte slice. 230 | var ret []byte 231 | return reflect.ValueOf(ret), nil 232 | default: 233 | panic("unreachable") 234 | } 235 | } 236 | 237 | func callbackArg(typ reflect.Type) (callbackArgConverter, error) { 238 | switch typ.Kind() { 239 | case reflect.Interface: 240 | if typ.NumMethod() != 0 { 241 | return nil, errors.New("the only supported interface type is interface{}") 242 | } 243 | return callbackArgGeneric, nil 244 | case reflect.Slice: 245 | if typ.Elem().Kind() != reflect.Uint8 { 246 | return nil, errors.New("the only supported slice type is []byte") 247 | } 248 | return callbackArgBytes, nil 249 | case reflect.String: 250 | return callbackArgString, nil 251 | case reflect.Bool: 252 | return callbackArgBool, nil 253 | case reflect.Int64: 254 | return callbackArgInt64, nil 255 | case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint: 256 | c := callbackArgCast{callbackArgInt64, typ} 257 | return c.Run, nil 258 | case reflect.Float64: 259 | return callbackArgFloat64, nil 260 | case reflect.Float32: 261 | c := callbackArgCast{callbackArgFloat64, typ} 262 | return c.Run, nil 263 | default: 264 | return nil, fmt.Errorf("don't know how to convert to %s", typ) 265 | } 266 | } 267 | 268 | func callbackConvertArgs(argv []*C.sqlite3_value, converters []callbackArgConverter, variadic callbackArgConverter) ([]reflect.Value, error) { 269 | var args []reflect.Value 270 | 271 | if len(argv) < len(converters) { 272 | return nil, fmt.Errorf("function requires at least %d arguments", len(converters)) 273 | } 274 | 275 | for i, arg := range argv[:len(converters)] { 276 | v, err := converters[i](arg) 277 | if err != nil { 278 | return nil, err 279 | } 280 | args = append(args, v) 281 | } 282 | 283 | if variadic != nil { 284 | for _, arg := range argv[len(converters):] { 285 | v, err := variadic(arg) 286 | if err != nil { 287 | return nil, err 288 | } 289 | args = append(args, v) 290 | } 291 | } 292 | return args, nil 293 | } 294 | 295 | type callbackRetConverter func(*C.sqlite3_context, reflect.Value) error 296 | 297 | func callbackRetInteger(ctx *C.sqlite3_context, v reflect.Value) error { 298 | switch v.Type().Kind() { 299 | case reflect.Int64: 300 | case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint: 301 | v = v.Convert(reflect.TypeOf(int64(0))) 302 | case reflect.Bool: 303 | b := v.Interface().(bool) 304 | if b { 305 | v = reflect.ValueOf(int64(1)) 306 | } else { 307 | v = reflect.ValueOf(int64(0)) 308 | } 309 | default: 310 | return fmt.Errorf("cannot convert %s to INTEGER", v.Type()) 311 | } 312 | 313 | C.sqlite3_result_int64(ctx, C.sqlite3_int64(v.Interface().(int64))) 314 | return nil 315 | } 316 | 317 | func callbackRetFloat(ctx *C.sqlite3_context, v reflect.Value) error { 318 | switch v.Type().Kind() { 319 | case reflect.Float64: 320 | case reflect.Float32: 321 | v = v.Convert(reflect.TypeOf(float64(0))) 322 | default: 323 | return fmt.Errorf("cannot convert %s to FLOAT", v.Type()) 324 | } 325 | 326 | C.sqlite3_result_double(ctx, C.double(v.Interface().(float64))) 327 | return nil 328 | } 329 | 330 | func callbackRetBlob(ctx *C.sqlite3_context, v reflect.Value) error { 331 | if v.Type().Kind() != reflect.Slice || v.Type().Elem().Kind() != reflect.Uint8 { 332 | return fmt.Errorf("cannot convert %s to BLOB", v.Type()) 333 | } 334 | i := v.Interface() 335 | if i == nil || len(i.([]byte)) == 0 { 336 | C.sqlite3_result_null(ctx) 337 | } else { 338 | bs := i.([]byte) 339 | C._sqlite3_result_blob(ctx, unsafe.Pointer(&bs[0]), C.int(len(bs))) 340 | } 341 | return nil 342 | } 343 | 344 | func callbackRetText(ctx *C.sqlite3_context, v reflect.Value) error { 345 | if v.Type().Kind() != reflect.String { 346 | return fmt.Errorf("cannot convert %s to TEXT", v.Type()) 347 | } 348 | C._sqlite3_result_text(ctx, C.CString(v.Interface().(string))) 349 | return nil 350 | } 351 | 352 | func callbackRetNil(ctx *C.sqlite3_context, v reflect.Value) error { 353 | return nil 354 | } 355 | 356 | func callbackRet(typ reflect.Type) (callbackRetConverter, error) { 357 | switch typ.Kind() { 358 | case reflect.Interface: 359 | errorInterface := reflect.TypeOf((*error)(nil)).Elem() 360 | if typ.Implements(errorInterface) { 361 | return callbackRetNil, nil 362 | } 363 | fallthrough 364 | case reflect.Slice: 365 | if typ.Elem().Kind() != reflect.Uint8 { 366 | return nil, errors.New("the only supported slice type is []byte") 367 | } 368 | return callbackRetBlob, nil 369 | case reflect.String: 370 | return callbackRetText, nil 371 | case reflect.Bool, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint: 372 | return callbackRetInteger, nil 373 | case reflect.Float32, reflect.Float64: 374 | return callbackRetFloat, nil 375 | default: 376 | return nil, fmt.Errorf("don't know how to convert to %s", typ) 377 | } 378 | } 379 | 380 | func callbackError(ctx *C.sqlite3_context, err error) { 381 | cstr := C.CString(err.Error()) 382 | defer C.free(unsafe.Pointer(cstr)) 383 | C.sqlite3_result_error(ctx, cstr, C.int(-1)) 384 | } 385 | 386 | // Test support code. Tests are not allowed to import "C", so we can't 387 | // declare any functions that use C.sqlite3_value. 388 | func callbackSyntheticForTests(v reflect.Value, err error) callbackArgConverter { 389 | return func(*C.sqlite3_value) (reflect.Value, error) { 390 | return v, err 391 | } 392 | } 393 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/convert.go: -------------------------------------------------------------------------------- 1 | // Extracted from Go database/sql source code 2 | 3 | // Copyright 2011 The Go Authors. All rights reserved. 4 | // Use of this source code is governed by a BSD-style 5 | // license that can be found in the LICENSE file. 6 | 7 | // Type conversions for Scan. 8 | 9 | package sqlite3 10 | 11 | import ( 12 | "database/sql" 13 | "database/sql/driver" 14 | "errors" 15 | "fmt" 16 | "reflect" 17 | "strconv" 18 | "time" 19 | ) 20 | 21 | var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error 22 | 23 | // convertAssign copies to dest the value in src, converting it if possible. 24 | // An error is returned if the copy would result in loss of information. 25 | // dest should be a pointer type. 26 | func convertAssign(dest, src interface{}) error { 27 | // Common cases, without reflect. 28 | switch s := src.(type) { 29 | case string: 30 | switch d := dest.(type) { 31 | case *string: 32 | if d == nil { 33 | return errNilPtr 34 | } 35 | *d = s 36 | return nil 37 | case *[]byte: 38 | if d == nil { 39 | return errNilPtr 40 | } 41 | *d = []byte(s) 42 | return nil 43 | case *sql.RawBytes: 44 | if d == nil { 45 | return errNilPtr 46 | } 47 | *d = append((*d)[:0], s...) 48 | return nil 49 | } 50 | case []byte: 51 | switch d := dest.(type) { 52 | case *string: 53 | if d == nil { 54 | return errNilPtr 55 | } 56 | *d = string(s) 57 | return nil 58 | case *interface{}: 59 | if d == nil { 60 | return errNilPtr 61 | } 62 | *d = cloneBytes(s) 63 | return nil 64 | case *[]byte: 65 | if d == nil { 66 | return errNilPtr 67 | } 68 | *d = cloneBytes(s) 69 | return nil 70 | case *sql.RawBytes: 71 | if d == nil { 72 | return errNilPtr 73 | } 74 | *d = s 75 | return nil 76 | } 77 | case time.Time: 78 | switch d := dest.(type) { 79 | case *time.Time: 80 | *d = s 81 | return nil 82 | case *string: 83 | *d = s.Format(time.RFC3339Nano) 84 | return nil 85 | case *[]byte: 86 | if d == nil { 87 | return errNilPtr 88 | } 89 | *d = []byte(s.Format(time.RFC3339Nano)) 90 | return nil 91 | case *sql.RawBytes: 92 | if d == nil { 93 | return errNilPtr 94 | } 95 | *d = s.AppendFormat((*d)[:0], time.RFC3339Nano) 96 | return nil 97 | } 98 | case nil: 99 | switch d := dest.(type) { 100 | case *interface{}: 101 | if d == nil { 102 | return errNilPtr 103 | } 104 | *d = nil 105 | return nil 106 | case *[]byte: 107 | if d == nil { 108 | return errNilPtr 109 | } 110 | *d = nil 111 | return nil 112 | case *sql.RawBytes: 113 | if d == nil { 114 | return errNilPtr 115 | } 116 | *d = nil 117 | return nil 118 | } 119 | } 120 | 121 | var sv reflect.Value 122 | 123 | switch d := dest.(type) { 124 | case *string: 125 | sv = reflect.ValueOf(src) 126 | switch sv.Kind() { 127 | case reflect.Bool, 128 | reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, 129 | reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, 130 | reflect.Float32, reflect.Float64: 131 | *d = asString(src) 132 | return nil 133 | } 134 | case *[]byte: 135 | sv = reflect.ValueOf(src) 136 | if b, ok := asBytes(nil, sv); ok { 137 | *d = b 138 | return nil 139 | } 140 | case *sql.RawBytes: 141 | sv = reflect.ValueOf(src) 142 | if b, ok := asBytes([]byte(*d)[:0], sv); ok { 143 | *d = sql.RawBytes(b) 144 | return nil 145 | } 146 | case *bool: 147 | bv, err := driver.Bool.ConvertValue(src) 148 | if err == nil { 149 | *d = bv.(bool) 150 | } 151 | return err 152 | case *interface{}: 153 | *d = src 154 | return nil 155 | } 156 | 157 | if scanner, ok := dest.(sql.Scanner); ok { 158 | return scanner.Scan(src) 159 | } 160 | 161 | dpv := reflect.ValueOf(dest) 162 | if dpv.Kind() != reflect.Ptr { 163 | return errors.New("destination not a pointer") 164 | } 165 | if dpv.IsNil() { 166 | return errNilPtr 167 | } 168 | 169 | if !sv.IsValid() { 170 | sv = reflect.ValueOf(src) 171 | } 172 | 173 | dv := reflect.Indirect(dpv) 174 | if sv.IsValid() && sv.Type().AssignableTo(dv.Type()) { 175 | switch b := src.(type) { 176 | case []byte: 177 | dv.Set(reflect.ValueOf(cloneBytes(b))) 178 | default: 179 | dv.Set(sv) 180 | } 181 | return nil 182 | } 183 | 184 | if dv.Kind() == sv.Kind() && sv.Type().ConvertibleTo(dv.Type()) { 185 | dv.Set(sv.Convert(dv.Type())) 186 | return nil 187 | } 188 | 189 | // The following conversions use a string value as an intermediate representation 190 | // to convert between various numeric types. 191 | // 192 | // This also allows scanning into user defined types such as "type Int int64". 193 | // For symmetry, also check for string destination types. 194 | switch dv.Kind() { 195 | case reflect.Ptr: 196 | if src == nil { 197 | dv.Set(reflect.Zero(dv.Type())) 198 | return nil 199 | } 200 | dv.Set(reflect.New(dv.Type().Elem())) 201 | return convertAssign(dv.Interface(), src) 202 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 203 | s := asString(src) 204 | i64, err := strconv.ParseInt(s, 10, dv.Type().Bits()) 205 | if err != nil { 206 | err = strconvErr(err) 207 | return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err) 208 | } 209 | dv.SetInt(i64) 210 | return nil 211 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 212 | s := asString(src) 213 | u64, err := strconv.ParseUint(s, 10, dv.Type().Bits()) 214 | if err != nil { 215 | err = strconvErr(err) 216 | return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err) 217 | } 218 | dv.SetUint(u64) 219 | return nil 220 | case reflect.Float32, reflect.Float64: 221 | s := asString(src) 222 | f64, err := strconv.ParseFloat(s, dv.Type().Bits()) 223 | if err != nil { 224 | err = strconvErr(err) 225 | return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err) 226 | } 227 | dv.SetFloat(f64) 228 | return nil 229 | case reflect.String: 230 | switch v := src.(type) { 231 | case string: 232 | dv.SetString(v) 233 | return nil 234 | case []byte: 235 | dv.SetString(string(v)) 236 | return nil 237 | } 238 | } 239 | 240 | return fmt.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, dest) 241 | } 242 | 243 | func strconvErr(err error) error { 244 | if ne, ok := err.(*strconv.NumError); ok { 245 | return ne.Err 246 | } 247 | return err 248 | } 249 | 250 | func cloneBytes(b []byte) []byte { 251 | if b == nil { 252 | return nil 253 | } 254 | c := make([]byte, len(b)) 255 | copy(c, b) 256 | return c 257 | } 258 | 259 | func asString(src interface{}) string { 260 | switch v := src.(type) { 261 | case string: 262 | return v 263 | case []byte: 264 | return string(v) 265 | } 266 | rv := reflect.ValueOf(src) 267 | switch rv.Kind() { 268 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 269 | return strconv.FormatInt(rv.Int(), 10) 270 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 271 | return strconv.FormatUint(rv.Uint(), 10) 272 | case reflect.Float64: 273 | return strconv.FormatFloat(rv.Float(), 'g', -1, 64) 274 | case reflect.Float32: 275 | return strconv.FormatFloat(rv.Float(), 'g', -1, 32) 276 | case reflect.Bool: 277 | return strconv.FormatBool(rv.Bool()) 278 | } 279 | return fmt.Sprintf("%v", src) 280 | } 281 | 282 | func asBytes(buf []byte, rv reflect.Value) (b []byte, ok bool) { 283 | switch rv.Kind() { 284 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 285 | return strconv.AppendInt(buf, rv.Int(), 10), true 286 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 287 | return strconv.AppendUint(buf, rv.Uint(), 10), true 288 | case reflect.Float32: 289 | return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 32), true 290 | case reflect.Float64: 291 | return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 64), true 292 | case reflect.Bool: 293 | return strconv.AppendBool(buf, rv.Bool()), true 294 | case reflect.String: 295 | s := rv.String() 296 | return append(buf, s...), true 297 | } 298 | return 299 | } 300 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package sqlite3 provides interface to SQLite3 databases. 3 | 4 | This works as a driver for database/sql. 5 | 6 | Installation 7 | 8 | go get github.com/mattn/go-sqlite3 9 | 10 | Supported Types 11 | 12 | Currently, go-sqlite3 supports the following data types. 13 | 14 | +------------------------------+ 15 | |go | sqlite3 | 16 | |----------|-------------------| 17 | |nil | null | 18 | |int | integer | 19 | |int64 | integer | 20 | |float64 | float | 21 | |bool | integer | 22 | |[]byte | blob | 23 | |string | text | 24 | |time.Time | timestamp/datetime| 25 | +------------------------------+ 26 | 27 | SQLite3 Extension 28 | 29 | You can write your own extension module for sqlite3. For example, below is an 30 | extension for a Regexp matcher operation. 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | SQLITE_EXTENSION_INIT1 38 | static void regexp_func(sqlite3_context *context, int argc, sqlite3_value **argv) { 39 | if (argc >= 2) { 40 | const char *target = (const char *)sqlite3_value_text(argv[1]); 41 | const char *pattern = (const char *)sqlite3_value_text(argv[0]); 42 | const char* errstr = NULL; 43 | int erroff = 0; 44 | int vec[500]; 45 | int n, rc; 46 | pcre* re = pcre_compile(pattern, 0, &errstr, &erroff, NULL); 47 | rc = pcre_exec(re, NULL, target, strlen(target), 0, 0, vec, 500); 48 | if (rc <= 0) { 49 | sqlite3_result_error(context, errstr, 0); 50 | return; 51 | } 52 | sqlite3_result_int(context, 1); 53 | } 54 | } 55 | 56 | #ifdef _WIN32 57 | __declspec(dllexport) 58 | #endif 59 | int sqlite3_extension_init(sqlite3 *db, char **errmsg, 60 | const sqlite3_api_routines *api) { 61 | SQLITE_EXTENSION_INIT2(api); 62 | return sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, 63 | (void*)db, regexp_func, NULL, NULL); 64 | } 65 | 66 | It needs to be built as a so/dll shared library. And you need to register 67 | the extension module like below. 68 | 69 | sql.Register("sqlite3_with_extensions", 70 | &sqlite3.SQLiteDriver{ 71 | Extensions: []string{ 72 | "sqlite3_mod_regexp", 73 | }, 74 | }) 75 | 76 | Then, you can use this extension. 77 | 78 | rows, err := db.Query("select text from mytable where name regexp '^golang'") 79 | 80 | Connection Hook 81 | 82 | You can hook and inject your code when the connection is established by setting 83 | ConnectHook to get the SQLiteConn. 84 | 85 | sql.Register("sqlite3_with_hook_example", 86 | &sqlite3.SQLiteDriver{ 87 | ConnectHook: func(conn *sqlite3.SQLiteConn) error { 88 | sqlite3conn = append(sqlite3conn, conn) 89 | return nil 90 | }, 91 | }) 92 | 93 | You can also use database/sql.Conn.Raw (Go >= 1.13): 94 | 95 | conn, err := db.Conn(context.Background()) 96 | // if err != nil { ... } 97 | defer conn.Close() 98 | err = conn.Raw(func (driverConn interface{}) error { 99 | sqliteConn := driverConn.(*sqlite3.SQLiteConn) 100 | // ... use sqliteConn 101 | }) 102 | // if err != nil { ... } 103 | 104 | Go SQlite3 Extensions 105 | 106 | If you want to register Go functions as SQLite extension functions 107 | you can make a custom driver by calling RegisterFunction from 108 | ConnectHook. 109 | 110 | regex = func(re, s string) (bool, error) { 111 | return regexp.MatchString(re, s) 112 | } 113 | sql.Register("sqlite3_extended", 114 | &sqlite3.SQLiteDriver{ 115 | ConnectHook: func(conn *sqlite3.SQLiteConn) error { 116 | return conn.RegisterFunc("regexp", regex, true) 117 | }, 118 | }) 119 | 120 | You can then use the custom driver by passing its name to sql.Open. 121 | 122 | var i int 123 | conn, err := sql.Open("sqlite3_extended", "./foo.db") 124 | if err != nil { 125 | panic(err) 126 | } 127 | err = db.QueryRow(`SELECT regexp("foo.*", "seafood")`).Scan(&i) 128 | if err != nil { 129 | panic(err) 130 | } 131 | 132 | See the documentation of RegisterFunc for more details. 133 | 134 | */ 135 | package sqlite3 136 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/error.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | package sqlite3 7 | 8 | /* 9 | #ifndef USE_LIBSQLITE3 10 | #include "sqlite3-binding.h" 11 | #else 12 | #include 13 | #endif 14 | */ 15 | import "C" 16 | import "syscall" 17 | 18 | // ErrNo inherit errno. 19 | type ErrNo int 20 | 21 | // ErrNoMask is mask code. 22 | const ErrNoMask C.int = 0xff 23 | 24 | // ErrNoExtended is extended errno. 25 | type ErrNoExtended int 26 | 27 | // Error implement sqlite error code. 28 | type Error struct { 29 | Code ErrNo /* The error code returned by SQLite */ 30 | ExtendedCode ErrNoExtended /* The extended error code returned by SQLite */ 31 | SystemErrno syscall.Errno /* The system errno returned by the OS through SQLite, if applicable */ 32 | err string /* The error string returned by sqlite3_errmsg(), 33 | this usually contains more specific details. */ 34 | } 35 | 36 | // result codes from http://www.sqlite.org/c3ref/c_abort.html 37 | var ( 38 | ErrError = ErrNo(1) /* SQL error or missing database */ 39 | ErrInternal = ErrNo(2) /* Internal logic error in SQLite */ 40 | ErrPerm = ErrNo(3) /* Access permission denied */ 41 | ErrAbort = ErrNo(4) /* Callback routine requested an abort */ 42 | ErrBusy = ErrNo(5) /* The database file is locked */ 43 | ErrLocked = ErrNo(6) /* A table in the database is locked */ 44 | ErrNomem = ErrNo(7) /* A malloc() failed */ 45 | ErrReadonly = ErrNo(8) /* Attempt to write a readonly database */ 46 | ErrInterrupt = ErrNo(9) /* Operation terminated by sqlite3_interrupt() */ 47 | ErrIoErr = ErrNo(10) /* Some kind of disk I/O error occurred */ 48 | ErrCorrupt = ErrNo(11) /* The database disk image is malformed */ 49 | ErrNotFound = ErrNo(12) /* Unknown opcode in sqlite3_file_control() */ 50 | ErrFull = ErrNo(13) /* Insertion failed because database is full */ 51 | ErrCantOpen = ErrNo(14) /* Unable to open the database file */ 52 | ErrProtocol = ErrNo(15) /* Database lock protocol error */ 53 | ErrEmpty = ErrNo(16) /* Database is empty */ 54 | ErrSchema = ErrNo(17) /* The database schema changed */ 55 | ErrTooBig = ErrNo(18) /* String or BLOB exceeds size limit */ 56 | ErrConstraint = ErrNo(19) /* Abort due to constraint violation */ 57 | ErrMismatch = ErrNo(20) /* Data type mismatch */ 58 | ErrMisuse = ErrNo(21) /* Library used incorrectly */ 59 | ErrNoLFS = ErrNo(22) /* Uses OS features not supported on host */ 60 | ErrAuth = ErrNo(23) /* Authorization denied */ 61 | ErrFormat = ErrNo(24) /* Auxiliary database format error */ 62 | ErrRange = ErrNo(25) /* 2nd parameter to sqlite3_bind out of range */ 63 | ErrNotADB = ErrNo(26) /* File opened that is not a database file */ 64 | ErrNotice = ErrNo(27) /* Notifications from sqlite3_log() */ 65 | ErrWarning = ErrNo(28) /* Warnings from sqlite3_log() */ 66 | ) 67 | 68 | // Error return error message from errno. 69 | func (err ErrNo) Error() string { 70 | return Error{Code: err}.Error() 71 | } 72 | 73 | // Extend return extended errno. 74 | func (err ErrNo) Extend(by int) ErrNoExtended { 75 | return ErrNoExtended(int(err) | (by << 8)) 76 | } 77 | 78 | // Error return error message that is extended code. 79 | func (err ErrNoExtended) Error() string { 80 | return Error{Code: ErrNo(C.int(err) & ErrNoMask), ExtendedCode: err}.Error() 81 | } 82 | 83 | func (err Error) Error() string { 84 | var str string 85 | if err.err != "" { 86 | str = err.err 87 | } else { 88 | str = C.GoString(C.sqlite3_errstr(C.int(err.Code))) 89 | } 90 | if err.SystemErrno != 0 { 91 | str += ": " + err.SystemErrno.Error() 92 | } 93 | return str 94 | } 95 | 96 | // result codes from http://www.sqlite.org/c3ref/c_abort_rollback.html 97 | var ( 98 | ErrIoErrRead = ErrIoErr.Extend(1) 99 | ErrIoErrShortRead = ErrIoErr.Extend(2) 100 | ErrIoErrWrite = ErrIoErr.Extend(3) 101 | ErrIoErrFsync = ErrIoErr.Extend(4) 102 | ErrIoErrDirFsync = ErrIoErr.Extend(5) 103 | ErrIoErrTruncate = ErrIoErr.Extend(6) 104 | ErrIoErrFstat = ErrIoErr.Extend(7) 105 | ErrIoErrUnlock = ErrIoErr.Extend(8) 106 | ErrIoErrRDlock = ErrIoErr.Extend(9) 107 | ErrIoErrDelete = ErrIoErr.Extend(10) 108 | ErrIoErrBlocked = ErrIoErr.Extend(11) 109 | ErrIoErrNoMem = ErrIoErr.Extend(12) 110 | ErrIoErrAccess = ErrIoErr.Extend(13) 111 | ErrIoErrCheckReservedLock = ErrIoErr.Extend(14) 112 | ErrIoErrLock = ErrIoErr.Extend(15) 113 | ErrIoErrClose = ErrIoErr.Extend(16) 114 | ErrIoErrDirClose = ErrIoErr.Extend(17) 115 | ErrIoErrSHMOpen = ErrIoErr.Extend(18) 116 | ErrIoErrSHMSize = ErrIoErr.Extend(19) 117 | ErrIoErrSHMLock = ErrIoErr.Extend(20) 118 | ErrIoErrSHMMap = ErrIoErr.Extend(21) 119 | ErrIoErrSeek = ErrIoErr.Extend(22) 120 | ErrIoErrDeleteNoent = ErrIoErr.Extend(23) 121 | ErrIoErrMMap = ErrIoErr.Extend(24) 122 | ErrIoErrGetTempPath = ErrIoErr.Extend(25) 123 | ErrIoErrConvPath = ErrIoErr.Extend(26) 124 | ErrLockedSharedCache = ErrLocked.Extend(1) 125 | ErrBusyRecovery = ErrBusy.Extend(1) 126 | ErrBusySnapshot = ErrBusy.Extend(2) 127 | ErrCantOpenNoTempDir = ErrCantOpen.Extend(1) 128 | ErrCantOpenIsDir = ErrCantOpen.Extend(2) 129 | ErrCantOpenFullPath = ErrCantOpen.Extend(3) 130 | ErrCantOpenConvPath = ErrCantOpen.Extend(4) 131 | ErrCorruptVTab = ErrCorrupt.Extend(1) 132 | ErrReadonlyRecovery = ErrReadonly.Extend(1) 133 | ErrReadonlyCantLock = ErrReadonly.Extend(2) 134 | ErrReadonlyRollback = ErrReadonly.Extend(3) 135 | ErrReadonlyDbMoved = ErrReadonly.Extend(4) 136 | ErrAbortRollback = ErrAbort.Extend(2) 137 | ErrConstraintCheck = ErrConstraint.Extend(1) 138 | ErrConstraintCommitHook = ErrConstraint.Extend(2) 139 | ErrConstraintForeignKey = ErrConstraint.Extend(3) 140 | ErrConstraintFunction = ErrConstraint.Extend(4) 141 | ErrConstraintNotNull = ErrConstraint.Extend(5) 142 | ErrConstraintPrimaryKey = ErrConstraint.Extend(6) 143 | ErrConstraintTrigger = ErrConstraint.Extend(7) 144 | ErrConstraintUnique = ErrConstraint.Extend(8) 145 | ErrConstraintVTab = ErrConstraint.Extend(9) 146 | ErrConstraintRowID = ErrConstraint.Extend(10) 147 | ErrNoticeRecoverWAL = ErrNotice.Extend(1) 148 | ErrNoticeRecoverRollback = ErrNotice.Extend(2) 149 | ErrWarningAutoIndex = ErrWarning.Extend(1) 150 | ) 151 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mattn/go-sqlite3 2 | 3 | go 1.12 4 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjojo/gophervista/0778fcb538da8532fa01652dc290c551aa1543c6/vendor/github.com/mattn/go-sqlite3/go.sum -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_context.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | package sqlite3 7 | 8 | /* 9 | 10 | #ifndef USE_LIBSQLITE3 11 | #include "sqlite3-binding.h" 12 | #else 13 | #include 14 | #endif 15 | #include 16 | // These wrappers are necessary because SQLITE_TRANSIENT 17 | // is a pointer constant, and cgo doesn't translate them correctly. 18 | 19 | static inline void my_result_text(sqlite3_context *ctx, char *p, int np) { 20 | sqlite3_result_text(ctx, p, np, SQLITE_TRANSIENT); 21 | } 22 | 23 | static inline void my_result_blob(sqlite3_context *ctx, void *p, int np) { 24 | sqlite3_result_blob(ctx, p, np, SQLITE_TRANSIENT); 25 | } 26 | */ 27 | import "C" 28 | 29 | import ( 30 | "math" 31 | "reflect" 32 | "unsafe" 33 | ) 34 | 35 | const i64 = unsafe.Sizeof(int(0)) > 4 36 | 37 | // SQLiteContext behave sqlite3_context 38 | type SQLiteContext C.sqlite3_context 39 | 40 | // ResultBool sets the result of an SQL function. 41 | func (c *SQLiteContext) ResultBool(b bool) { 42 | if b { 43 | c.ResultInt(1) 44 | } else { 45 | c.ResultInt(0) 46 | } 47 | } 48 | 49 | // ResultBlob sets the result of an SQL function. 50 | // See: sqlite3_result_blob, http://sqlite.org/c3ref/result_blob.html 51 | func (c *SQLiteContext) ResultBlob(b []byte) { 52 | if i64 && len(b) > math.MaxInt32 { 53 | C.sqlite3_result_error_toobig((*C.sqlite3_context)(c)) 54 | return 55 | } 56 | var p *byte 57 | if len(b) > 0 { 58 | p = &b[0] 59 | } 60 | C.my_result_blob((*C.sqlite3_context)(c), unsafe.Pointer(p), C.int(len(b))) 61 | } 62 | 63 | // ResultDouble sets the result of an SQL function. 64 | // See: sqlite3_result_double, http://sqlite.org/c3ref/result_blob.html 65 | func (c *SQLiteContext) ResultDouble(d float64) { 66 | C.sqlite3_result_double((*C.sqlite3_context)(c), C.double(d)) 67 | } 68 | 69 | // ResultInt sets the result of an SQL function. 70 | // See: sqlite3_result_int, http://sqlite.org/c3ref/result_blob.html 71 | func (c *SQLiteContext) ResultInt(i int) { 72 | if i64 && (i > math.MaxInt32 || i < math.MinInt32) { 73 | C.sqlite3_result_int64((*C.sqlite3_context)(c), C.sqlite3_int64(i)) 74 | } else { 75 | C.sqlite3_result_int((*C.sqlite3_context)(c), C.int(i)) 76 | } 77 | } 78 | 79 | // ResultInt64 sets the result of an SQL function. 80 | // See: sqlite3_result_int64, http://sqlite.org/c3ref/result_blob.html 81 | func (c *SQLiteContext) ResultInt64(i int64) { 82 | C.sqlite3_result_int64((*C.sqlite3_context)(c), C.sqlite3_int64(i)) 83 | } 84 | 85 | // ResultNull sets the result of an SQL function. 86 | // See: sqlite3_result_null, http://sqlite.org/c3ref/result_blob.html 87 | func (c *SQLiteContext) ResultNull() { 88 | C.sqlite3_result_null((*C.sqlite3_context)(c)) 89 | } 90 | 91 | // ResultText sets the result of an SQL function. 92 | // See: sqlite3_result_text, http://sqlite.org/c3ref/result_blob.html 93 | func (c *SQLiteContext) ResultText(s string) { 94 | h := (*reflect.StringHeader)(unsafe.Pointer(&s)) 95 | cs, l := (*C.char)(unsafe.Pointer(h.Data)), C.int(h.Len) 96 | C.my_result_text((*C.sqlite3_context)(c), cs, l) 97 | } 98 | 99 | // ResultZeroblob sets the result of an SQL function. 100 | // See: sqlite3_result_zeroblob, http://sqlite.org/c3ref/result_blob.html 101 | func (c *SQLiteContext) ResultZeroblob(n int) { 102 | C.sqlite3_result_zeroblob((*C.sqlite3_context)(c), C.int(n)) 103 | } 104 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_func_crypt.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 G.J.R. Timmer . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | package sqlite3 7 | 8 | import ( 9 | "crypto/sha1" 10 | "crypto/sha256" 11 | "crypto/sha512" 12 | ) 13 | 14 | // This file provides several different implementations for the 15 | // default embedded sqlite_crypt function. 16 | // This function is uses a caesar-cypher by default 17 | // and is used within the UserAuthentication module to encode 18 | // the password. 19 | // 20 | // The provided functions can be used as an overload to the sqlite_crypt 21 | // function through the use of the RegisterFunc on the connection. 22 | // 23 | // Because the functions can serv a purpose to an end-user 24 | // without using the UserAuthentication module 25 | // the functions are default compiled in. 26 | // 27 | // From SQLITE3 - user-auth.txt 28 | // The sqlite_user.pw field is encoded by a built-in SQL function 29 | // "sqlite_crypt(X,Y)". The two arguments are both BLOBs. The first argument 30 | // is the plaintext password supplied to the sqlite3_user_authenticate() 31 | // interface. The second argument is the sqlite_user.pw value and is supplied 32 | // so that the function can extract the "salt" used by the password encoder. 33 | // The result of sqlite_crypt(X,Y) is another blob which is the value that 34 | // ends up being stored in sqlite_user.pw. To verify credentials X supplied 35 | // by the sqlite3_user_authenticate() routine, SQLite runs: 36 | // 37 | // sqlite_user.pw == sqlite_crypt(X, sqlite_user.pw) 38 | // 39 | // To compute an appropriate sqlite_user.pw value from a new or modified 40 | // password X, sqlite_crypt(X,NULL) is run. A new random salt is selected 41 | // when the second argument is NULL. 42 | // 43 | // The built-in version of of sqlite_crypt() uses a simple Caesar-cypher 44 | // which prevents passwords from being revealed by searching the raw database 45 | // for ASCII text, but is otherwise trivally broken. For better password 46 | // security, the database should be encrypted using the SQLite Encryption 47 | // Extension or similar technology. Or, the application can use the 48 | // sqlite3_create_function() interface to provide an alternative 49 | // implementation of sqlite_crypt() that computes a stronger password hash, 50 | // perhaps using a cryptographic hash function like SHA1. 51 | 52 | // CryptEncoderSHA1 encodes a password with SHA1 53 | func CryptEncoderSHA1(pass []byte, hash interface{}) []byte { 54 | h := sha1.Sum(pass) 55 | return h[:] 56 | } 57 | 58 | // CryptEncoderSSHA1 encodes a password with SHA1 with the 59 | // configured salt. 60 | func CryptEncoderSSHA1(salt string) func(pass []byte, hash interface{}) []byte { 61 | return func(pass []byte, hash interface{}) []byte { 62 | s := []byte(salt) 63 | p := append(pass, s...) 64 | h := sha1.Sum(p) 65 | return h[:] 66 | } 67 | } 68 | 69 | // CryptEncoderSHA256 encodes a password with SHA256 70 | func CryptEncoderSHA256(pass []byte, hash interface{}) []byte { 71 | h := sha256.Sum256(pass) 72 | return h[:] 73 | } 74 | 75 | // CryptEncoderSSHA256 encodes a password with SHA256 76 | // with the configured salt 77 | func CryptEncoderSSHA256(salt string) func(pass []byte, hash interface{}) []byte { 78 | return func(pass []byte, hash interface{}) []byte { 79 | s := []byte(salt) 80 | p := append(pass, s...) 81 | h := sha256.Sum256(p) 82 | return h[:] 83 | } 84 | } 85 | 86 | // CryptEncoderSHA384 encodes a password with SHA384 87 | func CryptEncoderSHA384(pass []byte, hash interface{}) []byte { 88 | h := sha512.Sum384(pass) 89 | return h[:] 90 | } 91 | 92 | // CryptEncoderSSHA384 encodes a password with SHA384 93 | // with the configured salt 94 | func CryptEncoderSSHA384(salt string) func(pass []byte, hash interface{}) []byte { 95 | return func(pass []byte, hash interface{}) []byte { 96 | s := []byte(salt) 97 | p := append(pass, s...) 98 | h := sha512.Sum384(p) 99 | return h[:] 100 | } 101 | } 102 | 103 | // CryptEncoderSHA512 encodes a password with SHA512 104 | func CryptEncoderSHA512(pass []byte, hash interface{}) []byte { 105 | h := sha512.Sum512(pass) 106 | return h[:] 107 | } 108 | 109 | // CryptEncoderSSHA512 encodes a password with SHA512 110 | // with the configured salt 111 | func CryptEncoderSSHA512(salt string) func(pass []byte, hash interface{}) []byte { 112 | return func(pass []byte, hash interface{}) []byte { 113 | s := []byte(salt) 114 | p := append(pass, s...) 115 | h := sha512.Sum512(p) 116 | return h[:] 117 | } 118 | } 119 | 120 | // EOF 121 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_go18.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // +build cgo 7 | // +build go1.8 8 | 9 | package sqlite3 10 | 11 | import ( 12 | "database/sql/driver" 13 | 14 | "context" 15 | ) 16 | 17 | // Ping implement Pinger. 18 | func (c *SQLiteConn) Ping(ctx context.Context) error { 19 | if c.db == nil { 20 | // must be ErrBadConn for sql to close the database 21 | return driver.ErrBadConn 22 | } 23 | return nil 24 | } 25 | 26 | // QueryContext implement QueryerContext. 27 | func (c *SQLiteConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) { 28 | list := make([]namedValue, len(args)) 29 | for i, nv := range args { 30 | list[i] = namedValue(nv) 31 | } 32 | return c.query(ctx, query, list) 33 | } 34 | 35 | // ExecContext implement ExecerContext. 36 | func (c *SQLiteConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) { 37 | list := make([]namedValue, len(args)) 38 | for i, nv := range args { 39 | list[i] = namedValue(nv) 40 | } 41 | return c.exec(ctx, query, list) 42 | } 43 | 44 | // PrepareContext implement ConnPrepareContext. 45 | func (c *SQLiteConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) { 46 | return c.prepare(ctx, query) 47 | } 48 | 49 | // BeginTx implement ConnBeginTx. 50 | func (c *SQLiteConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { 51 | return c.begin(ctx) 52 | } 53 | 54 | // QueryContext implement QueryerContext. 55 | func (s *SQLiteStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { 56 | list := make([]namedValue, len(args)) 57 | for i, nv := range args { 58 | list[i] = namedValue(nv) 59 | } 60 | return s.query(ctx, list) 61 | } 62 | 63 | // ExecContext implement ExecerContext. 64 | func (s *SQLiteStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) { 65 | list := make([]namedValue, len(args)) 66 | for i, nv := range args { 67 | list[i] = namedValue(nv) 68 | } 69 | return s.exec(ctx, list) 70 | } 71 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_libsqlite3.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // +build libsqlite3 7 | 8 | package sqlite3 9 | 10 | /* 11 | #cgo CFLAGS: -DUSE_LIBSQLITE3 12 | #cgo linux LDFLAGS: -lsqlite3 13 | #cgo darwin LDFLAGS: -L/usr/local/opt/sqlite/lib -lsqlite3 14 | #cgo darwin CFLAGS: -I/usr/local/opt/sqlite/include 15 | #cgo openbsd LDFLAGS: -lsqlite3 16 | #cgo solaris LDFLAGS: -lsqlite3 17 | #cgo windows LDFLAGS: -lsqlite3 18 | */ 19 | import "C" 20 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_load_extension.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // +build !sqlite_omit_load_extension 7 | 8 | package sqlite3 9 | 10 | /* 11 | #ifndef USE_LIBSQLITE3 12 | #include "sqlite3-binding.h" 13 | #else 14 | #include 15 | #endif 16 | #include 17 | */ 18 | import "C" 19 | import ( 20 | "errors" 21 | "unsafe" 22 | ) 23 | 24 | func (c *SQLiteConn) loadExtensions(extensions []string) error { 25 | rv := C.sqlite3_enable_load_extension(c.db, 1) 26 | if rv != C.SQLITE_OK { 27 | return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) 28 | } 29 | 30 | for _, extension := range extensions { 31 | if err := c.loadExtension(extension, nil); err != nil { 32 | C.sqlite3_enable_load_extension(c.db, 0) 33 | return err 34 | } 35 | } 36 | 37 | rv = C.sqlite3_enable_load_extension(c.db, 0) 38 | if rv != C.SQLITE_OK { 39 | return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) 40 | } 41 | 42 | return nil 43 | } 44 | 45 | // LoadExtension load the sqlite3 extension. 46 | func (c *SQLiteConn) LoadExtension(lib string, entry string) error { 47 | rv := C.sqlite3_enable_load_extension(c.db, 1) 48 | if rv != C.SQLITE_OK { 49 | return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) 50 | } 51 | 52 | if err := c.loadExtension(lib, &entry); err != nil { 53 | C.sqlite3_enable_load_extension(c.db, 0) 54 | return err 55 | } 56 | 57 | rv = C.sqlite3_enable_load_extension(c.db, 0) 58 | if rv != C.SQLITE_OK { 59 | return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) 60 | } 61 | 62 | return nil 63 | } 64 | 65 | func (c *SQLiteConn) loadExtension(lib string, entry *string) error { 66 | clib := C.CString(lib) 67 | defer C.free(unsafe.Pointer(clib)) 68 | 69 | var centry *C.char 70 | if entry != nil { 71 | centry = C.CString(*entry) 72 | defer C.free(unsafe.Pointer(centry)) 73 | } 74 | 75 | var errMsg *C.char 76 | defer C.sqlite3_free(unsafe.Pointer(errMsg)) 77 | 78 | rv := C.sqlite3_load_extension(c.db, clib, centry, &errMsg) 79 | if rv != C.SQLITE_OK { 80 | return errors.New(C.GoString(errMsg)) 81 | } 82 | 83 | return nil 84 | } 85 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_load_extension_omit.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // +build sqlite_omit_load_extension 7 | 8 | package sqlite3 9 | 10 | /* 11 | #cgo CFLAGS: -DSQLITE_OMIT_LOAD_EXTENSION 12 | */ 13 | import "C" 14 | import ( 15 | "errors" 16 | ) 17 | 18 | func (c *SQLiteConn) loadExtensions(extensions []string) error { 19 | return errors.New("Extensions have been disabled for static builds") 20 | } 21 | 22 | func (c *SQLiteConn) LoadExtension(lib string, entry string) error { 23 | return errors.New("Extensions have been disabled for static builds") 24 | } 25 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_opt_allow_uri_authority.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // Copyright (C) 2018 G.J.R. Timmer . 3 | // 4 | // Use of this source code is governed by an MIT-style 5 | // license that can be found in the LICENSE file. 6 | 7 | // +build sqlite_allow_uri_authority 8 | 9 | package sqlite3 10 | 11 | /* 12 | #cgo CFLAGS: -DSQLITE_ALLOW_URI_AUTHORITY 13 | #cgo LDFLAGS: -lm 14 | */ 15 | import "C" 16 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_opt_app_armor.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // Copyright (C) 2018 G.J.R. Timmer . 3 | // 4 | // Use of this source code is governed by an MIT-style 5 | // license that can be found in the LICENSE file. 6 | 7 | // +build !windows 8 | // +build sqlite_app_armor 9 | 10 | package sqlite3 11 | 12 | /* 13 | #cgo CFLAGS: -DSQLITE_ENABLE_API_ARMOR 14 | #cgo LDFLAGS: -lm 15 | */ 16 | import "C" 17 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_opt_column_metadata.go: -------------------------------------------------------------------------------- 1 | // +build sqlite_column_metadata 2 | 3 | package sqlite3 4 | 5 | /* 6 | #ifndef USE_LIBSQLITE3 7 | #cgo CFLAGS: -DSQLITE_ENABLE_COLUMN_METADATA 8 | #include 9 | #else 10 | #include 11 | #endif 12 | */ 13 | import "C" 14 | 15 | // ColumnTableName returns the table that is the origin of a particular result 16 | // column in a SELECT statement. 17 | // 18 | // See https://www.sqlite.org/c3ref/column_database_name.html 19 | func (s *SQLiteStmt) ColumnTableName(n int) string { 20 | return C.GoString(C.sqlite3_column_table_name(s.s, C.int(n))) 21 | } 22 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_opt_foreign_keys.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // Copyright (C) 2018 G.J.R. Timmer . 3 | // 4 | // Use of this source code is governed by an MIT-style 5 | // license that can be found in the LICENSE file. 6 | 7 | // +build sqlite_foreign_keys 8 | 9 | package sqlite3 10 | 11 | /* 12 | #cgo CFLAGS: -DSQLITE_DEFAULT_FOREIGN_KEYS=1 13 | #cgo LDFLAGS: -lm 14 | */ 15 | import "C" 16 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_opt_fts5.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // +build sqlite_fts5 fts5 7 | 8 | package sqlite3 9 | 10 | /* 11 | #cgo CFLAGS: -DSQLITE_ENABLE_FTS5 12 | #cgo LDFLAGS: -lm 13 | */ 14 | import "C" 15 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_opt_icu.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // +build sqlite_icu icu 7 | 8 | package sqlite3 9 | 10 | /* 11 | #cgo LDFLAGS: -licuuc -licui18n 12 | #cgo CFLAGS: -DSQLITE_ENABLE_ICU 13 | #cgo darwin CFLAGS: -I/usr/local/opt/icu4c/include 14 | #cgo darwin LDFLAGS: -L/usr/local/opt/icu4c/lib 15 | #cgo openbsd LDFLAGS: -lsqlite3 16 | */ 17 | import "C" 18 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_opt_introspect.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // Copyright (C) 2018 G.J.R. Timmer . 3 | 4 | // Use of this source code is governed by an MIT-style 5 | // license that can be found in the LICENSE file. 6 | 7 | // +build sqlite_introspect 8 | 9 | package sqlite3 10 | 11 | /* 12 | #cgo CFLAGS: -DSQLITE_INTROSPECTION_PRAGMAS 13 | #cgo LDFLAGS: -lm 14 | */ 15 | import "C" 16 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_opt_json1.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // +build sqlite_json sqlite_json1 json1 7 | 8 | package sqlite3 9 | 10 | /* 11 | #cgo CFLAGS: -DSQLITE_ENABLE_JSON1 12 | */ 13 | import "C" 14 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_opt_preupdate.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 G.J.R. Timmer . 2 | // Copyright (C) 2018 segment.com 3 | // 4 | // Use of this source code is governed by an MIT-style 5 | // license that can be found in the LICENSE file. 6 | 7 | // +build cgo 8 | 9 | package sqlite3 10 | 11 | // SQLitePreUpdateData represents all of the data available during a 12 | // pre-update hook call. 13 | type SQLitePreUpdateData struct { 14 | Conn *SQLiteConn 15 | Op int 16 | DatabaseName string 17 | TableName string 18 | OldRowID int64 19 | NewRowID int64 20 | } 21 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_opt_preupdate_hook.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 G.J.R. Timmer . 2 | // Copyright (C) 2018 segment.com 3 | // 4 | // Use of this source code is governed by an MIT-style 5 | // license that can be found in the LICENSE file. 6 | 7 | // +build sqlite_preupdate_hook 8 | 9 | package sqlite3 10 | 11 | /* 12 | #cgo CFLAGS: -DSQLITE_ENABLE_PREUPDATE_HOOK 13 | #cgo LDFLAGS: -lm 14 | 15 | #ifndef USE_LIBSQLITE3 16 | #include "sqlite3-binding.h" 17 | #else 18 | #include 19 | #endif 20 | #include 21 | #include 22 | 23 | void preUpdateHookTrampoline(void*, sqlite3 *, int, char *, char *, sqlite3_int64, sqlite3_int64); 24 | */ 25 | import "C" 26 | import ( 27 | "errors" 28 | "unsafe" 29 | ) 30 | 31 | // RegisterPreUpdateHook sets the pre-update hook for a connection. 32 | // 33 | // The callback is passed a SQLitePreUpdateData struct with the data for 34 | // the update, as well as methods for fetching copies of impacted data. 35 | // 36 | // If there is an existing update hook for this connection, it will be 37 | // removed. If callback is nil the existing hook (if any) will be removed 38 | // without creating a new one. 39 | func (c *SQLiteConn) RegisterPreUpdateHook(callback func(SQLitePreUpdateData)) { 40 | if callback == nil { 41 | C.sqlite3_preupdate_hook(c.db, nil, nil) 42 | } else { 43 | C.sqlite3_preupdate_hook(c.db, (*[0]byte)(unsafe.Pointer(C.preUpdateHookTrampoline)), unsafe.Pointer(newHandle(c, callback))) 44 | } 45 | } 46 | 47 | // Depth returns the source path of the write, see sqlite3_preupdate_depth() 48 | func (d *SQLitePreUpdateData) Depth() int { 49 | return int(C.sqlite3_preupdate_depth(d.Conn.db)) 50 | } 51 | 52 | // Count returns the number of columns in the row 53 | func (d *SQLitePreUpdateData) Count() int { 54 | return int(C.sqlite3_preupdate_count(d.Conn.db)) 55 | } 56 | 57 | func (d *SQLitePreUpdateData) row(dest []interface{}, new bool) error { 58 | for i := 0; i < d.Count() && i < len(dest); i++ { 59 | var val *C.sqlite3_value 60 | var src interface{} 61 | 62 | // Initially I tried making this just a function pointer argument, but 63 | // it's absurdly complicated to pass C function pointers. 64 | if new { 65 | C.sqlite3_preupdate_new(d.Conn.db, C.int(i), &val) 66 | } else { 67 | C.sqlite3_preupdate_old(d.Conn.db, C.int(i), &val) 68 | } 69 | 70 | switch C.sqlite3_value_type(val) { 71 | case C.SQLITE_INTEGER: 72 | src = int64(C.sqlite3_value_int64(val)) 73 | case C.SQLITE_FLOAT: 74 | src = float64(C.sqlite3_value_double(val)) 75 | case C.SQLITE_BLOB: 76 | len := C.sqlite3_value_bytes(val) 77 | blobptr := C.sqlite3_value_blob(val) 78 | src = C.GoBytes(blobptr, len) 79 | case C.SQLITE_TEXT: 80 | len := C.sqlite3_value_bytes(val) 81 | cstrptr := unsafe.Pointer(C.sqlite3_value_text(val)) 82 | src = C.GoBytes(cstrptr, len) 83 | case C.SQLITE_NULL: 84 | src = nil 85 | } 86 | 87 | err := convertAssign(&dest[i], src) 88 | if err != nil { 89 | return err 90 | } 91 | } 92 | 93 | return nil 94 | } 95 | 96 | // Old populates dest with the row data to be replaced. This works similar to 97 | // database/sql's Rows.Scan() 98 | func (d *SQLitePreUpdateData) Old(dest ...interface{}) error { 99 | if d.Op == SQLITE_INSERT { 100 | return errors.New("There is no old row for INSERT operations") 101 | } 102 | return d.row(dest, false) 103 | } 104 | 105 | // New populates dest with the replacement row data. This works similar to 106 | // database/sql's Rows.Scan() 107 | func (d *SQLitePreUpdateData) New(dest ...interface{}) error { 108 | if d.Op == SQLITE_DELETE { 109 | return errors.New("There is no new row for DELETE operations") 110 | } 111 | return d.row(dest, true) 112 | } 113 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_opt_preupdate_omit.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 G.J.R. Timmer . 2 | // Copyright (C) 2018 segment.com 3 | // 4 | // Use of this source code is governed by an MIT-style 5 | // license that can be found in the LICENSE file. 6 | 7 | // +build !sqlite_preupdate_hook,cgo 8 | 9 | package sqlite3 10 | 11 | // RegisterPreUpdateHook sets the pre-update hook for a connection. 12 | // 13 | // The callback is passed a SQLitePreUpdateData struct with the data for 14 | // the update, as well as methods for fetching copies of impacted data. 15 | // 16 | // If there is an existing update hook for this connection, it will be 17 | // removed. If callback is nil the existing hook (if any) will be removed 18 | // without creating a new one. 19 | func (c *SQLiteConn) RegisterPreUpdateHook(callback func(SQLitePreUpdateData)) { 20 | // NOOP 21 | } 22 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_opt_secure_delete.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // Copyright (C) 2018 G.J.R. Timmer . 3 | // 4 | // Use of this source code is governed by an MIT-style 5 | // license that can be found in the LICENSE file. 6 | 7 | // +build sqlite_secure_delete 8 | 9 | package sqlite3 10 | 11 | /* 12 | #cgo CFLAGS: -DSQLITE_SECURE_DELETE=1 13 | #cgo LDFLAGS: -lm 14 | */ 15 | import "C" 16 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_opt_secure_delete_fast.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // Copyright (C) 2018 G.J.R. Timmer . 3 | // 4 | // Use of this source code is governed by an MIT-style 5 | // license that can be found in the LICENSE file. 6 | 7 | // +build sqlite_secure_delete_fast 8 | 9 | package sqlite3 10 | 11 | /* 12 | #cgo CFLAGS: -DSQLITE_SECURE_DELETE=FAST 13 | #cgo LDFLAGS: -lm 14 | */ 15 | import "C" 16 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_opt_stat4.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // Copyright (C) 2018 G.J.R. Timmer . 3 | // 4 | // Use of this source code is governed by an MIT-style 5 | // license that can be found in the LICENSE file. 6 | 7 | // +build sqlite_stat4 8 | 9 | package sqlite3 10 | 11 | /* 12 | #cgo CFLAGS: -DSQLITE_ENABLE_STAT4 13 | #cgo LDFLAGS: -lm 14 | */ 15 | import "C" 16 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_opt_unlock_notify.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY 7 | #include 8 | #include "sqlite3-binding.h" 9 | 10 | extern int unlock_notify_wait(sqlite3 *db); 11 | 12 | int 13 | _sqlite3_step_blocking(sqlite3_stmt *stmt) 14 | { 15 | int rv; 16 | sqlite3* db; 17 | 18 | db = sqlite3_db_handle(stmt); 19 | for (;;) { 20 | rv = sqlite3_step(stmt); 21 | if (rv != SQLITE_LOCKED) { 22 | break; 23 | } 24 | if (sqlite3_extended_errcode(db) != SQLITE_LOCKED_SHAREDCACHE) { 25 | break; 26 | } 27 | rv = unlock_notify_wait(db); 28 | if (rv != SQLITE_OK) { 29 | break; 30 | } 31 | sqlite3_reset(stmt); 32 | } 33 | 34 | return rv; 35 | } 36 | 37 | int 38 | _sqlite3_step_row_blocking(sqlite3_stmt* stmt, long long* rowid, long long* changes) 39 | { 40 | int rv; 41 | sqlite3* db; 42 | 43 | db = sqlite3_db_handle(stmt); 44 | for (;;) { 45 | rv = sqlite3_step(stmt); 46 | if (rv!=SQLITE_LOCKED) { 47 | break; 48 | } 49 | if (sqlite3_extended_errcode(db) != SQLITE_LOCKED_SHAREDCACHE) { 50 | break; 51 | } 52 | rv = unlock_notify_wait(db); 53 | if (rv != SQLITE_OK) { 54 | break; 55 | } 56 | sqlite3_reset(stmt); 57 | } 58 | 59 | *rowid = (long long) sqlite3_last_insert_rowid(db); 60 | *changes = (long long) sqlite3_changes(db); 61 | return rv; 62 | } 63 | 64 | int 65 | _sqlite3_prepare_v2_blocking(sqlite3 *db, const char *zSql, int nBytes, sqlite3_stmt **ppStmt, const char **pzTail) 66 | { 67 | int rv; 68 | 69 | for (;;) { 70 | rv = sqlite3_prepare_v2(db, zSql, nBytes, ppStmt, pzTail); 71 | if (rv!=SQLITE_LOCKED) { 72 | break; 73 | } 74 | if (sqlite3_extended_errcode(db) != SQLITE_LOCKED_SHAREDCACHE) { 75 | break; 76 | } 77 | rv = unlock_notify_wait(db); 78 | if (rv != SQLITE_OK) { 79 | break; 80 | } 81 | } 82 | 83 | return rv; 84 | } 85 | #endif 86 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_opt_unlock_notify.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // +build cgo 7 | // +build sqlite_unlock_notify 8 | 9 | package sqlite3 10 | 11 | /* 12 | #cgo CFLAGS: -DSQLITE_ENABLE_UNLOCK_NOTIFY 13 | 14 | #include 15 | #include "sqlite3-binding.h" 16 | 17 | extern void unlock_notify_callback(void *arg, int argc); 18 | */ 19 | import "C" 20 | import ( 21 | "fmt" 22 | "math" 23 | "sync" 24 | "unsafe" 25 | ) 26 | 27 | type unlock_notify_table struct { 28 | sync.Mutex 29 | seqnum uint 30 | table map[uint]chan struct{} 31 | } 32 | 33 | var unt unlock_notify_table = unlock_notify_table{table: make(map[uint]chan struct{})} 34 | 35 | func (t *unlock_notify_table) add(c chan struct{}) uint { 36 | t.Lock() 37 | defer t.Unlock() 38 | h := t.seqnum 39 | t.table[h] = c 40 | t.seqnum++ 41 | return h 42 | } 43 | 44 | func (t *unlock_notify_table) remove(h uint) { 45 | t.Lock() 46 | defer t.Unlock() 47 | delete(t.table, h) 48 | } 49 | 50 | func (t *unlock_notify_table) get(h uint) chan struct{} { 51 | t.Lock() 52 | defer t.Unlock() 53 | c, ok := t.table[h] 54 | if !ok { 55 | panic(fmt.Sprintf("Non-existent key for unlcok-notify channel: %d", h)) 56 | } 57 | return c 58 | } 59 | 60 | //export unlock_notify_callback 61 | func unlock_notify_callback(argv unsafe.Pointer, argc C.int) { 62 | for i := 0; i < int(argc); i++ { 63 | parg := ((*(*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.uint)(nil))]*[1]uint)(argv))[i]) 64 | arg := *parg 65 | h := arg[0] 66 | c := unt.get(h) 67 | c <- struct{}{} 68 | } 69 | } 70 | 71 | //export unlock_notify_wait 72 | func unlock_notify_wait(db *C.sqlite3) C.int { 73 | // It has to be a bufferred channel to not block in sqlite_unlock_notify 74 | // as sqlite_unlock_notify could invoke the callback before it returns. 75 | c := make(chan struct{}, 1) 76 | defer close(c) 77 | 78 | h := unt.add(c) 79 | defer unt.remove(h) 80 | 81 | pargv := C.malloc(C.sizeof_uint) 82 | defer C.free(pargv) 83 | 84 | argv := (*[1]uint)(pargv) 85 | argv[0] = h 86 | if rv := C.sqlite3_unlock_notify(db, (*[0]byte)(C.unlock_notify_callback), unsafe.Pointer(pargv)); rv != C.SQLITE_OK { 87 | return rv 88 | } 89 | 90 | <-c 91 | 92 | return C.SQLITE_OK 93 | } 94 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_opt_userauth.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 G.J.R. Timmer . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // +build sqlite_userauth 7 | 8 | package sqlite3 9 | 10 | /* 11 | #cgo CFLAGS: -DSQLITE_USER_AUTHENTICATION 12 | #cgo LDFLAGS: -lm 13 | #ifndef USE_LIBSQLITE3 14 | #include "sqlite3-binding.h" 15 | #else 16 | #include 17 | #endif 18 | #include 19 | 20 | static int 21 | _sqlite3_user_authenticate(sqlite3* db, const char* zUsername, const char* aPW, int nPW) 22 | { 23 | return sqlite3_user_authenticate(db, zUsername, aPW, nPW); 24 | } 25 | 26 | static int 27 | _sqlite3_user_add(sqlite3* db, const char* zUsername, const char* aPW, int nPW, int isAdmin) 28 | { 29 | return sqlite3_user_add(db, zUsername, aPW, nPW, isAdmin); 30 | } 31 | 32 | static int 33 | _sqlite3_user_change(sqlite3* db, const char* zUsername, const char* aPW, int nPW, int isAdmin) 34 | { 35 | return sqlite3_user_change(db, zUsername, aPW, nPW, isAdmin); 36 | } 37 | 38 | static int 39 | _sqlite3_user_delete(sqlite3* db, const char* zUsername) 40 | { 41 | return sqlite3_user_delete(db, zUsername); 42 | } 43 | 44 | static int 45 | _sqlite3_auth_enabled(sqlite3* db) 46 | { 47 | int exists = -1; 48 | 49 | sqlite3_stmt *stmt; 50 | sqlite3_prepare_v2(db, "select count(type) from sqlite_master WHERE type='table' and name='sqlite_user';", -1, &stmt, NULL); 51 | 52 | while ( sqlite3_step(stmt) == SQLITE_ROW) { 53 | exists = sqlite3_column_int(stmt, 0); 54 | } 55 | 56 | sqlite3_finalize(stmt); 57 | 58 | return exists; 59 | } 60 | */ 61 | import "C" 62 | import ( 63 | "errors" 64 | "unsafe" 65 | ) 66 | 67 | const ( 68 | SQLITE_AUTH = C.SQLITE_AUTH 69 | ) 70 | 71 | var ( 72 | ErrUnauthorized = errors.New("SQLITE_AUTH: Unauthorized") 73 | ErrAdminRequired = errors.New("SQLITE_AUTH: Unauthorized; Admin Privileges Required") 74 | ) 75 | 76 | // Authenticate will perform an authentication of the provided username 77 | // and password against the database. 78 | // 79 | // If a database contains the SQLITE_USER table, then the 80 | // call to Authenticate must be invoked with an 81 | // appropriate username and password prior to enable read and write 82 | //access to the database. 83 | // 84 | // Return SQLITE_OK on success or SQLITE_ERROR if the username/password 85 | // combination is incorrect or unknown. 86 | // 87 | // If the SQLITE_USER table is not present in the database file, then 88 | // this interface is a harmless no-op returnning SQLITE_OK. 89 | func (c *SQLiteConn) Authenticate(username, password string) error { 90 | rv := c.authenticate(username, password) 91 | switch rv { 92 | case C.SQLITE_ERROR, C.SQLITE_AUTH: 93 | return ErrUnauthorized 94 | case C.SQLITE_OK: 95 | return nil 96 | default: 97 | return c.lastError() 98 | } 99 | } 100 | 101 | // authenticate provides the actual authentication to SQLite. 102 | // This is not exported for usage in Go. 103 | // It is however exported for usage within SQL by the user. 104 | // 105 | // Returns: 106 | // C.SQLITE_OK (0) 107 | // C.SQLITE_ERROR (1) 108 | // C.SQLITE_AUTH (23) 109 | func (c *SQLiteConn) authenticate(username, password string) int { 110 | // Allocate C Variables 111 | cuser := C.CString(username) 112 | cpass := C.CString(password) 113 | 114 | // Free C Variables 115 | defer func() { 116 | C.free(unsafe.Pointer(cuser)) 117 | C.free(unsafe.Pointer(cpass)) 118 | }() 119 | 120 | return int(C._sqlite3_user_authenticate(c.db, cuser, cpass, C.int(len(password)))) 121 | } 122 | 123 | // AuthUserAdd can be used (by an admin user only) 124 | // to create a new user. When called on a no-authentication-required 125 | // database, this routine converts the database into an authentication- 126 | // required database, automatically makes the added user an 127 | // administrator, and logs in the current connection as that user. 128 | // The AuthUserAdd only works for the "main" database, not 129 | // for any ATTACH-ed databases. Any call to AuthUserAdd by a 130 | // non-admin user results in an error. 131 | func (c *SQLiteConn) AuthUserAdd(username, password string, admin bool) error { 132 | isAdmin := 0 133 | if admin { 134 | isAdmin = 1 135 | } 136 | 137 | rv := c.authUserAdd(username, password, isAdmin) 138 | switch rv { 139 | case C.SQLITE_ERROR, C.SQLITE_AUTH: 140 | return ErrAdminRequired 141 | case C.SQLITE_OK: 142 | return nil 143 | default: 144 | return c.lastError() 145 | } 146 | } 147 | 148 | // authUserAdd enables the User Authentication if not enabled. 149 | // Otherwise it will add a user. 150 | // 151 | // When user authentication is already enabled then this function 152 | // can only be called by an admin. 153 | // 154 | // This is not exported for usage in Go. 155 | // It is however exported for usage within SQL by the user. 156 | // 157 | // Returns: 158 | // C.SQLITE_OK (0) 159 | // C.SQLITE_ERROR (1) 160 | // C.SQLITE_AUTH (23) 161 | func (c *SQLiteConn) authUserAdd(username, password string, admin int) int { 162 | // Allocate C Variables 163 | cuser := C.CString(username) 164 | cpass := C.CString(password) 165 | 166 | // Free C Variables 167 | defer func() { 168 | C.free(unsafe.Pointer(cuser)) 169 | C.free(unsafe.Pointer(cpass)) 170 | }() 171 | 172 | return int(C._sqlite3_user_add(c.db, cuser, cpass, C.int(len(password)), C.int(admin))) 173 | } 174 | 175 | // AuthUserChange can be used to change a users 176 | // login credentials or admin privilege. Any user can change their own 177 | // login credentials. Only an admin user can change another users login 178 | // credentials or admin privilege setting. No user may change their own 179 | // admin privilege setting. 180 | func (c *SQLiteConn) AuthUserChange(username, password string, admin bool) error { 181 | isAdmin := 0 182 | if admin { 183 | isAdmin = 1 184 | } 185 | 186 | rv := c.authUserChange(username, password, isAdmin) 187 | switch rv { 188 | case C.SQLITE_ERROR, C.SQLITE_AUTH: 189 | return ErrAdminRequired 190 | case C.SQLITE_OK: 191 | return nil 192 | default: 193 | return c.lastError() 194 | } 195 | } 196 | 197 | // authUserChange allows to modify a user. 198 | // Users can change their own password. 199 | // 200 | // Only admins can change passwords for other users 201 | // and modify the admin flag. 202 | // 203 | // The admin flag of the current logged in user cannot be changed. 204 | // THis ensures that their is always an admin. 205 | // 206 | // This is not exported for usage in Go. 207 | // It is however exported for usage within SQL by the user. 208 | // 209 | // Returns: 210 | // C.SQLITE_OK (0) 211 | // C.SQLITE_ERROR (1) 212 | // C.SQLITE_AUTH (23) 213 | func (c *SQLiteConn) authUserChange(username, password string, admin int) int { 214 | // Allocate C Variables 215 | cuser := C.CString(username) 216 | cpass := C.CString(password) 217 | 218 | // Free C Variables 219 | defer func() { 220 | C.free(unsafe.Pointer(cuser)) 221 | C.free(unsafe.Pointer(cpass)) 222 | }() 223 | 224 | return int(C._sqlite3_user_change(c.db, cuser, cpass, C.int(len(password)), C.int(admin))) 225 | } 226 | 227 | // AuthUserDelete can be used (by an admin user only) 228 | // to delete a user. The currently logged-in user cannot be deleted, 229 | // which guarantees that there is always an admin user and hence that 230 | // the database cannot be converted into a no-authentication-required 231 | // database. 232 | func (c *SQLiteConn) AuthUserDelete(username string) error { 233 | rv := c.authUserDelete(username) 234 | switch rv { 235 | case C.SQLITE_ERROR, C.SQLITE_AUTH: 236 | return ErrAdminRequired 237 | case C.SQLITE_OK: 238 | return nil 239 | default: 240 | return c.lastError() 241 | } 242 | } 243 | 244 | // authUserDelete can be used to delete a user. 245 | // 246 | // This function can only be executed by an admin. 247 | // 248 | // This is not exported for usage in Go. 249 | // It is however exported for usage within SQL by the user. 250 | // 251 | // Returns: 252 | // C.SQLITE_OK (0) 253 | // C.SQLITE_ERROR (1) 254 | // C.SQLITE_AUTH (23) 255 | func (c *SQLiteConn) authUserDelete(username string) int { 256 | // Allocate C Variables 257 | cuser := C.CString(username) 258 | 259 | // Free C Variables 260 | defer func() { 261 | C.free(unsafe.Pointer(cuser)) 262 | }() 263 | 264 | return int(C._sqlite3_user_delete(c.db, cuser)) 265 | } 266 | 267 | // AuthEnabled checks if the database is protected by user authentication 268 | func (c *SQLiteConn) AuthEnabled() (exists bool) { 269 | rv := c.authEnabled() 270 | if rv == 1 { 271 | exists = true 272 | } 273 | 274 | return 275 | } 276 | 277 | // authEnabled perform the actual check for user authentication. 278 | // 279 | // This is not exported for usage in Go. 280 | // It is however exported for usage within SQL by the user. 281 | // 282 | // Returns: 283 | // 0 - Disabled 284 | // 1 - Enabled 285 | func (c *SQLiteConn) authEnabled() int { 286 | return int(C._sqlite3_auth_enabled(c.db)) 287 | } 288 | 289 | // EOF 290 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_opt_userauth_omit.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 G.J.R. Timmer . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // +build !sqlite_userauth 7 | 8 | package sqlite3 9 | 10 | import ( 11 | "C" 12 | ) 13 | 14 | // Authenticate will perform an authentication of the provided username 15 | // and password against the database. 16 | // 17 | // If a database contains the SQLITE_USER table, then the 18 | // call to Authenticate must be invoked with an 19 | // appropriate username and password prior to enable read and write 20 | //access to the database. 21 | // 22 | // Return SQLITE_OK on success or SQLITE_ERROR if the username/password 23 | // combination is incorrect or unknown. 24 | // 25 | // If the SQLITE_USER table is not present in the database file, then 26 | // this interface is a harmless no-op returnning SQLITE_OK. 27 | func (c *SQLiteConn) Authenticate(username, password string) error { 28 | // NOOP 29 | return nil 30 | } 31 | 32 | // authenticate provides the actual authentication to SQLite. 33 | // This is not exported for usage in Go. 34 | // It is however exported for usage within SQL by the user. 35 | // 36 | // Returns: 37 | // C.SQLITE_OK (0) 38 | // C.SQLITE_ERROR (1) 39 | // C.SQLITE_AUTH (23) 40 | func (c *SQLiteConn) authenticate(username, password string) int { 41 | // NOOP 42 | return 0 43 | } 44 | 45 | // AuthUserAdd can be used (by an admin user only) 46 | // to create a new user. When called on a no-authentication-required 47 | // database, this routine converts the database into an authentication- 48 | // required database, automatically makes the added user an 49 | // administrator, and logs in the current connection as that user. 50 | // The AuthUserAdd only works for the "main" database, not 51 | // for any ATTACH-ed databases. Any call to AuthUserAdd by a 52 | // non-admin user results in an error. 53 | func (c *SQLiteConn) AuthUserAdd(username, password string, admin bool) error { 54 | // NOOP 55 | return nil 56 | } 57 | 58 | // authUserAdd enables the User Authentication if not enabled. 59 | // Otherwise it will add a user. 60 | // 61 | // When user authentication is already enabled then this function 62 | // can only be called by an admin. 63 | // 64 | // This is not exported for usage in Go. 65 | // It is however exported for usage within SQL by the user. 66 | // 67 | // Returns: 68 | // C.SQLITE_OK (0) 69 | // C.SQLITE_ERROR (1) 70 | // C.SQLITE_AUTH (23) 71 | func (c *SQLiteConn) authUserAdd(username, password string, admin int) int { 72 | // NOOP 73 | return 0 74 | } 75 | 76 | // AuthUserChange can be used to change a users 77 | // login credentials or admin privilege. Any user can change their own 78 | // login credentials. Only an admin user can change another users login 79 | // credentials or admin privilege setting. No user may change their own 80 | // admin privilege setting. 81 | func (c *SQLiteConn) AuthUserChange(username, password string, admin bool) error { 82 | // NOOP 83 | return nil 84 | } 85 | 86 | // authUserChange allows to modify a user. 87 | // Users can change their own password. 88 | // 89 | // Only admins can change passwords for other users 90 | // and modify the admin flag. 91 | // 92 | // The admin flag of the current logged in user cannot be changed. 93 | // THis ensures that their is always an admin. 94 | // 95 | // This is not exported for usage in Go. 96 | // It is however exported for usage within SQL by the user. 97 | // 98 | // Returns: 99 | // C.SQLITE_OK (0) 100 | // C.SQLITE_ERROR (1) 101 | // C.SQLITE_AUTH (23) 102 | func (c *SQLiteConn) authUserChange(username, password string, admin int) int { 103 | // NOOP 104 | return 0 105 | } 106 | 107 | // AuthUserDelete can be used (by an admin user only) 108 | // to delete a user. The currently logged-in user cannot be deleted, 109 | // which guarantees that there is always an admin user and hence that 110 | // the database cannot be converted into a no-authentication-required 111 | // database. 112 | func (c *SQLiteConn) AuthUserDelete(username string) error { 113 | // NOOP 114 | return nil 115 | } 116 | 117 | // authUserDelete can be used to delete a user. 118 | // 119 | // This function can only be executed by an admin. 120 | // 121 | // This is not exported for usage in Go. 122 | // It is however exported for usage within SQL by the user. 123 | // 124 | // Returns: 125 | // C.SQLITE_OK (0) 126 | // C.SQLITE_ERROR (1) 127 | // C.SQLITE_AUTH (23) 128 | func (c *SQLiteConn) authUserDelete(username string) int { 129 | // NOOP 130 | return 0 131 | } 132 | 133 | // AuthEnabled checks if the database is protected by user authentication 134 | func (c *SQLiteConn) AuthEnabled() (exists bool) { 135 | // NOOP 136 | return false 137 | } 138 | 139 | // authEnabled perform the actual check for user authentication. 140 | // 141 | // This is not exported for usage in Go. 142 | // It is however exported for usage within SQL by the user. 143 | // 144 | // Returns: 145 | // 0 - Disabled 146 | // 1 - Enabled 147 | func (c *SQLiteConn) authEnabled() int { 148 | // NOOP 149 | return 0 150 | } 151 | 152 | // EOF 153 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_opt_vacuum_full.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // Copyright (C) 2018 G.J.R. Timmer . 3 | // 4 | // Use of this source code is governed by an MIT-style 5 | // license that can be found in the LICENSE file. 6 | 7 | // +build sqlite_vacuum_full 8 | 9 | package sqlite3 10 | 11 | /* 12 | #cgo CFLAGS: -DSQLITE_DEFAULT_AUTOVACUUM=1 13 | #cgo LDFLAGS: -lm 14 | */ 15 | import "C" 16 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_opt_vacuum_incr.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // Copyright (C) 2018 G.J.R. Timmer . 3 | // 4 | // Use of this source code is governed by an MIT-style 5 | // license that can be found in the LICENSE file. 6 | 7 | // +build sqlite_vacuum_incr 8 | 9 | package sqlite3 10 | 11 | /* 12 | #cgo CFLAGS: -DSQLITE_DEFAULT_AUTOVACUUM=2 13 | #cgo LDFLAGS: -lm 14 | */ 15 | import "C" 16 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_opt_vtable.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // +build sqlite_vtable vtable 7 | 8 | package sqlite3 9 | 10 | /* 11 | #cgo CFLAGS: -std=gnu99 12 | #cgo CFLAGS: -DSQLITE_ENABLE_RTREE 13 | #cgo CFLAGS: -DSQLITE_THREADSAFE 14 | #cgo CFLAGS: -DSQLITE_ENABLE_FTS3 15 | #cgo CFLAGS: -DSQLITE_ENABLE_FTS3_PARENTHESIS 16 | #cgo CFLAGS: -DSQLITE_ENABLE_FTS4_UNICODE61 17 | #cgo CFLAGS: -DSQLITE_TRACE_SIZE_LIMIT=15 18 | #cgo CFLAGS: -DSQLITE_ENABLE_COLUMN_METADATA=1 19 | #cgo CFLAGS: -Wno-deprecated-declarations 20 | 21 | #ifndef USE_LIBSQLITE3 22 | #include "sqlite3-binding.h" 23 | #else 24 | #include 25 | #endif 26 | #include 27 | #include 28 | #include 29 | 30 | static inline char *_sqlite3_mprintf(char *zFormat, char *arg) { 31 | return sqlite3_mprintf(zFormat, arg); 32 | } 33 | 34 | typedef struct goVTab goVTab; 35 | 36 | struct goVTab { 37 | sqlite3_vtab base; 38 | void *vTab; 39 | }; 40 | 41 | uintptr_t goMInit(void *db, void *pAux, int argc, char **argv, char **pzErr, int isCreate); 42 | 43 | static int cXInit(sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char **pzErr, int isCreate) { 44 | void *vTab = (void *)goMInit(db, pAux, argc, (char**)argv, pzErr, isCreate); 45 | if (!vTab || *pzErr) { 46 | return SQLITE_ERROR; 47 | } 48 | goVTab *pvTab = (goVTab *)sqlite3_malloc(sizeof(goVTab)); 49 | if (!pvTab) { 50 | *pzErr = sqlite3_mprintf("%s", "Out of memory"); 51 | return SQLITE_NOMEM; 52 | } 53 | memset(pvTab, 0, sizeof(goVTab)); 54 | pvTab->vTab = vTab; 55 | 56 | *ppVTab = (sqlite3_vtab *)pvTab; 57 | *pzErr = 0; 58 | return SQLITE_OK; 59 | } 60 | 61 | static inline int cXCreate(sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char **pzErr) { 62 | return cXInit(db, pAux, argc, argv, ppVTab, pzErr, 1); 63 | } 64 | static inline int cXConnect(sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char **pzErr) { 65 | return cXInit(db, pAux, argc, argv, ppVTab, pzErr, 0); 66 | } 67 | 68 | char* goVBestIndex(void *pVTab, void *icp); 69 | 70 | static inline int cXBestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *info) { 71 | char *pzErr = goVBestIndex(((goVTab*)pVTab)->vTab, info); 72 | if (pzErr) { 73 | if (pVTab->zErrMsg) 74 | sqlite3_free(pVTab->zErrMsg); 75 | pVTab->zErrMsg = pzErr; 76 | return SQLITE_ERROR; 77 | } 78 | return SQLITE_OK; 79 | } 80 | 81 | char* goVRelease(void *pVTab, int isDestroy); 82 | 83 | static int cXRelease(sqlite3_vtab *pVTab, int isDestroy) { 84 | char *pzErr = goVRelease(((goVTab*)pVTab)->vTab, isDestroy); 85 | if (pzErr) { 86 | if (pVTab->zErrMsg) 87 | sqlite3_free(pVTab->zErrMsg); 88 | pVTab->zErrMsg = pzErr; 89 | return SQLITE_ERROR; 90 | } 91 | if (pVTab->zErrMsg) 92 | sqlite3_free(pVTab->zErrMsg); 93 | sqlite3_free(pVTab); 94 | return SQLITE_OK; 95 | } 96 | 97 | static inline int cXDisconnect(sqlite3_vtab *pVTab) { 98 | return cXRelease(pVTab, 0); 99 | } 100 | static inline int cXDestroy(sqlite3_vtab *pVTab) { 101 | return cXRelease(pVTab, 1); 102 | } 103 | 104 | typedef struct goVTabCursor goVTabCursor; 105 | 106 | struct goVTabCursor { 107 | sqlite3_vtab_cursor base; 108 | void *vTabCursor; 109 | }; 110 | 111 | uintptr_t goVOpen(void *pVTab, char **pzErr); 112 | 113 | static int cXOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) { 114 | void *vTabCursor = (void *)goVOpen(((goVTab*)pVTab)->vTab, &(pVTab->zErrMsg)); 115 | goVTabCursor *pCursor = (goVTabCursor *)sqlite3_malloc(sizeof(goVTabCursor)); 116 | if (!pCursor) { 117 | return SQLITE_NOMEM; 118 | } 119 | memset(pCursor, 0, sizeof(goVTabCursor)); 120 | pCursor->vTabCursor = vTabCursor; 121 | *ppCursor = (sqlite3_vtab_cursor *)pCursor; 122 | return SQLITE_OK; 123 | } 124 | 125 | static int setErrMsg(sqlite3_vtab_cursor *pCursor, char *pzErr) { 126 | if (pCursor->pVtab->zErrMsg) 127 | sqlite3_free(pCursor->pVtab->zErrMsg); 128 | pCursor->pVtab->zErrMsg = pzErr; 129 | return SQLITE_ERROR; 130 | } 131 | 132 | char* goVClose(void *pCursor); 133 | 134 | static int cXClose(sqlite3_vtab_cursor *pCursor) { 135 | char *pzErr = goVClose(((goVTabCursor*)pCursor)->vTabCursor); 136 | if (pzErr) { 137 | return setErrMsg(pCursor, pzErr); 138 | } 139 | sqlite3_free(pCursor); 140 | return SQLITE_OK; 141 | } 142 | 143 | char* goVFilter(void *pCursor, int idxNum, char* idxName, int argc, sqlite3_value **argv); 144 | 145 | static int cXFilter(sqlite3_vtab_cursor *pCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv) { 146 | char *pzErr = goVFilter(((goVTabCursor*)pCursor)->vTabCursor, idxNum, (char*)idxStr, argc, argv); 147 | if (pzErr) { 148 | return setErrMsg(pCursor, pzErr); 149 | } 150 | return SQLITE_OK; 151 | } 152 | 153 | char* goVNext(void *pCursor); 154 | 155 | static int cXNext(sqlite3_vtab_cursor *pCursor) { 156 | char *pzErr = goVNext(((goVTabCursor*)pCursor)->vTabCursor); 157 | if (pzErr) { 158 | return setErrMsg(pCursor, pzErr); 159 | } 160 | return SQLITE_OK; 161 | } 162 | 163 | int goVEof(void *pCursor); 164 | 165 | static inline int cXEof(sqlite3_vtab_cursor *pCursor) { 166 | return goVEof(((goVTabCursor*)pCursor)->vTabCursor); 167 | } 168 | 169 | char* goVColumn(void *pCursor, void *cp, int col); 170 | 171 | static int cXColumn(sqlite3_vtab_cursor *pCursor, sqlite3_context *ctx, int i) { 172 | char *pzErr = goVColumn(((goVTabCursor*)pCursor)->vTabCursor, ctx, i); 173 | if (pzErr) { 174 | return setErrMsg(pCursor, pzErr); 175 | } 176 | return SQLITE_OK; 177 | } 178 | 179 | char* goVRowid(void *pCursor, sqlite3_int64 *pRowid); 180 | 181 | static int cXRowid(sqlite3_vtab_cursor *pCursor, sqlite3_int64 *pRowid) { 182 | char *pzErr = goVRowid(((goVTabCursor*)pCursor)->vTabCursor, pRowid); 183 | if (pzErr) { 184 | return setErrMsg(pCursor, pzErr); 185 | } 186 | return SQLITE_OK; 187 | } 188 | 189 | char* goVUpdate(void *pVTab, int argc, sqlite3_value **argv, sqlite3_int64 *pRowid); 190 | 191 | static int cXUpdate(sqlite3_vtab *pVTab, int argc, sqlite3_value **argv, sqlite3_int64 *pRowid) { 192 | char *pzErr = goVUpdate(((goVTab*)pVTab)->vTab, argc, argv, pRowid); 193 | if (pzErr) { 194 | if (pVTab->zErrMsg) 195 | sqlite3_free(pVTab->zErrMsg); 196 | pVTab->zErrMsg = pzErr; 197 | return SQLITE_ERROR; 198 | } 199 | return SQLITE_OK; 200 | } 201 | 202 | static sqlite3_module goModule = { 203 | 0, // iVersion 204 | cXCreate, // xCreate - create a table 205 | cXConnect, // xConnect - connect to an existing table 206 | cXBestIndex, // xBestIndex - Determine search strategy 207 | cXDisconnect, // xDisconnect - Disconnect from a table 208 | cXDestroy, // xDestroy - Drop a table 209 | cXOpen, // xOpen - open a cursor 210 | cXClose, // xClose - close a cursor 211 | cXFilter, // xFilter - configure scan constraints 212 | cXNext, // xNext - advance a cursor 213 | cXEof, // xEof 214 | cXColumn, // xColumn - read data 215 | cXRowid, // xRowid - read data 216 | cXUpdate, // xUpdate - write data 217 | // Not implemented 218 | 0, // xBegin - begin transaction 219 | 0, // xSync - sync transaction 220 | 0, // xCommit - commit transaction 221 | 0, // xRollback - rollback transaction 222 | 0, // xFindFunction - function overloading 223 | 0, // xRename - rename the table 224 | 0, // xSavepoint 225 | 0, // xRelease 226 | 0 // xRollbackTo 227 | }; 228 | 229 | // See https://sqlite.org/vtab.html#eponymous_only_virtual_tables 230 | static sqlite3_module goModuleEponymousOnly = { 231 | 0, // iVersion 232 | 0, // xCreate - create a table, which here is null 233 | cXConnect, // xConnect - connect to an existing table 234 | cXBestIndex, // xBestIndex - Determine search strategy 235 | cXDisconnect, // xDisconnect - Disconnect from a table 236 | cXDestroy, // xDestroy - Drop a table 237 | cXOpen, // xOpen - open a cursor 238 | cXClose, // xClose - close a cursor 239 | cXFilter, // xFilter - configure scan constraints 240 | cXNext, // xNext - advance a cursor 241 | cXEof, // xEof 242 | cXColumn, // xColumn - read data 243 | cXRowid, // xRowid - read data 244 | cXUpdate, // xUpdate - write data 245 | // Not implemented 246 | 0, // xBegin - begin transaction 247 | 0, // xSync - sync transaction 248 | 0, // xCommit - commit transaction 249 | 0, // xRollback - rollback transaction 250 | 0, // xFindFunction - function overloading 251 | 0, // xRename - rename the table 252 | 0, // xSavepoint 253 | 0, // xRelease 254 | 0 // xRollbackTo 255 | }; 256 | 257 | void goMDestroy(void*); 258 | 259 | static int _sqlite3_create_module(sqlite3 *db, const char *zName, uintptr_t pClientData) { 260 | return sqlite3_create_module_v2(db, zName, &goModule, (void*) pClientData, goMDestroy); 261 | } 262 | 263 | static int _sqlite3_create_module_eponymous_only(sqlite3 *db, const char *zName, uintptr_t pClientData) { 264 | return sqlite3_create_module_v2(db, zName, &goModuleEponymousOnly, (void*) pClientData, goMDestroy); 265 | } 266 | */ 267 | import "C" 268 | 269 | import ( 270 | "fmt" 271 | "math" 272 | "reflect" 273 | "unsafe" 274 | ) 275 | 276 | type sqliteModule struct { 277 | c *SQLiteConn 278 | name string 279 | module Module 280 | } 281 | 282 | type sqliteVTab struct { 283 | module *sqliteModule 284 | vTab VTab 285 | } 286 | 287 | type sqliteVTabCursor struct { 288 | vTab *sqliteVTab 289 | vTabCursor VTabCursor 290 | } 291 | 292 | // Op is type of operations. 293 | type Op uint8 294 | 295 | // Op mean identity of operations. 296 | const ( 297 | OpEQ Op = 2 298 | OpGT = 4 299 | OpLE = 8 300 | OpLT = 16 301 | OpGE = 32 302 | OpMATCH = 64 303 | OpLIKE = 65 /* 3.10.0 and later only */ 304 | OpGLOB = 66 /* 3.10.0 and later only */ 305 | OpREGEXP = 67 /* 3.10.0 and later only */ 306 | OpScanUnique = 1 /* Scan visits at most 1 row */ 307 | ) 308 | 309 | // InfoConstraint give information of constraint. 310 | type InfoConstraint struct { 311 | Column int 312 | Op Op 313 | Usable bool 314 | } 315 | 316 | // InfoOrderBy give information of order-by. 317 | type InfoOrderBy struct { 318 | Column int 319 | Desc bool 320 | } 321 | 322 | func constraints(info *C.sqlite3_index_info) []InfoConstraint { 323 | slice := *(*[]C.struct_sqlite3_index_constraint)(unsafe.Pointer(&reflect.SliceHeader{ 324 | Data: uintptr(unsafe.Pointer(info.aConstraint)), 325 | Len: int(info.nConstraint), 326 | Cap: int(info.nConstraint), 327 | })) 328 | 329 | cst := make([]InfoConstraint, 0, len(slice)) 330 | for _, c := range slice { 331 | var usable bool 332 | if c.usable > 0 { 333 | usable = true 334 | } 335 | cst = append(cst, InfoConstraint{ 336 | Column: int(c.iColumn), 337 | Op: Op(c.op), 338 | Usable: usable, 339 | }) 340 | } 341 | return cst 342 | } 343 | 344 | func orderBys(info *C.sqlite3_index_info) []InfoOrderBy { 345 | slice := *(*[]C.struct_sqlite3_index_orderby)(unsafe.Pointer(&reflect.SliceHeader{ 346 | Data: uintptr(unsafe.Pointer(info.aOrderBy)), 347 | Len: int(info.nOrderBy), 348 | Cap: int(info.nOrderBy), 349 | })) 350 | 351 | ob := make([]InfoOrderBy, 0, len(slice)) 352 | for _, c := range slice { 353 | var desc bool 354 | if c.desc > 0 { 355 | desc = true 356 | } 357 | ob = append(ob, InfoOrderBy{ 358 | Column: int(c.iColumn), 359 | Desc: desc, 360 | }) 361 | } 362 | return ob 363 | } 364 | 365 | // IndexResult is a Go struct representation of what eventually ends up in the 366 | // output fields for `sqlite3_index_info` 367 | // See: https://www.sqlite.org/c3ref/index_info.html 368 | type IndexResult struct { 369 | Used []bool // aConstraintUsage 370 | IdxNum int 371 | IdxStr string 372 | AlreadyOrdered bool // orderByConsumed 373 | EstimatedCost float64 374 | EstimatedRows float64 375 | } 376 | 377 | // mPrintf is a utility wrapper around sqlite3_mprintf 378 | func mPrintf(format, arg string) *C.char { 379 | cf := C.CString(format) 380 | defer C.free(unsafe.Pointer(cf)) 381 | ca := C.CString(arg) 382 | defer C.free(unsafe.Pointer(ca)) 383 | return C._sqlite3_mprintf(cf, ca) 384 | } 385 | 386 | //export goMInit 387 | func goMInit(db, pClientData unsafe.Pointer, argc C.int, argv **C.char, pzErr **C.char, isCreate C.int) C.uintptr_t { 388 | m := lookupHandle(pClientData).(*sqliteModule) 389 | if m.c.db != (*C.sqlite3)(db) { 390 | *pzErr = mPrintf("%s", "Inconsistent db handles") 391 | return 0 392 | } 393 | args := make([]string, argc) 394 | var A []*C.char 395 | slice := reflect.SliceHeader{Data: uintptr(unsafe.Pointer(argv)), Len: int(argc), Cap: int(argc)} 396 | a := reflect.NewAt(reflect.TypeOf(A), unsafe.Pointer(&slice)).Elem().Interface() 397 | for i, s := range a.([]*C.char) { 398 | args[i] = C.GoString(s) 399 | } 400 | var vTab VTab 401 | var err error 402 | if isCreate == 1 { 403 | vTab, err = m.module.Create(m.c, args) 404 | } else { 405 | vTab, err = m.module.Connect(m.c, args) 406 | } 407 | 408 | if err != nil { 409 | *pzErr = mPrintf("%s", err.Error()) 410 | return 0 411 | } 412 | vt := sqliteVTab{m, vTab} 413 | *pzErr = nil 414 | return C.uintptr_t(uintptr(newHandle(m.c, &vt))) 415 | } 416 | 417 | //export goVRelease 418 | func goVRelease(pVTab unsafe.Pointer, isDestroy C.int) *C.char { 419 | vt := lookupHandle(pVTab).(*sqliteVTab) 420 | var err error 421 | if isDestroy == 1 { 422 | err = vt.vTab.Destroy() 423 | } else { 424 | err = vt.vTab.Disconnect() 425 | } 426 | if err != nil { 427 | return mPrintf("%s", err.Error()) 428 | } 429 | return nil 430 | } 431 | 432 | //export goVOpen 433 | func goVOpen(pVTab unsafe.Pointer, pzErr **C.char) C.uintptr_t { 434 | vt := lookupHandle(pVTab).(*sqliteVTab) 435 | vTabCursor, err := vt.vTab.Open() 436 | if err != nil { 437 | *pzErr = mPrintf("%s", err.Error()) 438 | return 0 439 | } 440 | vtc := sqliteVTabCursor{vt, vTabCursor} 441 | *pzErr = nil 442 | return C.uintptr_t(uintptr(newHandle(vt.module.c, &vtc))) 443 | } 444 | 445 | //export goVBestIndex 446 | func goVBestIndex(pVTab unsafe.Pointer, icp unsafe.Pointer) *C.char { 447 | vt := lookupHandle(pVTab).(*sqliteVTab) 448 | info := (*C.sqlite3_index_info)(icp) 449 | csts := constraints(info) 450 | res, err := vt.vTab.BestIndex(csts, orderBys(info)) 451 | if err != nil { 452 | return mPrintf("%s", err.Error()) 453 | } 454 | if len(res.Used) != len(csts) { 455 | return mPrintf("Result.Used != expected value", "") 456 | } 457 | 458 | // Get a pointer to constraint_usage struct so we can update in place. 459 | 460 | slice := *(*[]C.struct_sqlite3_index_constraint_usage)(unsafe.Pointer(&reflect.SliceHeader{ 461 | Data: uintptr(unsafe.Pointer(info.aConstraintUsage)), 462 | Len: int(info.nConstraint), 463 | Cap: int(info.nConstraint), 464 | })) 465 | index := 1 466 | for i := range slice { 467 | if res.Used[i] { 468 | slice[i].argvIndex = C.int(index) 469 | slice[i].omit = C.uchar(1) 470 | index++ 471 | } 472 | } 473 | 474 | info.idxNum = C.int(res.IdxNum) 475 | info.idxStr = (*C.char)(C.sqlite3_malloc(C.int(len(res.IdxStr) + 1))) 476 | if info.idxStr == nil { 477 | // C.malloc and C.CString ordinarily do this for you. See https://golang.org/cmd/cgo/ 478 | panic("out of memory") 479 | } 480 | info.needToFreeIdxStr = C.int(1) 481 | 482 | idxStr := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ 483 | Data: uintptr(unsafe.Pointer(info.idxStr)), 484 | Len: len(res.IdxStr) + 1, 485 | Cap: len(res.IdxStr) + 1, 486 | })) 487 | copy(idxStr, res.IdxStr) 488 | idxStr[len(idxStr)-1] = 0 // null-terminated string 489 | 490 | if res.AlreadyOrdered { 491 | info.orderByConsumed = C.int(1) 492 | } 493 | info.estimatedCost = C.double(res.EstimatedCost) 494 | info.estimatedRows = C.sqlite3_int64(res.EstimatedRows) 495 | 496 | return nil 497 | } 498 | 499 | //export goVClose 500 | func goVClose(pCursor unsafe.Pointer) *C.char { 501 | vtc := lookupHandle(pCursor).(*sqliteVTabCursor) 502 | err := vtc.vTabCursor.Close() 503 | if err != nil { 504 | return mPrintf("%s", err.Error()) 505 | } 506 | return nil 507 | } 508 | 509 | //export goMDestroy 510 | func goMDestroy(pClientData unsafe.Pointer) { 511 | m := lookupHandle(pClientData).(*sqliteModule) 512 | m.module.DestroyModule() 513 | } 514 | 515 | //export goVFilter 516 | func goVFilter(pCursor unsafe.Pointer, idxNum C.int, idxName *C.char, argc C.int, argv **C.sqlite3_value) *C.char { 517 | vtc := lookupHandle(pCursor).(*sqliteVTabCursor) 518 | args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc] 519 | vals := make([]interface{}, 0, argc) 520 | for _, v := range args { 521 | conv, err := callbackArgGeneric(v) 522 | if err != nil { 523 | return mPrintf("%s", err.Error()) 524 | } 525 | vals = append(vals, conv.Interface()) 526 | } 527 | err := vtc.vTabCursor.Filter(int(idxNum), C.GoString(idxName), vals) 528 | if err != nil { 529 | return mPrintf("%s", err.Error()) 530 | } 531 | return nil 532 | } 533 | 534 | //export goVNext 535 | func goVNext(pCursor unsafe.Pointer) *C.char { 536 | vtc := lookupHandle(pCursor).(*sqliteVTabCursor) 537 | err := vtc.vTabCursor.Next() 538 | if err != nil { 539 | return mPrintf("%s", err.Error()) 540 | } 541 | return nil 542 | } 543 | 544 | //export goVEof 545 | func goVEof(pCursor unsafe.Pointer) C.int { 546 | vtc := lookupHandle(pCursor).(*sqliteVTabCursor) 547 | err := vtc.vTabCursor.EOF() 548 | if err { 549 | return 1 550 | } 551 | return 0 552 | } 553 | 554 | //export goVColumn 555 | func goVColumn(pCursor, cp unsafe.Pointer, col C.int) *C.char { 556 | vtc := lookupHandle(pCursor).(*sqliteVTabCursor) 557 | c := (*SQLiteContext)(cp) 558 | err := vtc.vTabCursor.Column(c, int(col)) 559 | if err != nil { 560 | return mPrintf("%s", err.Error()) 561 | } 562 | return nil 563 | } 564 | 565 | //export goVRowid 566 | func goVRowid(pCursor unsafe.Pointer, pRowid *C.sqlite3_int64) *C.char { 567 | vtc := lookupHandle(pCursor).(*sqliteVTabCursor) 568 | rowid, err := vtc.vTabCursor.Rowid() 569 | if err != nil { 570 | return mPrintf("%s", err.Error()) 571 | } 572 | *pRowid = C.sqlite3_int64(rowid) 573 | return nil 574 | } 575 | 576 | //export goVUpdate 577 | func goVUpdate(pVTab unsafe.Pointer, argc C.int, argv **C.sqlite3_value, pRowid *C.sqlite3_int64) *C.char { 578 | vt := lookupHandle(pVTab).(*sqliteVTab) 579 | 580 | var tname string 581 | if n, ok := vt.vTab.(interface { 582 | TableName() string 583 | }); ok { 584 | tname = n.TableName() + " " 585 | } 586 | 587 | err := fmt.Errorf("virtual %s table %sis read-only", vt.module.name, tname) 588 | if v, ok := vt.vTab.(VTabUpdater); ok { 589 | // convert argv 590 | args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc] 591 | vals := make([]interface{}, 0, argc) 592 | for _, v := range args { 593 | conv, err := callbackArgGeneric(v) 594 | if err != nil { 595 | return mPrintf("%s", err.Error()) 596 | } 597 | 598 | // work around for SQLITE_NULL 599 | x := conv.Interface() 600 | if z, ok := x.([]byte); ok && z == nil { 601 | x = nil 602 | } 603 | 604 | vals = append(vals, x) 605 | } 606 | 607 | switch { 608 | case argc == 1: 609 | err = v.Delete(vals[0]) 610 | 611 | case argc > 1 && vals[0] == nil: 612 | var id int64 613 | id, err = v.Insert(vals[1], vals[2:]) 614 | if err == nil { 615 | *pRowid = C.sqlite3_int64(id) 616 | } 617 | 618 | case argc > 1: 619 | err = v.Update(vals[1], vals[2:]) 620 | } 621 | } 622 | 623 | if err != nil { 624 | return mPrintf("%s", err.Error()) 625 | } 626 | 627 | return nil 628 | } 629 | 630 | // Module is a "virtual table module", it defines the implementation of a 631 | // virtual tables. See: http://sqlite.org/c3ref/module.html 632 | type Module interface { 633 | // http://sqlite.org/vtab.html#xcreate 634 | Create(c *SQLiteConn, args []string) (VTab, error) 635 | // http://sqlite.org/vtab.html#xconnect 636 | Connect(c *SQLiteConn, args []string) (VTab, error) 637 | // http://sqlite.org/c3ref/create_module.html 638 | DestroyModule() 639 | } 640 | 641 | // EponymousOnlyModule is a "virtual table module" (as above), but 642 | // for defining "eponymous only" virtual tables See: https://sqlite.org/vtab.html#eponymous_only_virtual_tables 643 | type EponymousOnlyModule interface { 644 | Module 645 | EponymousOnlyModule() 646 | } 647 | 648 | // VTab describes a particular instance of the virtual table. 649 | // See: http://sqlite.org/c3ref/vtab.html 650 | type VTab interface { 651 | // http://sqlite.org/vtab.html#xbestindex 652 | BestIndex([]InfoConstraint, []InfoOrderBy) (*IndexResult, error) 653 | // http://sqlite.org/vtab.html#xdisconnect 654 | Disconnect() error 655 | // http://sqlite.org/vtab.html#sqlite3_module.xDestroy 656 | Destroy() error 657 | // http://sqlite.org/vtab.html#xopen 658 | Open() (VTabCursor, error) 659 | } 660 | 661 | // VTabUpdater is a type that allows a VTab to be inserted, updated, or 662 | // deleted. 663 | // See: https://sqlite.org/vtab.html#xupdate 664 | type VTabUpdater interface { 665 | Delete(interface{}) error 666 | Insert(interface{}, []interface{}) (int64, error) 667 | Update(interface{}, []interface{}) error 668 | } 669 | 670 | // VTabCursor describes cursors that point into the virtual table and are used 671 | // to loop through the virtual table. See: http://sqlite.org/c3ref/vtab_cursor.html 672 | type VTabCursor interface { 673 | // http://sqlite.org/vtab.html#xclose 674 | Close() error 675 | // http://sqlite.org/vtab.html#xfilter 676 | Filter(idxNum int, idxStr string, vals []interface{}) error 677 | // http://sqlite.org/vtab.html#xnext 678 | Next() error 679 | // http://sqlite.org/vtab.html#xeof 680 | EOF() bool 681 | // http://sqlite.org/vtab.html#xcolumn 682 | Column(c *SQLiteContext, col int) error 683 | // http://sqlite.org/vtab.html#xrowid 684 | Rowid() (int64, error) 685 | } 686 | 687 | // DeclareVTab declares the Schema of a virtual table. 688 | // See: http://sqlite.org/c3ref/declare_vtab.html 689 | func (c *SQLiteConn) DeclareVTab(sql string) error { 690 | zSQL := C.CString(sql) 691 | defer C.free(unsafe.Pointer(zSQL)) 692 | rv := C.sqlite3_declare_vtab(c.db, zSQL) 693 | if rv != C.SQLITE_OK { 694 | return c.lastError() 695 | } 696 | return nil 697 | } 698 | 699 | // CreateModule registers a virtual table implementation. 700 | // See: http://sqlite.org/c3ref/create_module.html 701 | func (c *SQLiteConn) CreateModule(moduleName string, module Module) error { 702 | mname := C.CString(moduleName) 703 | defer C.free(unsafe.Pointer(mname)) 704 | udm := sqliteModule{c, moduleName, module} 705 | switch module.(type) { 706 | case EponymousOnlyModule: 707 | rv := C._sqlite3_create_module_eponymous_only(c.db, mname, C.uintptr_t(uintptr(newHandle(c, &udm)))) 708 | if rv != C.SQLITE_OK { 709 | return c.lastError() 710 | } 711 | return nil 712 | case Module: 713 | rv := C._sqlite3_create_module(c.db, mname, C.uintptr_t(uintptr(newHandle(c, &udm)))) 714 | if rv != C.SQLITE_OK { 715 | return c.lastError() 716 | } 717 | return nil 718 | } 719 | return nil 720 | } 721 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_other.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // +build !windows 7 | 8 | package sqlite3 9 | 10 | /* 11 | #cgo CFLAGS: -I. 12 | #cgo linux LDFLAGS: -ldl 13 | #cgo linux,ppc LDFLAGS: -lpthread 14 | #cgo linux,ppc64 LDFLAGS: -lpthread 15 | #cgo linux,ppc64le LDFLAGS: -lpthread 16 | */ 17 | import "C" 18 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_solaris.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // +build solaris 7 | 8 | package sqlite3 9 | 10 | /* 11 | #cgo CFLAGS: -D__EXTENSIONS__=1 12 | #cgo LDFLAGS: -lc 13 | */ 14 | import "C" 15 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_trace.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // +build sqlite_trace trace 7 | 8 | package sqlite3 9 | 10 | /* 11 | #ifndef USE_LIBSQLITE3 12 | #include "sqlite3-binding.h" 13 | #else 14 | #include 15 | #endif 16 | #include 17 | 18 | int traceCallbackTrampoline(unsigned int traceEventCode, void *ctx, void *p, void *x); 19 | */ 20 | import "C" 21 | 22 | import ( 23 | "fmt" 24 | "strings" 25 | "sync" 26 | "unsafe" 27 | ) 28 | 29 | // Trace... constants identify the possible events causing callback invocation. 30 | // Values are same as the corresponding SQLite Trace Event Codes. 31 | const ( 32 | TraceStmt = uint32(C.SQLITE_TRACE_STMT) 33 | TraceProfile = uint32(C.SQLITE_TRACE_PROFILE) 34 | TraceRow = uint32(C.SQLITE_TRACE_ROW) 35 | TraceClose = uint32(C.SQLITE_TRACE_CLOSE) 36 | ) 37 | 38 | type TraceInfo struct { 39 | // Pack together the shorter fields, to keep the struct smaller. 40 | // On a 64-bit machine there would be padding 41 | // between EventCode and ConnHandle; having AutoCommit here is "free": 42 | EventCode uint32 43 | AutoCommit bool 44 | ConnHandle uintptr 45 | 46 | // Usually filled, unless EventCode = TraceClose = SQLITE_TRACE_CLOSE: 47 | // identifier for a prepared statement: 48 | StmtHandle uintptr 49 | 50 | // Two strings filled when EventCode = TraceStmt = SQLITE_TRACE_STMT: 51 | // (1) either the unexpanded SQL text of the prepared statement, or 52 | // an SQL comment that indicates the invocation of a trigger; 53 | // (2) expanded SQL, if requested and if (1) is not an SQL comment. 54 | StmtOrTrigger string 55 | ExpandedSQL string // only if requested (TraceConfig.WantExpandedSQL = true) 56 | 57 | // filled when EventCode = TraceProfile = SQLITE_TRACE_PROFILE: 58 | // estimated number of nanoseconds that the prepared statement took to run: 59 | RunTimeNanosec int64 60 | 61 | DBError Error 62 | } 63 | 64 | // TraceUserCallback gives the signature for a trace function 65 | // provided by the user (Go application programmer). 66 | // SQLite 3.14 documentation (as of September 2, 2016) 67 | // for SQL Trace Hook = sqlite3_trace_v2(): 68 | // The integer return value from the callback is currently ignored, 69 | // though this may change in future releases. Callback implementations 70 | // should return zero to ensure future compatibility. 71 | type TraceUserCallback func(TraceInfo) int 72 | 73 | type TraceConfig struct { 74 | Callback TraceUserCallback 75 | EventMask uint32 76 | WantExpandedSQL bool 77 | } 78 | 79 | func fillDBError(dbErr *Error, db *C.sqlite3) { 80 | // See SQLiteConn.lastError(), in file 'sqlite3.go' at the time of writing (Sept 5, 2016) 81 | dbErr.Code = ErrNo(C.sqlite3_errcode(db)) 82 | dbErr.ExtendedCode = ErrNoExtended(C.sqlite3_extended_errcode(db)) 83 | dbErr.err = C.GoString(C.sqlite3_errmsg(db)) 84 | } 85 | 86 | func fillExpandedSQL(info *TraceInfo, db *C.sqlite3, pStmt unsafe.Pointer) { 87 | if pStmt == nil { 88 | panic("No SQLite statement pointer in P arg of trace_v2 callback") 89 | } 90 | 91 | expSQLiteCStr := C.sqlite3_expanded_sql((*C.sqlite3_stmt)(pStmt)) 92 | defer C.sqlite3_free(unsafe.Pointer(expSQLiteCStr)) 93 | if expSQLiteCStr == nil { 94 | fillDBError(&info.DBError, db) 95 | return 96 | } 97 | info.ExpandedSQL = C.GoString(expSQLiteCStr) 98 | } 99 | 100 | //export traceCallbackTrampoline 101 | func traceCallbackTrampoline( 102 | traceEventCode C.uint, 103 | // Parameter named 'C' in SQLite docs = Context given at registration: 104 | ctx unsafe.Pointer, 105 | // Parameter named 'P' in SQLite docs (Primary event data?): 106 | p unsafe.Pointer, 107 | // Parameter named 'X' in SQLite docs (eXtra event data?): 108 | xValue unsafe.Pointer) C.int { 109 | 110 | eventCode := uint32(traceEventCode) 111 | 112 | if ctx == nil { 113 | panic(fmt.Sprintf("No context (ev 0x%x)", traceEventCode)) 114 | } 115 | 116 | contextDB := (*C.sqlite3)(ctx) 117 | connHandle := uintptr(ctx) 118 | 119 | var traceConf TraceConfig 120 | var found bool 121 | if eventCode == TraceClose { 122 | // clean up traceMap: 'pop' means get and delete 123 | traceConf, found = popTraceMapping(connHandle) 124 | } else { 125 | traceConf, found = lookupTraceMapping(connHandle) 126 | } 127 | 128 | if !found { 129 | panic(fmt.Sprintf("Mapping not found for handle 0x%x (ev 0x%x)", 130 | connHandle, eventCode)) 131 | } 132 | 133 | var info TraceInfo 134 | 135 | info.EventCode = eventCode 136 | info.AutoCommit = (int(C.sqlite3_get_autocommit(contextDB)) != 0) 137 | info.ConnHandle = connHandle 138 | 139 | switch eventCode { 140 | case TraceStmt: 141 | info.StmtHandle = uintptr(p) 142 | 143 | var xStr string 144 | if xValue != nil { 145 | xStr = C.GoString((*C.char)(xValue)) 146 | } 147 | info.StmtOrTrigger = xStr 148 | if !strings.HasPrefix(xStr, "--") { 149 | // Not SQL comment, therefore the current event 150 | // is not related to a trigger. 151 | // The user might want to receive the expanded SQL; 152 | // let's check: 153 | if traceConf.WantExpandedSQL { 154 | fillExpandedSQL(&info, contextDB, p) 155 | } 156 | } 157 | 158 | case TraceProfile: 159 | info.StmtHandle = uintptr(p) 160 | 161 | if xValue == nil { 162 | panic("NULL pointer in X arg of trace_v2 callback for SQLITE_TRACE_PROFILE event") 163 | } 164 | 165 | info.RunTimeNanosec = *(*int64)(xValue) 166 | 167 | // sample the error //TODO: is it safe? is it useful? 168 | fillDBError(&info.DBError, contextDB) 169 | 170 | case TraceRow: 171 | info.StmtHandle = uintptr(p) 172 | 173 | case TraceClose: 174 | handle := uintptr(p) 175 | if handle != info.ConnHandle { 176 | panic(fmt.Sprintf("Different conn handle 0x%x (expected 0x%x) in SQLITE_TRACE_CLOSE event.", 177 | handle, info.ConnHandle)) 178 | } 179 | 180 | default: 181 | // Pass unsupported events to the user callback (if configured); 182 | // let the user callback decide whether to panic or ignore them. 183 | } 184 | 185 | // Do not execute user callback when the event was not requested by user! 186 | // Remember that the Close event is always selected when 187 | // registering this callback trampoline with SQLite --- for cleanup. 188 | // In the future there may be more events forced to "selected" in SQLite 189 | // for the driver's needs. 190 | if traceConf.EventMask&eventCode == 0 { 191 | return 0 192 | } 193 | 194 | r := 0 195 | if traceConf.Callback != nil { 196 | r = traceConf.Callback(info) 197 | } 198 | return C.int(r) 199 | } 200 | 201 | type traceMapEntry struct { 202 | config TraceConfig 203 | } 204 | 205 | var traceMapLock sync.Mutex 206 | var traceMap = make(map[uintptr]traceMapEntry) 207 | 208 | func addTraceMapping(connHandle uintptr, traceConf TraceConfig) { 209 | traceMapLock.Lock() 210 | defer traceMapLock.Unlock() 211 | 212 | oldEntryCopy, found := traceMap[connHandle] 213 | if found { 214 | panic(fmt.Sprintf("Adding trace config %v: handle 0x%x already registered (%v).", 215 | traceConf, connHandle, oldEntryCopy.config)) 216 | } 217 | traceMap[connHandle] = traceMapEntry{config: traceConf} 218 | } 219 | 220 | func lookupTraceMapping(connHandle uintptr) (TraceConfig, bool) { 221 | traceMapLock.Lock() 222 | defer traceMapLock.Unlock() 223 | 224 | entryCopy, found := traceMap[connHandle] 225 | return entryCopy.config, found 226 | } 227 | 228 | // 'pop' = get and delete from map before returning the value to the caller 229 | func popTraceMapping(connHandle uintptr) (TraceConfig, bool) { 230 | traceMapLock.Lock() 231 | defer traceMapLock.Unlock() 232 | 233 | entryCopy, found := traceMap[connHandle] 234 | if found { 235 | delete(traceMap, connHandle) 236 | } 237 | return entryCopy.config, found 238 | } 239 | 240 | // SetTrace installs or removes the trace callback for the given database connection. 241 | // It's not named 'RegisterTrace' because only one callback can be kept and called. 242 | // Calling SetTrace a second time on same database connection 243 | // overrides (cancels) any prior callback and all its settings: 244 | // event mask, etc. 245 | func (c *SQLiteConn) SetTrace(requested *TraceConfig) error { 246 | connHandle := uintptr(unsafe.Pointer(c.db)) 247 | 248 | _, _ = popTraceMapping(connHandle) 249 | 250 | if requested == nil { 251 | // The traceMap entry was deleted already by popTraceMapping(): 252 | // can disable all events now, no need to watch for TraceClose. 253 | err := c.setSQLiteTrace(0) 254 | return err 255 | } 256 | 257 | reqCopy := *requested 258 | 259 | // Disable potentially expensive operations 260 | // if their result will not be used. We are doing this 261 | // just in case the caller provided nonsensical input. 262 | if reqCopy.EventMask&TraceStmt == 0 { 263 | reqCopy.WantExpandedSQL = false 264 | } 265 | 266 | addTraceMapping(connHandle, reqCopy) 267 | 268 | // The callback trampoline function does cleanup on Close event, 269 | // regardless of the presence or absence of the user callback. 270 | // Therefore it needs the Close event to be selected: 271 | actualEventMask := uint(reqCopy.EventMask | TraceClose) 272 | err := c.setSQLiteTrace(actualEventMask) 273 | return err 274 | } 275 | 276 | func (c *SQLiteConn) setSQLiteTrace(sqliteEventMask uint) error { 277 | rv := C.sqlite3_trace_v2(c.db, 278 | C.uint(sqliteEventMask), 279 | (*[0]byte)(unsafe.Pointer(C.traceCallbackTrampoline)), 280 | unsafe.Pointer(c.db)) // Fourth arg is same as first: we are 281 | // passing the database connection handle as callback context. 282 | 283 | if rv != C.SQLITE_OK { 284 | return c.lastError() 285 | } 286 | return nil 287 | } 288 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_type.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // Use of this source code is governed by an MIT-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package sqlite3 6 | 7 | /* 8 | #ifndef USE_LIBSQLITE3 9 | #include "sqlite3-binding.h" 10 | #else 11 | #include 12 | #endif 13 | */ 14 | import "C" 15 | import ( 16 | "database/sql" 17 | "reflect" 18 | "strings" 19 | ) 20 | 21 | // ColumnTypeDatabaseTypeName implement RowsColumnTypeDatabaseTypeName. 22 | func (rc *SQLiteRows) ColumnTypeDatabaseTypeName(i int) string { 23 | return C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i))) 24 | } 25 | 26 | /* 27 | func (rc *SQLiteRows) ColumnTypeLength(index int) (length int64, ok bool) { 28 | return 0, false 29 | } 30 | 31 | func (rc *SQLiteRows) ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) { 32 | return 0, 0, false 33 | } 34 | */ 35 | 36 | // ColumnTypeNullable implement RowsColumnTypeNullable. 37 | func (rc *SQLiteRows) ColumnTypeNullable(i int) (nullable, ok bool) { 38 | return true, true 39 | } 40 | 41 | // ColumnTypeScanType implement RowsColumnTypeScanType. 42 | func (rc *SQLiteRows) ColumnTypeScanType(i int) reflect.Type { 43 | //ct := C.sqlite3_column_type(rc.s.s, C.int(i)) // Always returns 5 44 | return scanType(C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i)))) 45 | } 46 | 47 | const ( 48 | SQLITE_INTEGER = iota 49 | SQLITE_TEXT 50 | SQLITE_BLOB 51 | SQLITE_REAL 52 | SQLITE_NUMERIC 53 | SQLITE_TIME 54 | SQLITE_BOOL 55 | SQLITE_NULL 56 | ) 57 | 58 | func scanType(cdt string) reflect.Type { 59 | t := strings.ToUpper(cdt) 60 | i := databaseTypeConvSqlite(t) 61 | switch i { 62 | case SQLITE_INTEGER: 63 | return reflect.TypeOf(sql.NullInt64{}) 64 | case SQLITE_TEXT: 65 | return reflect.TypeOf(sql.NullString{}) 66 | case SQLITE_BLOB: 67 | return reflect.TypeOf(sql.RawBytes{}) 68 | case SQLITE_REAL: 69 | return reflect.TypeOf(sql.NullFloat64{}) 70 | case SQLITE_NUMERIC: 71 | return reflect.TypeOf(sql.NullFloat64{}) 72 | case SQLITE_BOOL: 73 | return reflect.TypeOf(sql.NullBool{}) 74 | case SQLITE_TIME: 75 | return reflect.TypeOf(sql.NullTime{}) 76 | } 77 | return reflect.TypeOf(new(interface{})) 78 | } 79 | 80 | func databaseTypeConvSqlite(t string) int { 81 | if strings.Contains(t, "INT") { 82 | return SQLITE_INTEGER 83 | } 84 | if t == "CLOB" || t == "TEXT" || 85 | strings.Contains(t, "CHAR") { 86 | return SQLITE_TEXT 87 | } 88 | if t == "BLOB" { 89 | return SQLITE_BLOB 90 | } 91 | if t == "REAL" || t == "FLOAT" || 92 | strings.Contains(t, "DOUBLE") { 93 | return SQLITE_REAL 94 | } 95 | if t == "DATE" || t == "DATETIME" || 96 | t == "TIMESTAMP" { 97 | return SQLITE_TIME 98 | } 99 | if t == "NUMERIC" || 100 | strings.Contains(t, "DECIMAL") { 101 | return SQLITE_NUMERIC 102 | } 103 | if t == "BOOLEAN" { 104 | return SQLITE_BOOL 105 | } 106 | 107 | return SQLITE_NULL 108 | } 109 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_usleep_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 G.J.R. Timmer . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // +build cgo 7 | 8 | package sqlite3 9 | 10 | // usleep is a function available on *nix based systems. 11 | // This function is not present in Windows. 12 | // Windows has a sleep function but this works with seconds 13 | // and not with microseconds as usleep. 14 | // 15 | // This code should improve performance on windows because 16 | // without the presence of usleep SQLite waits 1 second. 17 | // 18 | // Source: https://github.com/php/php-src/blob/PHP-5.0/win32/time.c 19 | // License: https://github.com/php/php-src/blob/PHP-5.0/LICENSE 20 | // Details: https://stackoverflow.com/questions/5801813/c-usleep-is-obsolete-workarounds-for-windows-mingw?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa 21 | 22 | /* 23 | #include 24 | 25 | void usleep(__int64 usec) 26 | { 27 | HANDLE timer; 28 | LARGE_INTEGER ft; 29 | 30 | // Convert to 100 nanosecond interval, negative value indicates relative time 31 | ft.QuadPart = -(10*usec); 32 | 33 | timer = CreateWaitableTimer(NULL, TRUE, NULL); 34 | SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); 35 | WaitForSingleObject(timer, INFINITE); 36 | CloseHandle(timer); 37 | } 38 | */ 39 | import "C" 40 | 41 | // EOF 42 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/sqlite3_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // +build windows 7 | 8 | package sqlite3 9 | 10 | /* 11 | #cgo CFLAGS: -I. 12 | #cgo CFLAGS: -fno-stack-check 13 | #cgo CFLAGS: -fno-stack-protector 14 | #cgo CFLAGS: -mno-stack-arg-probe 15 | #cgo LDFLAGS: -lmingwex -lmingw32 16 | #cgo windows,386 CFLAGS: -D_USE_32BIT_TIME_T 17 | */ 18 | import "C" 19 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-sqlite3/static_mock.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // +build !cgo 7 | 8 | package sqlite3 9 | 10 | import ( 11 | "database/sql" 12 | "database/sql/driver" 13 | "errors" 14 | ) 15 | 16 | var errorMsg = errors.New("Binary was compiled with 'CGO_ENABLED=0', go-sqlite3 requires cgo to work. This is a stub") 17 | 18 | func init() { 19 | sql.Register("sqlite3", &SQLiteDriver{}) 20 | } 21 | 22 | type ( 23 | SQLiteDriver struct { 24 | Extensions []string 25 | ConnectHook func(*SQLiteConn) error 26 | } 27 | SQLiteConn struct{} 28 | ) 29 | 30 | func (SQLiteDriver) Open(s string) (driver.Conn, error) { return nil, errorMsg } 31 | func (c *SQLiteConn) RegisterAggregator(string, interface{}, bool) error { return errorMsg } 32 | func (c *SQLiteConn) RegisterAuthorizer(func(int, string, string, string) int) {} 33 | func (c *SQLiteConn) RegisterCollation(string, func(string, string) int) error { return errorMsg } 34 | func (c *SQLiteConn) RegisterCommitHook(func() int) {} 35 | func (c *SQLiteConn) RegisterFunc(string, interface{}, bool) error { return errorMsg } 36 | func (c *SQLiteConn) RegisterRollbackHook(func()) {} 37 | func (c *SQLiteConn) RegisterUpdateHook(func(int, string, string, int64)) {} 38 | -------------------------------------------------------------------------------- /vendor/modules.txt: -------------------------------------------------------------------------------- 1 | # github.com/mattn/go-sqlite3 v1.14.10 2 | ## explicit 3 | github.com/mattn/go-sqlite3 4 | -------------------------------------------------------------------------------- /vm-harness/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "log" 7 | "net" 8 | "os" 9 | "os/exec" 10 | "strings" 11 | "time" 12 | ) 13 | 14 | func main() { 15 | qemucmd := exec.Command("/usr/local/bin/qemu-system-x86_64", 16 | "-m", "192", 17 | "-drive", "file=98.qcow2,id=disk,cache=unsafe", 18 | "-net", "nic,model=ne2k_isa", 19 | "-net", "user,hostfwd=tcp:127.0.0.1:5555-:8443", 20 | 21 | "-drive", "file=0.iso,index=3,media=cdrom", 22 | "-drive", "file=1.iso,index=1,media=cdrom", 23 | "-drive", "file=2.iso,index=2,media=cdrom", 24 | 25 | "-vnc", "127.0.0.1:0", 26 | "-vga", "cirrus", 27 | "-serial", "stdio") // to read off the windows 98 reporter 28 | 29 | qemucmd.Stderr = os.Stderr 30 | serial, err := qemucmd.StdoutPipe() 31 | if err != nil { 32 | log.Fatalf("!!! %s", err.Error()) 33 | } 34 | 35 | c, err := net.Dial("unix", "/var/run/collectd.socket") 36 | if err != nil { 37 | log.Fatalf("Cannot connect to collectd! %s", err.Error()) 38 | } 39 | 40 | go func() { 41 | err := qemucmd.Run() 42 | if err != nil { 43 | log.Fatalf("!!! %s", err.Error()) 44 | } 45 | }() 46 | 47 | time.Sleep(time.Second * 5) 48 | 49 | go func() { 50 | for { 51 | b := make([]byte, 1000) 52 | n, err := c.Read(b) 53 | if err != nil { 54 | return 55 | } 56 | fmt.Print(string(b[:n])) 57 | } 58 | }() 59 | 60 | reader := bufio.NewReader(serial) 61 | for { 62 | ln, _, err := reader.ReadLine() 63 | if err != nil { 64 | return 65 | } 66 | c.Write([]byte(strings.Replace(string(ln), " N: ", " N:", 1) + "\n")) 67 | } 68 | } 69 | --------------------------------------------------------------------------------