Prepare conversion to version 1.0

This commit is contained in:
Tobie Morgan Hitchcock 2021-12-14 09:31:43 +00:00
parent b92219b04b
commit 24e58ba4d1
349 changed files with 0 additions and 74930 deletions

View file

@ -1,3 +0,0 @@
*
!app
!surreal

View file

@ -1,20 +0,0 @@
# EditorConfig coding styles definitions. For more information about the
# properties used in this file, please see the EditorConfig documentation:
# http://editorconfig.org/
root = true
[*]
charset = utf-8
end_of_line = LF
indent_size = 4
indent_style = tab
insert_final_newline = true
trim_trailing_whitespace = true
[*.{yml,json}]
indent_size = 2
indent_style = space
[*.{md,diff}]
trim_trailing_whitespace = false

43
.gitignore vendored
View file

@ -1,43 +0,0 @@
# -----------------------------------
# OS X
# -----------------------------------
# Directory files
.DS_Store
.AppleDouble
.LSOverride
# Thumbnail files
._*
# Files that might appear on external disk
.Spotlight-V100
.Trashes
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# -----------------------------------
# Files
# -----------------------------------
*.test
*.cover
# -----------------------------------
# Folders
# -----------------------------------
app/
dev/
# -----------------------------------
# Specific
# -----------------------------------
surreal
*.db

View file

@ -1,7 +0,0 @@
FROM alpine:latest
RUN apk add --update --no-cache ca-certificates
ADD surreal /usr/bin/
ENTRYPOINT ["surreal"]

201
LICENSE
View file

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright © 2020 SurrealDB Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -1,61 +0,0 @@
# Copyright © 2016 SurrealDB Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
GO ?= CGO_ENABLED=0 go
CGO ?= CGO_ENABLED=1 go
LDF := -s -w
.PHONY: default
default:
@echo "Choose a Makefile target:"
@$(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print " - " $$1}}' | sort
.PHONY: kill
kill:
pkill -9 -f surreal
.PHONY: clean
clean:
$(GO) clean -i -n
.PHONY: tests
tests:
$(GO) test ./...
.PHONY: racer
racer:
$(CGO) test -race ./...
.PHONY: build
build: LDF += $(shell GOPATH=${GOPATH} build/flags.sh)
build:
$(GO) build -v -ldflags '$(LDF)'
.PHONY: install
install: LDF += $(shell GOPATH=${GOPATH} build/flags.sh)
install:
$(GO) install -v -ldflags '$(LDF)'
.PHONY: compile
compile: LDF += $(shell GOPATH=${GOPATH} build/flags.sh)
compile:
GOOS=linux GOARCH=amd64 $(GO) build -v -ldflags '$(LDF)'
docker build --tag surrealdb/surrealdb:latest .
docker push surrealdb/surrealdb:latest
.PHONY: deploy
deploy:
build/macos.sh
build/linux.sh
build/windows.sh

View file

@ -1,77 +0,0 @@
# Surreal
Surreal is a scalable, distributed, strongly-consistent, collaborative document-graph database.
[![](https://img.shields.io/badge/status-alpha-ff00bb.svg?style=flat-square)](https://github.com/surrealdb/surrealdb) [![](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/surrealdb/surrealdb) [![](https://goreportcard.com/badge/github.com/surrealdb/surrealdb?style=flat-square)](https://goreportcard.com/report/github.com/surrealdb/surrealdb) [![](https://img.shields.io/badge/license-Apache_License_2.0-00bfff.svg?style=flat-square)](https://github.com/surrealdb/surrealdb)
#### Features
- NoSQL document-graph database written in [Go](http://golang.org)
- Administrative **database tools**
- Easily import data into a cluster
- Easily export data from a cluster
- Accessible and intuitive web interface
- Multiple **connection methods**
- Connect using REST
- Connect using JSON-RPC
- Connect using Websockets
- Multiple **data querying** methods
- Use advanced SQL queries
- Query using REST url endpoints
- Query using Websocket methods
- Customisable **authentication** access
- Specify public or private access
- Admin access to all database data
- Token access to all database data
- End-user multi-tenancy authentication
- Flexible **data manipulation** queries
- Automatic creation of tables
- Schema-less or schema-full tables
- Automatic data field sanitization
- Mandatory, readonly, and validated data fields
- Define embedded fields, and object arrays
- Advanced customisable **indexing** support
- Single-column indexes
- Multiple-column indexes
- Multiple-compound indexes
- Indexing of embedded data fields
- JS/LUA scripting for custom indexes
- Full-text indexing of all data by default
- **Collaborative** editing and manipulation of data
- Live realtime queries
- Publish data changes
- Subscribe to data changes
- Built-in concurrency control
- Pub/sub over websocket for data updates
- **Encryption** out-of-the-box as standard
- End-to-end intra-cluster communications
- End-user SSL encryption for http endpoints
- Encryption of all data at rest using AES-256
#### Installation
```bash
go get github.com/surrealdb/surrealdb
```
#### Running
```bash
surreal start --port-web 8000
```
#### Clustering
```bash
surreal start --port-web 8000 --port-tcp 33693 --db-path file://surreal-1.db --join localhost:33693 --log-level debug
surreal start --port-web 8001 --port-tcp 33694 --db-path file://surreal-2.db --join localhost:33693 --log-level debug
surreal start --port-web 8002 --port-tcp 33695 --db-path file://surreal-3.db --join localhost:33693 --log-level debug
```
#### Deployment
```bash
docker run --name surreal-1 abcum/surreal start --port-web 8000 --port-tcp 33693 --join localhost:33693 --log-level debug
docker run --name surreal-2 abcum/surreal start --port-web 8001 --port-tcp 33694 --join localhost:33693 --log-level debug
docker run --name surreal-3 abcum/surreal start --port-web 8002 --port-tcp 33695 --join localhost:33693 --log-level debug
```

View file

@ -1,9 +0,0 @@
#!/usr/bin/env sh
set -eu
cd "$(dirname "${0}")/.."
echo '-X "github.com/surrealdb/surrealdb/util/build.rev='$(git rev-parse HEAD)'"' \
'-X "github.com/surrealdb/surrealdb/util/build.ver='$(git describe --tags --abbrev=0 || echo 0.0.0)'"' \
'-X "github.com/surrealdb/surrealdb/util/build.time='$(date -u '+%Y/%m/%d %H:%M:%S')'"'

View file

@ -1,16 +0,0 @@
#!/usr/bin/env bash
VERS=$(git describe --tags --abbrev=0)
NAME=surreal-${VERS}.linux-amd64
HASH=${NAME}.txt
FILE=${NAME}.tgz
GOOS=linux GOARCH=amd64 go build -v -ldflags "$(bash build/flags.sh)"
tar -zcvf $FILE -s "#^#${NAME}/#" surreal
echo $(shasum -a 256 $FILE | cut -f1 -d' ') > $HASH
aws s3 cp --region eu-west-2 --cache-control "no-store" ./$FILE s3://download.surrealdb.com/
aws s3 cp --region eu-west-2 --cache-control "no-store" ./$HASH s3://download.surrealdb.com/
rm -rf $FILE $HASH surreal.exe surreal

View file

@ -1,50 +0,0 @@
#!/usr/bin/env bash
VERS=$(git describe --tags --abbrev=0)
NAME=surreal-${VERS}.darwin-universal
HASH=${NAME}.txt
FILE=${NAME}.tgz
GOOS=darwin GOARCH=amd64 go build -v -o surreal-amd64 -ldflags "$(bash build/flags.sh)"
GOOS=darwin GOARCH=arm64 go build -v -o surreal-arm64 -ldflags "$(bash build/flags.sh)"
lipo -create -output surreal surreal-amd64 surreal-arm64
tar -zcvf $FILE -s "#^#${NAME}/#" surreal
echo $(shasum -a 256 $FILE | cut -f1 -d' ') > $HASH
aws s3 cp --region eu-west-2 --cache-control "no-store" ./$FILE s3://download.surrealdb.com/
aws s3 cp --region eu-west-2 --cache-control "no-store" ./$HASH s3://download.surrealdb.com/
rm -rf $FILE $HASH surreal.exe surreal surreal-amd64 surreal-arm64
# amd64
NAME=surreal-${VERS}.darwin-amd64
HASH=${NAME}.txt
FILE=${NAME}.tgz
GOOS=darwin GOARCH=amd64 go build -v -ldflags "$(bash build/flags.sh)"
tar -zcvf $FILE -s "#^#${NAME}/#" surreal
echo $(shasum -a 256 $FILE | cut -f1 -d' ') > $HASH
aws s3 cp --region eu-west-2 --cache-control "no-store" ./$FILE s3://download.surrealdb.com/
aws s3 cp --region eu-west-2 --cache-control "no-store" ./$HASH s3://download.surrealdb.com/
rm -rf $FILE $HASH surreal.exe surreal
# arm64
NAME=surreal-${VERS}.darwin-arm64
HASH=${NAME}.txt
FILE=${NAME}.tgz
GOOS=darwin GOARCH=amd64 go build -v -ldflags "$(bash build/flags.sh)"
tar -zcvf $FILE -s "#^#${NAME}/#" surreal
echo $(shasum -a 256 $FILE | cut -f1 -d' ') > $HASH
aws s3 cp --region eu-west-2 --cache-control "no-store" ./$FILE s3://download.surrealdb.com/
aws s3 cp --region eu-west-2 --cache-control "no-store" ./$HASH s3://download.surrealdb.com/
rm -rf $FILE $HASH surreal.exe surreal

View file

@ -1,16 +0,0 @@
#!/usr/bin/env bash
VERS=$(git describe --tags --abbrev=0)
NAME=surreal-${VERS}.windows-amd64
HASH=${NAME}.txt
FILE=${NAME}.tgz
GOOS=windows GOARCH=amd64 go build -v -ldflags "$(bash build/flags.sh)"
tar -zcvf $FILE -s "#^#${NAME}/#" surreal.exe
echo $(shasum -a 256 $FILE | cut -f1 -d' ') > $HASH
aws s3 cp --region eu-west-2 --cache-control "no-store" ./$FILE s3://download.surrealdb.com/
aws s3 cp --region eu-west-2 --cache-control "no-store" ./$HASH s3://download.surrealdb.com/
rm -rf $FILE $HASH surreal.exe surreal

View file

@ -1,56 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cli
import (
"os"
"github.com/spf13/cobra"
"github.com/surrealdb/surrealdb/cnf"
)
var opts *cnf.Options
var mainCmd = &cobra.Command{
Use: "surreal",
Short: "SurrealDB command-line interface and server",
}
func init() {
mainCmd.AddCommand(
startCmd,
importCmd,
exportCmd,
versionCmd,
)
opts = &cnf.Options{}
mainCmd.PersistentFlags().StringVar(&opts.Logging.Level, "log-level", "error", "Specify log verbosity")
mainCmd.PersistentFlags().StringVar(&opts.Logging.Output, "log-output", "stderr", "Specify log output destination")
mainCmd.PersistentFlags().StringVar(&opts.Logging.Format, "log-format", "text", "Specify log output format (text, json)")
cobra.OnInitialize(setup)
}
// Init runs the cli app
func Init() {
if err := mainCmd.Execute(); err != nil {
os.Exit(1)
}
}

View file

@ -1,151 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cli
import (
"fmt"
"io"
"net/http"
"os"
"io/ioutil"
"github.com/spf13/cobra"
"github.com/surrealdb/surrealdb/log"
)
var (
exportUser string
exportPass string
exportConn string
exportNS string
exportDB string
)
var exportCmd = &cobra.Command{
Use: "export [flags] <file>",
Short: "Export an existing database into a SQL script",
Example: " surreal export --auth root:root backup.sql",
RunE: func(cmd *cobra.Command, args []string) (err error) {
var fle *os.File
var req *http.Request
var res *http.Response
// Ensure that the command has a filepath
// as the output file argument. If no filepath
// has been provided then return an error.
if len(args) != 1 {
log.Fatalln("No filepath provided.")
return
}
// Attempt to open or create the specified file
// in write-only mode, and if there is a problem
// creating the file, then return an error.
if fle, err = os.OpenFile(args[0], os.O_CREATE|os.O_WRONLY, 0644); err != nil {
log.Fatalln("Export failed - please check the filepath and try again.")
return
}
defer fle.Close()
// Create a new http request object that we
// can use to connect to the export endpoint
// using a GET http request type.
url := fmt.Sprintf("%s/export", exportConn)
if req, err = http.NewRequest("GET", url, nil); err != nil {
log.Fatalln("Connection failed - check the connection details and try again.")
return
}
// Specify that the request is an octet stream
req.Header.Set("Content-Type", "application/octet-stream")
// Specify the db authentication settings
req.SetBasicAuth(exportUser, exportPass)
// Specify the namespace to export
req.Header.Set("NS", exportNS)
// Specify the database to export
req.Header.Set("DB", exportDB)
// Attempt to dial the export endpoint and
// if there is an error then stop execution
// and return the connection error.
if res, err = http.DefaultClient.Do(req); err != nil {
log.Fatalln("Connection failed - check the connection details and try again.")
return
}
// Ensure that we close the body, otherwise
// if the Body is not closed, the Client can
// not use the underlying transport again.
defer res.Body.Close()
// Ensure that we didn't receive a 401 status
// code back from the server, otherwise there
// was a problem with our authentication.
if res.StatusCode == 401 {
log.Fatalln("Authentication failed - check the connection details and try again.")
return
}
// Ensure that we received a http 200 status
// code back from the server, otherwise there
// was a problem with our request.
if res.StatusCode != 200 {
bdy, _ := ioutil.ReadAll(res.Body)
log.Fatalf("%s", bdy)
return
}
// Copy the http response body to stdOut so
// that we can pipe the response to other
// commands or processes.
if _, err = io.Copy(fle, res.Body); err != nil {
log.Fatalln("Export failed - there was an error saving the database content.")
return
}
return
},
}
func init() {
exportCmd.PersistentFlags().StringVarP(&exportUser, "user", "u", "root", "Database authentication username to use when connecting.")
exportCmd.PersistentFlags().StringVarP(&exportPass, "pass", "p", "pass", "Database authentication password to use when connecting.")
exportCmd.PersistentFlags().StringVarP(&exportConn, "conn", "c", "https://surreal.io", "Remote database server url to connect to.")
exportCmd.PersistentFlags().StringVar(&exportNS, "ns", "", "Master authentication details to use when connecting.")
exportCmd.PersistentFlags().StringVar(&exportDB, "db", "", "Master authentication details to use when connecting.")
}

View file

@ -1,141 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cli
import (
"fmt"
"net/http"
"os"
"io/ioutil"
"github.com/spf13/cobra"
"github.com/surrealdb/surrealdb/log"
)
var (
importUser string
importPass string
importConn string
importNS string
importDB string
)
var importCmd = &cobra.Command{
Use: "import [flags] <file>",
Short: "Import a SQL script into an existing database",
Example: " surreal import --auth root:root backup.sql",
RunE: func(cmd *cobra.Command, args []string) (err error) {
var fle *os.File
var req *http.Request
var res *http.Response
// Ensure that the command has a filepath
// as the output file argument. If no filepath
// has been provided then return an error.
if len(args) != 1 {
log.Fatalln("No filepath provided.")
return
}
// Attempt to open or create the specified file
// in write-only mode, and if there is a problem
// creating the file, then return an error.
if fle, err = os.OpenFile(args[0], os.O_RDONLY, 0644); err != nil {
log.Fatalln("SQL failed - please check the filepath and try again.")
return
}
defer fle.Close()
// Configure the sql connection endpoint url
// and specify the authentication header using
// basic auth for root login.
url := fmt.Sprintf("%s/sql", importConn)
if req, err = http.NewRequest("POST", url, fle); err != nil {
log.Fatalln("Connection failed - check the connection details and try again.")
return
}
// Specify that the request is plain text
req.Header.Set("Content-Type", "text/plain")
// Specify the db authentication settings
req.SetBasicAuth(importUser, importPass)
// Specify the namespace to import
req.Header.Set("NS", importNS)
// Specify the database to import
req.Header.Set("DB", importDB)
// Attempt to dial the sql endpoint and
// if there is an error then stop execution
// and return the connection error.
if res, err = http.DefaultClient.Do(req); err != nil {
log.Fatalln("Connection failed - check the connection details and try again.")
return
}
// Ensure that we close the body, otherwise
// if the Body is not closed, the Client can
// not use the underlying transport again.
defer res.Body.Close()
// Ensure that we didn't receive a 401 status
// code back from the server, otherwise there
// was a problem with our authentication.
if res.StatusCode == 401 {
log.Fatalln("Authentication failed - check the connection details and try again.")
return
}
// Ensure that we received a http 200 status
// code back from the server, otherwise there
// was a problem with our request.
if res.StatusCode != 200 {
bdy, _ := ioutil.ReadAll(res.Body)
log.Fatalf("%s", bdy)
return
}
return
},
}
func init() {
importCmd.PersistentFlags().StringVarP(&importUser, "user", "u", "root", "Database authentication username to use when connecting.")
importCmd.PersistentFlags().StringVarP(&importPass, "pass", "p", "pass", "Database authentication password to use when connecting.")
importCmd.PersistentFlags().StringVarP(&importConn, "conn", "c", "https://surreal.io", "Remote database server url to connect to.")
importCmd.PersistentFlags().StringVar(&importNS, "ns", "", "Master authentication details to use when connecting.")
importCmd.PersistentFlags().StringVar(&importDB, "db", "", "Master authentication details to use when connecting.")
}

View file

@ -1,27 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cli
const logo = `
.d8888b. 888 8888888b. 888888b.
d88P Y88b 888 888 "Y88b 888 "88b
Y88b. 888 888 888 888 .88P
"Y888b. 888 888 888d888 888d888 .d88b. 8888b. 888 888 888 8888888K.
"Y88b. 888 888 888P" 888P" d8P Y8b "88b 888 888 888 888 "Y88b
"888 888 888 888 888 88888888 .d888888 888 888 888 888 888
Y88b d88P Y88b 888 888 888 Y8b. 888 888 888 888 .d88P 888 d88P
"Y8888P" "Y88888 888 888 "Y8888 "Y888888 888 8888888P" 8888888P"
`

View file

@ -1,250 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cli
import (
"fmt"
"net"
"os"
"path"
"regexp"
"strings"
"encoding/pem"
"github.com/surrealdb/surrealdb/cnf"
"github.com/surrealdb/surrealdb/log"
"github.com/surrealdb/surrealdb/util/rand"
)
func setup() {
// --------------------------------------------------
// DB
// --------------------------------------------------
// Ensure that the default
// database options are set
if opts.DB.Path == "" {
opts.DB.Path = "memory"
}
if opts.DB.Base == "" {
opts.DB.Base = "surreal"
}
if opts.DB.Code != "" {
opts.DB.Key = []byte(opts.DB.Code)
}
switch len(opts.DB.Key) {
case 0, 16, 24, 32:
default:
log.Fatal("Specify a valid encryption key length. Valid key sizes are 16bit, 24bit, or 32bit.")
}
if opts.DB.Path != "memory" {
if ok, _ := regexp.MatchString(`^(file|dendrodb)://(.+)$`, opts.DB.Path); !ok {
log.Fatalf("Invalid path %s. Specify a valid data store configuration path", opts.DB.Path)
}
}
if opts.DB.Cert.CA != "" || opts.DB.Cert.Crt != "" || opts.DB.Cert.Key != "" {
opts.DB.Cert.SSL = true
if dec, _ := pem.Decode([]byte(opts.DB.Cert.CA)); dec == nil || dec.Type != "CERTIFICATE" {
log.Fatal("Specify a valid PEM encoded CA file.")
}
if dec, _ := pem.Decode([]byte(opts.DB.Cert.Crt)); dec == nil || dec.Type != "CERTIFICATE" {
log.Fatal("Specify a valid PEM encoded certificate file.")
}
if dec, _ := pem.Decode([]byte(opts.DB.Cert.Key)); dec == nil || dec.Type != "RSA PRIVATE KEY" {
log.Fatal("Specify a valid PEM encoded private key file.")
}
}
// --------------------------------------------------
// Ports
// --------------------------------------------------
// Specify default port
if opts.Port == 0 {
opts.Port = 8000
}
// Specift default host
if opts.Bind == "" {
opts.Bind = "0.0.0.0"
}
// Ensure port number is valid
if opts.Port < 0 || opts.Port > 65535 {
log.Fatalf("Invalid port %d. Please specify a valid port number for --port-web", opts.Port)
}
// Store the ports in host:port string format
opts.Conn = fmt.Sprintf("%s:%d", opts.Bind, opts.Port)
// --------------------------------------------------
// Auth
// --------------------------------------------------
if opts.Auth.Auth != "" {
if opts.Auth.User != "" {
log.Fatal("Specify only --auth or --auth-user")
}
if opts.Auth.Pass != "" {
log.Fatal("Specify only --auth or --auth-pass")
}
both := strings.SplitN(opts.Auth.Auth, ":", 2)
if len(both) == 2 {
opts.Auth.User = both[0]
opts.Auth.Pass = both[1]
}
}
// Ensure that security
// is enabled by default
if opts.Auth.User == "" {
opts.Auth.User = "root"
}
if opts.Auth.Pass == "" {
opts.Auth.Pass = string(rand.New(20))
}
// Ensure that login as
// root can only be from
// specified ip addresses
for _, cidr := range opts.Auth.Addr {
_, subn, err := net.ParseCIDR(cidr)
if err != nil {
log.Fatalf("Invalid cidr %s. Please specify a valid CIDR address for --auth-addr", cidr)
}
opts.Auth.Nets = append(opts.Auth.Nets, subn)
}
// --------------------------------------------------
// Certs
// --------------------------------------------------
if strings.HasPrefix(opts.Cert.Crt, "-----") {
var err error
var doc *os.File
var out string = path.Join(os.TempDir(), "surreal.crt")
if doc, err = os.Create(out); err != nil {
log.Fatalf("Can not decode PEM encoded certificate into %s", out)
}
doc.Write([]byte(opts.Cert.Crt))
doc.Close()
opts.Cert.Crt = out
}
if strings.HasPrefix(opts.Cert.Key, "-----") {
var err error
var doc *os.File
var out string = path.Join(os.TempDir(), "surreal.key")
if doc, err = os.Create(out); err != nil {
log.Fatalf("Can not decode PEM encoded private key into %s", out)
}
doc.Write([]byte(opts.Cert.Key))
doc.Close()
opts.Cert.Key = out
}
// --------------------------------------------------
// Logging
// --------------------------------------------------
var chk map[string]bool
// Ensure that the specified
// logging level is allowed
if opts.Logging.Level != "" {
chk = map[string]bool{
"trace": true,
"debug": true,
"info": true,
"warn": true,
"error": true,
"fatal": true,
"panic": true,
}
if _, ok := chk[opts.Logging.Level]; !ok {
log.Fatal("Incorrect log level specified")
}
log.SetLevel(opts.Logging.Level)
}
// Ensure that the specified
// logging format is allowed
if opts.Logging.Format != "" {
chk = map[string]bool{
"text": true,
"json": true,
}
if _, ok := chk[opts.Logging.Format]; !ok {
log.Fatal("Incorrect log format specified")
}
log.SetFormat(opts.Logging.Format)
}
// Ensure that the specified
// logging output is allowed
if opts.Logging.Output != "" {
chk = map[string]bool{
"none": true,
"stdout": true,
"stderr": true,
"stackdriver": true,
}
if _, ok := chk[opts.Logging.Output]; !ok {
log.Fatal("Incorrect log output specified")
}
log.SetOutput(opts.Logging.Output)
}
// Enable global options object
cnf.Settings = opts
}

View file

@ -1,99 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cli
import (
"github.com/spf13/cobra"
"github.com/surrealdb/surrealdb/db"
"github.com/surrealdb/surrealdb/kvs"
"github.com/surrealdb/surrealdb/log"
"github.com/surrealdb/surrealdb/web"
)
var startCmd = &cobra.Command{
Use: "start [flags]",
Short: "Start the database and http server",
PreRun: func(cmd *cobra.Command, args []string) {
log.Display(logo)
},
RunE: func(cmd *cobra.Command, args []string) (err error) {
if err = kvs.Setup(opts); err != nil {
log.Fatal(err)
return
}
if err = db.Setup(opts); err != nil {
log.Fatal(err)
return
}
if err = web.Setup(opts); err != nil {
log.Fatal(err)
return
}
return
},
PostRunE: func(cmd *cobra.Command, args []string) (err error) {
if err = web.Exit(opts); err != nil {
log.Fatal(err)
return
}
if err = db.Exit(opts); err != nil {
log.Fatal(err)
return
}
if err = kvs.Exit(opts); err != nil {
log.Fatal(err)
return
}
return
},
}
func init() {
startCmd.PersistentFlags().StringVarP(&opts.Auth.Auth, "auth", "a", "root:root", "Master database authentication details")
startCmd.PersistentFlags().StringVar(&opts.Auth.User, "auth-user", "", "The master username for the database. Use this as an alternative to the --auth flag")
startCmd.PersistentFlags().StringVar(&opts.Auth.Pass, "auth-pass", "", "The master password for the database. Use this as an alternative to the --auth flag")
startCmd.PersistentFlags().StringSliceVar(&opts.Auth.Addr, "auth-addr", []string{"0.0.0.0/0", "0:0:0:0:0:0:0:0/0"}, "The IP address ranges from which master authentication is possible")
startCmd.PersistentFlags().StringVar(&opts.DB.Path, "path", "", "Database path used for storing data")
startCmd.PersistentFlags().IntVar(&opts.Port, "port", 8000, "The port on which to serve the web server")
startCmd.PersistentFlags().StringVarP(&opts.Bind, "bind", "b", "0.0.0.0", "The hostname or ip address to listen for connections on")
startCmd.PersistentFlags().DurationVar(&opts.Query.Timeout, "timeout", 0, "")
startCmd.PersistentFlags().StringVarP(&opts.DB.Code, "key", "k", "", "Encryption key to use for on-disk encryption")
startCmd.PersistentFlags().DurationVar(&opts.DB.Proc.Flush, "db-flush", 0, "A time duration to use when syncing data to persistent storage")
startCmd.PersistentFlags().DurationVar(&opts.DB.Proc.Shrink, "db-shrink", 0, "A time duration to use when shrinking data on persistent storage")
startCmd.PersistentFlags().StringVar(&opts.DB.Cert.CA, "kvs-ca", "", "Path to the CA file used to connect to the remote database")
startCmd.PersistentFlags().StringVar(&opts.DB.Cert.Crt, "kvs-crt", "", "Path to the certificate file used to connect to the remote database")
startCmd.PersistentFlags().StringVar(&opts.DB.Cert.Key, "kvs-key", "", "Path to the private key file used to connect to the remote database")
startCmd.PersistentFlags().StringVar(&opts.Cert.Crt, "web-crt", "", "Path to the server certificate. Needed when running in secure mode")
startCmd.PersistentFlags().StringVar(&opts.Cert.Key, "web-key", "", "Path to the server private key. Needed when running in secure mode")
}

View file

@ -1,43 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cli
import (
"fmt"
"os"
"text/tabwriter"
"github.com/spf13/cobra"
"github.com/surrealdb/surrealdb/util/build"
)
var versionCmd = &cobra.Command{
Use: "version",
Short: "Output version information",
Run: func(cmd *cobra.Command, args []string) {
info := build.GetInfo()
tw := tabwriter.NewWriter(os.Stdout, 2, 1, 2, ' ', 0)
fmt.Fprintf(tw, "Build Go: %s\n", info.Go)
fmt.Fprintf(tw, "Build Ver: %s\n", info.Ver)
fmt.Fprintf(tw, "Build Rev: %s\n", info.Rev)
fmt.Fprintf(tw, "Build Time: %s\n", info.Time)
tw.Flush()
},
}

View file

@ -1,39 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cnf
type Auth struct {
Data interface{} `json:"id" msgpack:"id"`
Kind Kind `json:"-" msgpack:"-"`
Scope string `json:"-" msgpack:"-"`
NS string `json:"-" msgpack:"-"`
DB string `json:"-" msgpack:"-"`
}
// Reset resets the authentication data.
func (a *Auth) Reset() *Auth {
// Reset the authentication level
a.Kind = AuthNO
// Remove any saved session data
a.Data = nil
// Clear any authenticated scope
a.Scope = ""
return a
}

View file

@ -1,71 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cnf
import (
"net"
"time"
)
var Settings *Options
// Options defines global configuration options
type Options struct {
DB struct {
Key []byte // Data encryption key
Code string // Data encryption key string
Path string // Path to store the data file
Base string // Base key to use in KV stores
Proc struct {
Flush time.Duration // Timeframe for flushing data
Shrink time.Duration // Timeframe for shrinking data
}
Cert struct {
CA string
Crt string
Key string
SSL bool
}
}
Port int // Port as an number
Conn string // Port as a string
Bind string // Hostname of the node
Cert struct {
Crt string // File location of server crt
Key string // File location of server key
}
Auth struct {
Auth string // Master authentication user:pass
User string // Master authentication username
Pass string // Master authentication password
Addr []string // Allowed ip ranges for authentication
Nets []*net.IPNet // Allowed cidr ranges for authentication
}
Query struct {
Timeout time.Duration // Fixed query timeout
}
Logging struct {
Level string // Stores the configured logging level
Output string // Stores the configured logging output
Format string // Stores the configured logging format
}
}

View file

@ -1,53 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cnf
const (
// Root access
AuthKV Kind = iota
// Namespace access
AuthNS
// Database access
AuthDB
// Scoped user access
AuthSC
// No access
AuthNO
)
type Kind int
func (k Kind) String() string {
switch k {
default:
return "NO"
case AuthKV:
return "KV"
case AuthNS:
return "NS"
case AuthDB:
return "DB"
case AuthSC:
return "SC"
}
}
func (k Kind) MarshalText() (data []byte, err error) {
return []byte(k.String()), err
}
func (k Kind) UnmarshalText(text []byte) error {
return nil
}

View file

@ -1,39 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"context"
"github.com/surrealdb/surrealdb/cnf"
)
func (e *executor) access(ctx context.Context, kind cnf.Kind) (err error) {
if perm(ctx) > kind {
return new(QueryError)
}
if kind > cnf.AuthKV && len(e.ns) == 0 {
return new(BlankError)
}
if kind > cnf.AuthNS && len(e.db) == 0 {
return new(BlankError)
}
return
}

View file

@ -1,58 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"github.com/dgraph-io/ristretto"
)
var keyCache *ristretto.Cache
var valCache *ristretto.Cache
func init() {
keyCache, _ = ristretto.NewCache(&ristretto.Config{
NumCounters: 1e7,
MaxCost: 1 << 32,
BufferItems: 64,
Cost: func(i interface{}) int64 {
switch v := i.(type) {
case string:
return int64(len(v))
case []byte:
return int64(len(v))
default:
return 1
}
},
})
valCache, _ = ristretto.NewCache(&ristretto.Config{
NumCounters: 1e7,
MaxCost: 1 << 32,
BufferItems: 64,
Cost: func(i interface{}) int64 {
switch v := i.(type) {
case string:
return int64(len(v))
case []byte:
return int64(len(v))
default:
return 1
}
},
})
}

View file

@ -1,195 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"context"
"github.com/surrealdb/surrealdb/cnf"
"github.com/surrealdb/surrealdb/sql"
)
func (d *document) check(ctx context.Context, cond sql.Expr) (ok bool, err error) {
// If no condition expression has been
// defined then we can ignore this, and
// process the current document.
if cond == nil {
return true, nil
}
// If a condition expression has been
// defined then let's process it to see
// what value it returns or error.
val, err := d.i.e.fetch(ctx, cond, d.current)
// If the condition expression result is
// not a boolean value, then let's see
// if the value can be equated to a bool.
return calcAsBool(val), err
}
// Grant checks to see if the table permissions allow
// this record to be accessed for live queries, and
// if not then it errors accordingly.
func (d *document) grant(ctx context.Context, met method) (ok bool, err error) {
var val interface{}
// If this is a document loaded from
// a subquery or data param, and not
// from the KV store, then there is
// no need to check permissions.
if d.key == nil {
return true, nil
}
// If we are authenticated using DB, NS,
// or KV permissions level, then we can
// ignore all permissions checks, but we
// must ensure the TB, DB, and NS exist.
if perm(ctx) < cnf.AuthSC {
return true, nil
}
// Otherwise, get the table definition
// so we can check if the permissions
// allow us to view this document.
tb, err := d.i.e.tx.GetTB(ctx, d.key.NS, d.key.DB, d.key.TB)
if err != nil {
return false, err
}
// Once we have the table we reset the
// context to DB level so that no other
// embedded permissions are checked on
// records within these permissions.
ctx = context.WithValue(ctx, ctxKeyKind, cnf.AuthDB)
// We then try to process the relevant
// permissions dependent on the query
// that we are currently processing. If
// there are no permissions specified
// for this table, then because this is
// a scoped request, return an error.
if p, ok := tb.Perms.(*sql.PermExpression); ok {
switch met {
case _SELECT:
val, err = d.i.e.fetch(ctx, p.Select, d.current)
case _CREATE:
val, err = d.i.e.fetch(ctx, p.Select, d.current)
case _UPDATE:
val, err = d.i.e.fetch(ctx, p.Select, d.current)
case _DELETE:
val, err = d.i.e.fetch(ctx, p.Select, d.initial)
}
}
// If the permissions expressions
// returns a boolean value, then we
// return this, dictating whether the
// document is able to be viewed.
return calcAsBool(val), err
}
// Query checks to see if the table permissions allow
// this record to be accessed for normal queries, and
// if not then it errors accordingly.
func (d *document) allow(ctx context.Context, met method) (ok bool, err error) {
var val interface{}
// If this is a document loaded from
// a subquery or data param, and not
// from the KV store, then there is
// no need to check permissions.
if d.key == nil {
return true, nil
}
// If we are authenticated using DB, NS,
// or KV permissions level, then we can
// ignore all permissions checks, but we
// must ensure the TB, DB, and NS exist.
if perm(ctx) < cnf.AuthSC {
return true, nil
}
// If this document is being created
// for the first time, then allow this
// check, and recheck after the fields
// have been merged into the document.
if met == _CREATE && !d.current.Exists("id") {
return true, nil
}
// Otherwise, get the table definition
// so we can check if the permissions
// allow us to view this document.
tb, err := d.i.e.tx.GetTB(ctx, d.key.NS, d.key.DB, d.key.TB)
if err != nil {
return false, err
}
// Once we have the table we reset the
// context to DB level so that no other
// embedded permissions are checked on
// records within these permissions.
ctx = context.WithValue(ctx, ctxKeyKind, cnf.AuthDB)
// We then try to process the relevant
// permissions dependent on the query
// that we are currently processing. If
// there are no permissions specified
// for this table, then because this is
// a scoped request, return an error.
if p, ok := tb.Perms.(*sql.PermExpression); ok {
switch met {
case _SELECT:
val, err = d.i.e.fetch(ctx, p.Select, d.current)
case _CREATE:
val, err = d.i.e.fetch(ctx, p.Create, d.current)
case _UPDATE:
val, err = d.i.e.fetch(ctx, p.Update, d.current)
case _DELETE:
val, err = d.i.e.fetch(ctx, p.Delete, d.current)
}
}
// If the permissions expressions
// returns a boolean value, then we
// return this, dictating whether the
// document is able to be viewed.
return calcAsBool(val), err
}

View file

@ -1,63 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"context"
"github.com/surrealdb/surrealdb/cnf"
)
func vers(ctx context.Context) uint32 {
v := ctx.Value(ctxKeyDive)
switch v {
case nil:
return 0
default:
return v.(uint32)
}
}
func perm(ctx context.Context) cnf.Kind {
v := ctx.Value(ctxKeyKind)
switch v {
case nil:
return cnf.AuthNO
default:
return v.(cnf.Kind)
}
}
func dive(ctx context.Context) context.Context {
v := ctx.Value(ctxKeyDive)
switch v {
case nil:
return context.WithValue(ctx, ctxKeyDive, uint32(1))
default:
if v.(uint32) > maxRecursiveQueries {
panic(errRecursiveOverload)
}
return context.WithValue(ctx, ctxKeyDive, v.(uint32)+1)
}
}

View file

@ -1,178 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"fmt"
"context"
"github.com/surrealdb/surrealdb/cnf"
"github.com/surrealdb/surrealdb/sql"
"github.com/surrealdb/surrealdb/util/data"
"github.com/surrealdb/surrealdb/util/guid"
"github.com/surrealdb/surrealdb/util/keys"
)
func (e *executor) executeCreate(ctx context.Context, stm *sql.CreateStatement) ([]interface{}, error) {
if err := e.access(ctx, cnf.AuthNO); err != nil {
return nil, err
}
var what sql.Exprs
for _, val := range stm.What {
w, err := e.fetch(ctx, val, nil)
if err != nil {
return nil, err
}
what = append(what, w)
}
i := newIterator(e, ctx, stm, false)
for _, w := range what {
switch what := w.(type) {
default:
return nil, fmt.Errorf("Can not execute CREATE query using value '%v'", what)
case *sql.Table:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db, TB: what.TB, ID: guid.New().String()}
i.processThing(ctx, key)
case *sql.Ident:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db, TB: what.VA, ID: guid.New().String()}
i.processThing(ctx, key)
case *sql.Thing:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db, TB: what.TB, ID: what.ID}
i.processThing(ctx, key)
case *sql.Model:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db, TB: what.TB, ID: nil}
i.processModel(ctx, key, what)
case *sql.Batch:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db, TB: what.TB, ID: nil}
i.processBatch(ctx, key, what)
// Result of subquery
case []interface{}:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db}
i.processOther(ctx, key, what)
// Result of subquery with LIMIT 1
case map[string]interface{}:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db}
i.processOther(ctx, key, []interface{}{what})
}
}
return i.Yield(ctx)
}
func (e *executor) fetchCreate(ctx context.Context, stm *sql.CreateStatement, doc *data.Doc) (interface{}, error) {
ctx = dive(ctx)
if doc != nil {
vars := data.New()
vars.Set(doc.Data(), varKeyParent)
if subs := ctx.Value(ctxKeySubs); subs != nil {
if subs, ok := subs.(*data.Doc); ok {
vars.Set(subs.Get(varKeyParents).Data(), varKeyParents)
}
} else {
vars.Array(varKeyParents)
}
vars.Append(doc.Data(), varKeyParents)
ctx = context.WithValue(ctx, ctxKeySubs, vars)
}
out, err := e.executeCreate(ctx, stm)
if err != nil {
return nil, err
}
switch len(out) {
case 1:
return data.Consume(out).Get(docKeyOne, docKeyId).Data(), nil
default:
return data.Consume(out).Get(docKeyAll, docKeyId).Data(), nil
}
}
func (d *document) runCreate(ctx context.Context, stm *sql.CreateStatement) (interface{}, error) {
var ok bool
var err error
var met = _CREATE
if err = d.init(ctx); err != nil {
return nil, err
}
if err = d.lock(ctx); err != nil {
return nil, err
}
if err = d.setup(ctx); err != nil {
return nil, err
}
if d.val.Exi() == true {
return nil, &ExistError{exist: d.id}
}
if err = d.merge(ctx, met, stm.Data); err != nil {
return nil, err
}
if ok, err = d.allow(ctx, met); err != nil {
return nil, err
} else if ok == false {
return nil, nil
}
if err = d.storeIndex(ctx); err != nil {
return nil, err
}
if err = d.storeThing(ctx); err != nil {
return nil, err
}
if err = d.table(ctx, met); err != nil {
return nil, err
}
if err = d.lives(ctx, met); err != nil {
return nil, err
}
if err = d.event(ctx, met); err != nil {
return nil, err
}
return d.yield(ctx, stm, stm.Echo)
}

View file

@ -1,334 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"testing"
"github.com/surrealdb/surrealdb/util/data"
. "github.com/smartystreets/goconvey/convey"
)
func TestCreate(t *testing.T) {
Convey("Create with invalid value", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE 1;
CREATE "one";
CREATE ["many"];
CREATE [{value:"one"}];
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 5)
So(res[1].Status, ShouldEqual, "ERR")
So(res[1].Detail, ShouldEqual, "Can not execute CREATE query using value '1'")
So(res[2].Status, ShouldEqual, "ERR")
So(res[2].Detail, ShouldEqual, "Can not execute CREATE query using value 'one'")
So(res[3].Status, ShouldEqual, "ERR")
So(res[3].Detail, ShouldEqual, "Can not execute CREATE query using value '[many]'")
So(res[4].Status, ShouldEqual, "ERR")
So(res[4].Detail, ShouldEqual, "Can not execute CREATE query using value '[map[value:one]]'")
})
Convey("Create record when it exists", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE person:test;
CREATE person:test;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 3)
So(res[1].Result, ShouldHaveLength, 1)
So(res[2].Result, ShouldHaveLength, 0)
So(res[2].Status, ShouldEqual, "ERR_EX")
So(res[2].Detail, ShouldEqual, "Database record 'person:test' already exists")
})
Convey("Create unique record using `table`", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Result, ShouldHaveLength, 1)
So(data.Consume(res[1].Result[0]).Get("meta.id").Data(), ShouldHaveLength, 20)
So(data.Consume(res[1].Result[0]).Get("meta.tb").Data(), ShouldEqual, "person")
})
Convey("Create specific record using `thing`", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE person:test;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Result, ShouldHaveLength, 1)
So(data.Consume(res[1].Result[0]).Get("meta.id").Data(), ShouldHaveLength, 4)
So(data.Consume(res[1].Result[0]).Get("meta.tb").Data(), ShouldEqual, "person")
})
Convey("Create unique records using `batch`", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE batch("person", ["1", "2", "person:3"]);
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Result, ShouldHaveLength, 3)
So(data.Consume(res[1].Result[0]).Get("meta.id").Data(), ShouldEqual, 1)
So(data.Consume(res[1].Result[2]).Get("meta.id").Data(), ShouldEqual, 3)
})
Convey("Create unique records using `model`", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE |person:100|;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Result, ShouldHaveLength, 100)
So(data.Consume(res[1].Result[0]).Get("meta.id").Data(), ShouldHaveLength, 20)
So(data.Consume(res[1].Result[99]).Get("meta.id").Data(), ShouldHaveLength, 20)
})
Convey("Create sequential ascending records using `model`", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE |person:1..100|;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Result, ShouldHaveLength, 100)
So(data.Consume(res[1].Result[0]).Get("meta.id").Data(), ShouldEqual, 1)
So(data.Consume(res[1].Result[99]).Get("meta.id").Data(), ShouldEqual, 100)
})
Convey("Create sequential descending records using `model`", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE |person:100..1|;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Result, ShouldHaveLength, 100)
So(data.Consume(res[1].Result[0]).Get("meta.id").Data(), ShouldEqual, 100)
So(data.Consume(res[1].Result[99]).Get("meta.id").Data(), ShouldEqual, 1)
})
Convey("Create sequential ascending negative-to-positive records using `model`", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE |person:-50..50|;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Result, ShouldHaveLength, 101)
So(data.Consume(res[1].Result[0]).Get("meta.id").Data(), ShouldEqual, -50)
So(data.Consume(res[1].Result[1]).Get("meta.id").Data(), ShouldEqual, -49)
So(data.Consume(res[1].Result[100]).Get("meta.id").Data(), ShouldEqual, 50)
})
Convey("Create sequential ascending decimal records using `model`", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE |person:1,0.5..50|;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Result, ShouldHaveLength, 99)
So(data.Consume(res[1].Result[0]).Get("meta.id").Data(), ShouldEqual, 1)
So(data.Consume(res[1].Result[1]).Get("meta.id").Data(), ShouldEqual, 1.5)
So(data.Consume(res[1].Result[98]).Get("meta.id").Data(), ShouldEqual, 50)
})
Convey("Create sequential descending decimal records using `model`", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE |person:50,0.5..1|;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Result, ShouldHaveLength, 99)
So(data.Consume(res[1].Result[0]).Get("meta.id").Data(), ShouldEqual, 50)
So(data.Consume(res[1].Result[1]).Get("meta.id").Data(), ShouldEqual, 49.5)
So(data.Consume(res[1].Result[98]).Get("meta.id").Data(), ShouldEqual, 1)
})
Convey("Create sequential ascending decimal negative-to-positive records using `model`", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE |person:-50,0.5..50|;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Result, ShouldHaveLength, 201)
So(data.Consume(res[1].Result[0]).Get("meta.id").Data(), ShouldEqual, -50)
So(data.Consume(res[1].Result[1]).Get("meta.id").Data(), ShouldEqual, -49.5)
So(data.Consume(res[1].Result[200]).Get("meta.id").Data(), ShouldEqual, 50)
})
Convey("Parsing same ID using ints, floats, and strings", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE person:1;
CREATE person:1.0;
CREATE person:1.0000;
CREATE person:1;
CREATE person:1.0;
CREATE person:1.0000;
SELECT id FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 8)
So(res[1].Result, ShouldHaveLength, 1)
So(res[2].Status, ShouldEqual, "ERR_EX")
So(res[2].Detail, ShouldEqual, "Database record 'person:1' already exists")
So(res[3].Status, ShouldEqual, "ERR_EX")
So(res[3].Detail, ShouldEqual, "Database record 'person:1' already exists")
So(res[4].Status, ShouldEqual, "ERR_EX")
So(res[4].Detail, ShouldEqual, "Database record 'person:1' already exists")
So(res[5].Status, ShouldEqual, "ERR_EX")
So(res[5].Detail, ShouldEqual, "Database record 'person:1' already exists")
So(res[6].Status, ShouldEqual, "ERR_EX")
So(res[6].Detail, ShouldEqual, "Database record 'person:1' already exists")
So(res[7].Result, ShouldHaveLength, 1)
})
Convey("Creating with a timeout of 1ms returns an error", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE |person:1..1000| TIMEOUT 1ms;
SELECT * FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 3)
So(res[1].Result, ShouldHaveLength, 0)
So(res[2].Result, ShouldHaveLength, 0)
So(res[1].Status, ShouldEqual, "ERR_TO")
So(res[1].Detail, ShouldEqual, "Query timeout of 1ms exceeded")
})
Convey("Creating a record with unbounded map or array keys succeeds", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE |test:1000|;
CREATE person:test SET tests=(SELECT id FROM test);
SELECT * FROM person:test;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[1].Status, ShouldEqual, "OK")
So(res[1].Result, ShouldHaveLength, 1000)
So(res[2].Status, ShouldEqual, "OK")
So(res[2].Result, ShouldHaveLength, 1)
So(res[3].Result, ShouldHaveLength, 1)
So(data.Consume(res[3].Result[0]).Get("meta.id").Data(), ShouldEqual, "test")
So(data.Consume(res[3].Result[0]).Get("meta.tb").Data(), ShouldEqual, "person")
So(data.Consume(res[3].Result[0]).Get("tests").Data(), ShouldHaveLength, 1000)
})
}

View file

@ -1,493 +0,0 @@
//go:build go1.6
// +build go1.6
// Code generated by codecgen - DO NOT EDIT.
package db
import (
"errors"
codec1978 "github.com/ugorji/go/codec"
"runtime"
"strconv"
)
const (
// ----- content types ----
codecSelferCcUTF87585 = 1
codecSelferCcRAW7585 = 255
// ----- value types used ----
codecSelferValueTypeArray7585 = 10
codecSelferValueTypeMap7585 = 9
codecSelferValueTypeString7585 = 6
codecSelferValueTypeInt7585 = 2
codecSelferValueTypeUint7585 = 3
codecSelferValueTypeFloat7585 = 4
codecSelferValueTypeNil7585 = 1
codecSelferBitsize7585 = uint8(32 << (^uint(0) >> 63))
codecSelferDecContainerLenNil7585 = -2147483648
)
var (
errCodecSelferOnlyMapOrArrayEncodeToStruct7585 = errors.New(`only encoded map or array can be decoded into a struct`)
)
type codecSelfer7585 struct{}
func codecSelfer7585False() bool { return false }
func codecSelfer7585True() bool { return true }
func init() {
if codec1978.GenVersion != 25 {
_, file, _, _ := runtime.Caller(0)
ver := strconv.FormatInt(int64(codec1978.GenVersion), 10)
panic(errors.New("codecgen version mismatch: current: 25, need " + ver + ". Re-generate file: " + file))
}
}
func (Response) codecSelferViaCodecgen() {}
func (x *Response) CodecEncodeSelf(e *codec1978.Encoder) {
var h codecSelfer7585
z, r := codec1978.GenHelper().Encoder(e)
_, _, _ = h, z, r
if z.EncBasicHandle().CheckCircularRef {
z.EncEncode(x)
return
}
if x == nil {
r.EncodeNil()
} else {
yy2arr2 := z.EncBasicHandle().StructToArray
_ = yy2arr2
const yyr2 bool = false // struct tag has 'toArray'
var yyq2 = [4]bool{ // should field at this index be written?
x.Time != "", // time
x.Status != "", // status
x.Detail != "", // detail
len(x.Result) != 0, // result
}
_ = yyq2
if yyr2 || yy2arr2 {
z.EncWriteArrayStart(4)
z.EncWriteArrayElem()
if yyq2[0] {
r.EncodeString(string(x.Time))
} else {
r.EncodeString("")
}
z.EncWriteArrayElem()
if yyq2[1] {
r.EncodeString(string(x.Status))
} else {
r.EncodeString("")
}
z.EncWriteArrayElem()
if yyq2[2] {
r.EncodeString(string(x.Detail))
} else {
r.EncodeString("")
}
z.EncWriteArrayElem()
if yyq2[3] {
if x.Result == nil {
r.EncodeNil()
} else {
z.F.EncSliceIntfV(x.Result, e)
} // end block: if x.Result slice == nil
} else {
r.EncodeNil()
}
z.EncWriteArrayEnd()
} else {
var yynn2 int
for _, b := range yyq2 {
if b {
yynn2++
}
}
z.EncWriteMapStart(yynn2)
yynn2 = 0
if yyq2[0] {
z.EncWriteMapElemKey()
if z.IsJSONHandle() {
z.WriteStr("\"time\"")
} else {
r.EncodeString(`time`)
}
z.EncWriteMapElemValue()
r.EncodeString(string(x.Time))
}
if yyq2[1] {
z.EncWriteMapElemKey()
if z.IsJSONHandle() {
z.WriteStr("\"status\"")
} else {
r.EncodeString(`status`)
}
z.EncWriteMapElemValue()
r.EncodeString(string(x.Status))
}
if yyq2[2] {
z.EncWriteMapElemKey()
if z.IsJSONHandle() {
z.WriteStr("\"detail\"")
} else {
r.EncodeString(`detail`)
}
z.EncWriteMapElemValue()
r.EncodeString(string(x.Detail))
}
if yyq2[3] {
z.EncWriteMapElemKey()
if z.IsJSONHandle() {
z.WriteStr("\"result\"")
} else {
r.EncodeString(`result`)
}
z.EncWriteMapElemValue()
if x.Result == nil {
r.EncodeNil()
} else {
z.F.EncSliceIntfV(x.Result, e)
} // end block: if x.Result slice == nil
}
z.EncWriteMapEnd()
}
}
}
func (x *Response) CodecDecodeSelf(d *codec1978.Decoder) {
var h codecSelfer7585
z, r := codec1978.GenHelper().Decoder(d)
_, _, _ = h, z, r
yyct2 := r.ContainerType()
if yyct2 == codecSelferValueTypeNil7585 {
*(x) = Response{}
} else if yyct2 == codecSelferValueTypeMap7585 {
yyl2 := z.DecReadMapStart()
if yyl2 == 0 {
} else {
x.codecDecodeSelfFromMap(yyl2, d)
}
z.DecReadMapEnd()
} else if yyct2 == codecSelferValueTypeArray7585 {
yyl2 := z.DecReadArrayStart()
if yyl2 != 0 {
x.codecDecodeSelfFromArray(yyl2, d)
}
z.DecReadArrayEnd()
} else {
panic(errCodecSelferOnlyMapOrArrayEncodeToStruct7585)
}
}
func (x *Response) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) {
var h codecSelfer7585
z, r := codec1978.GenHelper().Decoder(d)
_, _, _ = h, z, r
var yyhl3 bool = l >= 0
for yyj3 := 0; ; yyj3++ {
if yyhl3 {
if yyj3 >= l {
break
}
} else {
if z.DecCheckBreak() {
break
}
}
z.DecReadMapElemKey()
yys3 := r.DecodeStringAsBytes()
z.DecReadMapElemValue()
switch string(yys3) {
case "time":
x.Time = (string)(z.DecStringZC(r.DecodeStringAsBytes()))
case "status":
x.Status = (string)(z.DecStringZC(r.DecodeStringAsBytes()))
case "detail":
x.Detail = (string)(z.DecStringZC(r.DecodeStringAsBytes()))
case "result":
z.F.DecSliceIntfX(&x.Result, d)
default:
z.DecStructFieldNotFound(-1, string(yys3))
} // end switch yys3
} // end for yyj3
}
func (x *Response) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
var h codecSelfer7585
z, r := codec1978.GenHelper().Decoder(d)
_, _, _ = h, z, r
var yyj9 int
var yyb9 bool
var yyhl9 bool = l >= 0
yyj9++
if yyhl9 {
yyb9 = yyj9 > l
} else {
yyb9 = z.DecCheckBreak()
}
if yyb9 {
z.DecReadArrayEnd()
return
}
z.DecReadArrayElem()
x.Time = (string)(z.DecStringZC(r.DecodeStringAsBytes()))
yyj9++
if yyhl9 {
yyb9 = yyj9 > l
} else {
yyb9 = z.DecCheckBreak()
}
if yyb9 {
z.DecReadArrayEnd()
return
}
z.DecReadArrayElem()
x.Status = (string)(z.DecStringZC(r.DecodeStringAsBytes()))
yyj9++
if yyhl9 {
yyb9 = yyj9 > l
} else {
yyb9 = z.DecCheckBreak()
}
if yyb9 {
z.DecReadArrayEnd()
return
}
z.DecReadArrayElem()
x.Detail = (string)(z.DecStringZC(r.DecodeStringAsBytes()))
yyj9++
if yyhl9 {
yyb9 = yyj9 > l
} else {
yyb9 = z.DecCheckBreak()
}
if yyb9 {
z.DecReadArrayEnd()
return
}
z.DecReadArrayElem()
z.F.DecSliceIntfX(&x.Result, d)
for {
yyj9++
if yyhl9 {
yyb9 = yyj9 > l
} else {
yyb9 = z.DecCheckBreak()
}
if yyb9 {
break
}
z.DecReadArrayElem()
z.DecStructFieldNotFound(yyj9-1, "")
}
}
func (x *Response) IsCodecEmpty() bool {
return !(x.Time != "" || x.Status != "" || x.Detail != "" || len(x.Result) != 0 || false)
}
func (Dispatch) codecSelferViaCodecgen() {}
func (x *Dispatch) CodecEncodeSelf(e *codec1978.Encoder) {
var h codecSelfer7585
z, r := codec1978.GenHelper().Encoder(e)
_, _, _ = h, z, r
if z.EncBasicHandle().CheckCircularRef {
z.EncEncode(x)
return
}
if x == nil {
r.EncodeNil()
} else {
yy2arr2 := z.EncBasicHandle().StructToArray
_ = yy2arr2
const yyr2 bool = false // struct tag has 'toArray'
var yyq2 = [3]bool{ // should field at this index be written?
x.Query != "", // query
x.Action != "", // action
x.Result != nil, // result
}
_ = yyq2
if yyr2 || yy2arr2 {
z.EncWriteArrayStart(3)
z.EncWriteArrayElem()
if yyq2[0] {
r.EncodeString(string(x.Query))
} else {
r.EncodeString("")
}
z.EncWriteArrayElem()
if yyq2[1] {
r.EncodeString(string(x.Action))
} else {
r.EncodeString("")
}
z.EncWriteArrayElem()
if yyq2[2] {
z.EncFallback(x.Result)
} else {
r.EncodeNil()
}
z.EncWriteArrayEnd()
} else {
var yynn2 int
for _, b := range yyq2 {
if b {
yynn2++
}
}
z.EncWriteMapStart(yynn2)
yynn2 = 0
if yyq2[0] {
z.EncWriteMapElemKey()
if z.IsJSONHandle() {
z.WriteStr("\"query\"")
} else {
r.EncodeString(`query`)
}
z.EncWriteMapElemValue()
r.EncodeString(string(x.Query))
}
if yyq2[1] {
z.EncWriteMapElemKey()
if z.IsJSONHandle() {
z.WriteStr("\"action\"")
} else {
r.EncodeString(`action`)
}
z.EncWriteMapElemValue()
r.EncodeString(string(x.Action))
}
if yyq2[2] {
z.EncWriteMapElemKey()
if z.IsJSONHandle() {
z.WriteStr("\"result\"")
} else {
r.EncodeString(`result`)
}
z.EncWriteMapElemValue()
z.EncFallback(x.Result)
}
z.EncWriteMapEnd()
}
}
}
func (x *Dispatch) CodecDecodeSelf(d *codec1978.Decoder) {
var h codecSelfer7585
z, r := codec1978.GenHelper().Decoder(d)
_, _, _ = h, z, r
yyct2 := r.ContainerType()
if yyct2 == codecSelferValueTypeNil7585 {
*(x) = Dispatch{}
} else if yyct2 == codecSelferValueTypeMap7585 {
yyl2 := z.DecReadMapStart()
if yyl2 == 0 {
} else {
x.codecDecodeSelfFromMap(yyl2, d)
}
z.DecReadMapEnd()
} else if yyct2 == codecSelferValueTypeArray7585 {
yyl2 := z.DecReadArrayStart()
if yyl2 != 0 {
x.codecDecodeSelfFromArray(yyl2, d)
}
z.DecReadArrayEnd()
} else {
panic(errCodecSelferOnlyMapOrArrayEncodeToStruct7585)
}
}
func (x *Dispatch) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) {
var h codecSelfer7585
z, r := codec1978.GenHelper().Decoder(d)
_, _, _ = h, z, r
var yyhl3 bool = l >= 0
for yyj3 := 0; ; yyj3++ {
if yyhl3 {
if yyj3 >= l {
break
}
} else {
if z.DecCheckBreak() {
break
}
}
z.DecReadMapElemKey()
yys3 := r.DecodeStringAsBytes()
z.DecReadMapElemValue()
switch string(yys3) {
case "query":
x.Query = (string)(z.DecStringZC(r.DecodeStringAsBytes()))
case "action":
x.Action = (string)(z.DecStringZC(r.DecodeStringAsBytes()))
case "result":
z.DecFallback(&x.Result, true)
default:
z.DecStructFieldNotFound(-1, string(yys3))
} // end switch yys3
} // end for yyj3
}
func (x *Dispatch) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
var h codecSelfer7585
z, r := codec1978.GenHelper().Decoder(d)
_, _, _ = h, z, r
var yyj8 int
var yyb8 bool
var yyhl8 bool = l >= 0
yyj8++
if yyhl8 {
yyb8 = yyj8 > l
} else {
yyb8 = z.DecCheckBreak()
}
if yyb8 {
z.DecReadArrayEnd()
return
}
z.DecReadArrayElem()
x.Query = (string)(z.DecStringZC(r.DecodeStringAsBytes()))
yyj8++
if yyhl8 {
yyb8 = yyj8 > l
} else {
yyb8 = z.DecCheckBreak()
}
if yyb8 {
z.DecReadArrayEnd()
return
}
z.DecReadArrayElem()
x.Action = (string)(z.DecStringZC(r.DecodeStringAsBytes()))
yyj8++
if yyhl8 {
yyb8 = yyj8 > l
} else {
yyb8 = z.DecCheckBreak()
}
if yyb8 {
z.DecReadArrayEnd()
return
}
z.DecReadArrayElem()
z.DecFallback(&x.Result, true)
for {
yyj8++
if yyhl8 {
yyb8 = yyj8 > l
} else {
yyb8 = z.DecCheckBreak()
}
if yyb8 {
break
}
z.DecReadArrayElem()
z.DecStructFieldNotFound(yyj8-1, "")
}
}
func (x *Dispatch) IsCodecEmpty() bool {
return !(x.Query != "" || x.Action != "" || x.Result != nil || false)
}

273
db/db.go
View file

@ -1,273 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"io"
"os"
"context"
"net/http"
"github.com/surrealdb/fibre"
"github.com/surrealdb/surrealdb/cnf"
"github.com/surrealdb/surrealdb/kvs"
"github.com/surrealdb/surrealdb/sql"
"github.com/surrealdb/surrealdb/util/data"
_ "github.com/surrealdb/surrealdb/kvs/rixxdb"
)
var KV string
var NIL string
var ENV string
// Response is a response from the database
type Response struct {
Time string `codec:"time,omitempty"`
Status string `codec:"status,omitempty"`
Detail string `codec:"detail,omitempty"`
Result []interface{} `codec:"result,omitempty"`
}
// Dispatch is a dispatch from the database
type Dispatch struct {
Query string `codec:"query,omitempty"`
Action string `codec:"action,omitempty"`
Result interface{} `codec:"result,omitempty"`
}
func init() {
ENV = os.Getenv(varKeyEnv)
}
// Setup sets up the connection with the data layer
func Setup(opts *cnf.Options) (err error) {
KV = cnf.Settings.DB.Base
err = tidy()
return
}
// Exit shuts down the connection with the data layer
func Exit(opts *cnf.Options) (err error) {
sockets.Range(func(key, val interface{}) bool {
id, so := key.(string), val.(*socket)
deregister(so.fibre, id)()
return true
})
return
}
// Import loads database operations from a reader.
// This can be used to playback a database snapshot
// into an already running database.
func Sync(rw interface{}) (err error) {
switch v := rw.(type) {
case io.Reader:
return kvs.Import(v)
case io.Writer:
return kvs.Export(v)
default:
return nil
}
}
// Export saves all database operations to a writer.
// This can be used to save a database snapshot
// to a secondary file or stream.
func Export(fib *fibre.Context, ns, db string) (err error) {
return export(fib, ns, db)
}
// Socket registers a websocket for live queries
// returning two callback functions. The first
// function should be called when the websocket
// connects, and the second function should be
// called when the websocket disconnects.
func Socket(fib *fibre.Context, id string) (beg, end func()) {
return register(fib, id), deregister(fib, id)
}
// Execute parses a single sql query, or multiple
// sql queries, and executes them serially against
// the underlying data layer.
func Execute(fib *fibre.Context, txt interface{}, vars map[string]interface{}) (out []*Response, err error) {
// Parse the received SQL batch query strings
// into SQL ASTs, using any immutable preset
// variables if set.
ast, err := sql.Parse(txt)
if err != nil {
return
}
// Process the parsed SQL batch query using
// the predefined query variables.
return Process(fib, ast, vars)
}
// Process takes a parsed set of sql queries and
// executes them serially against the underlying
// data layer.
func Process(fib *fibre.Context, ast *sql.Query, vars map[string]interface{}) (out []*Response, err error) {
// If no preset variables have been defined
// then ensure that the variables is
// instantiated for future use.
if vars == nil {
vars = make(map[string]interface{})
}
// Fetch any variables which have been set
// on the connection. These might have been
// set on the WebSocket or using HTTP Headers.
if sess := fib.Get(ctxKeyVars); sess != nil {
for key, val := range sess.(map[string]interface{}) {
vars[key] = val
}
}
// Get the unique id for this connection
// so that we can assign it to the context
// and detect any websocket notifications.
id := fib.Uniq()
// Assign the authentication data to the
// context so that we can log the auth kind
// and the auth variable data to the request.
auth := fib.Get(ctxKeyAuth).(*cnf.Auth)
// Ensure that the specified environment
// variable 'ENV' is available to the
// request, to detect the environment.
vars[varKeyEnv] = ENV
// Ensure that the current authentication
// data is made available as a runtime
// variable to the query layer.
vars[varKeyAuth] = auth.Data
// Ensure that the current authentication
// scope is made available as a runtime
// variable to the query layer.
vars[varKeyScope] = auth.Scope
// Ensure that the session details, such
// as id, ip, and origin, are available on
// the 'conn' object on each query.
vars[varKeySession] = session(fib)
// Create a new context so that we can quit
// all goroutine workers if the http client
// itself is closed before finishing.
ctx, quit := context.WithCancel(fib.Context())
// When this function has finished ensure
// that we cancel this context so that any
// associated resources are released.
defer quit()
// Assign the Fibre http request data to the
// context so that we can trace all subsequent
// queries to alongside the http request.
ctx = context.WithValue(ctx, ctxKeyFibre, fib)
// Assign the authentication data to the
// context so that we can log the auth kind
// and the auth variable data to the request.
ctx = context.WithValue(ctx, ctxKeyKind, auth.Kind)
// Add the request variables to the context
// so that we can access them at a later
// stage within the nested callbacks.
ctx = context.WithValue(ctx, ctxKeyVars, data.Consume(vars))
// If the current connection is a normal http
// connection then force quit any running
// queries if the http socket closes.
if _, ok := fib.Response().Writer().(http.CloseNotifier); ok {
exit := fib.Response().CloseNotify()
done := make(chan struct{})
defer close(done)
go func() {
select {
case <-done:
case <-exit:
quit()
}
}()
}
// Create a new query executor with the query
// details, and the current runtime variables
// and execute the queries within.
executor := newExecutor(id, auth.NS, auth.DB)
// Execute the parsed SQL syntax tree in a
// separate goroutine so that we can send
// the output in chunks to the client.
go executor.execute(ctx, ast)
// Send all flushed asynchronous messages
// to the websocket connections which are
// listening to the queries.
defer send(id)
// Wait for all of the processed queries to
// return results, buffer the output, and
// return the output when finished.
for {
select {
case <-ctx.Done():
return nil, fibre.NewHTTPError(504)
case res, open := <-executor.send:
if !open {
return
}
out = append(out, res)
}
}
return
}

View file

@ -1,73 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"net/http/httptest"
"github.com/surrealdb/fibre"
"github.com/surrealdb/surrealdb/cnf"
"github.com/surrealdb/surrealdb/kvs"
"github.com/surrealdb/surrealdb/util/uuid"
)
var req *fibre.Request
var res *fibre.Response
func init() {
req = &fibre.Request{Request: httptest.NewRequest("GET", "/", nil)}
res = &fibre.Response{}
}
func setupDB(workers int) {
cnf.Settings = &cnf.Options{}
cnf.Settings.DB.Path = "memory"
cnf.Settings.DB.Base = "surreal"
workerCount = workers
kvs.Setup(cnf.Settings)
Setup(cnf.Settings)
}
func permsKV() (ctx *fibre.Context) {
ctx = fibre.NewContext(req, res, nil)
ctx.Set("id", uuid.New().String())
ctx.Set("auth", &cnf.Auth{
Kind: cnf.AuthKV,
NS: "test",
DB: "test",
})
return ctx
}
func permsSC() (ctx *fibre.Context) {
ctx = fibre.NewContext(req, res, nil)
ctx.Set("id", uuid.New().String())
ctx.Set("auth", &cnf.Auth{
Kind: cnf.AuthSC,
NS: "test",
DB: "test",
})
return ctx
}

View file

@ -1,291 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance wdbh the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"context"
"golang.org/x/crypto/bcrypt"
"github.com/surrealdb/surrealdb/cnf"
"github.com/surrealdb/surrealdb/sql"
"github.com/surrealdb/surrealdb/util/keys"
"github.com/surrealdb/surrealdb/util/rand"
)
func (e *executor) executeDefineNamespace(ctx context.Context, ast *sql.DefineNamespaceStatement) (out []interface{}, err error) {
if err := e.access(ctx, cnf.AuthKV); err != nil {
return nil, err
}
// Save the namespace definition
nkey := &keys.NS{KV: KV, NS: ast.Name.VA}
_, err = e.tx.Put(ctx, 0, nkey.Encode(), ast.Encode())
return
}
func (e *executor) executeDefineDatabase(ctx context.Context, ast *sql.DefineDatabaseStatement) (out []interface{}, err error) {
if err := e.access(ctx, cnf.AuthNS); err != nil {
return nil, err
}
e.tx.AddNS(ctx, e.ns)
// Save the database definition
dkey := &keys.DB{KV: KV, NS: e.ns, DB: ast.Name.VA}
_, err = e.tx.Put(ctx, 0, dkey.Encode(), ast.Encode())
return
}
func (e *executor) executeDefineLogin(ctx context.Context, ast *sql.DefineLoginStatement) (out []interface{}, err error) {
ast.Code = rand.New(128)
switch len(ast.Hash) {
default:
ast.Pass = ast.Hash
case 0:
ast.Pass, _ = bcrypt.GenerateFromPassword(ast.Pass, bcrypt.DefaultCost)
}
switch ast.Kind {
case sql.NAMESPACE:
if err := e.access(ctx, cnf.AuthNS); err != nil {
return nil, err
}
e.tx.AddNS(ctx, e.ns)
// Save the login definition
ukey := &keys.NU{KV: KV, NS: e.ns, US: ast.User.VA}
_, err = e.tx.Put(ctx, 0, ukey.Encode(), ast.Encode())
case sql.DATABASE:
if err := e.access(ctx, cnf.AuthDB); err != nil {
return nil, err
}
e.tx.AddDB(ctx, e.ns, e.db)
// Save the login definition
ukey := &keys.DU{KV: KV, NS: e.ns, DB: e.db, US: ast.User.VA}
_, err = e.tx.Put(ctx, 0, ukey.Encode(), ast.Encode())
}
return
}
func (e *executor) executeDefineToken(ctx context.Context, ast *sql.DefineTokenStatement) (out []interface{}, err error) {
switch ast.Kind {
case sql.NAMESPACE:
if err := e.access(ctx, cnf.AuthNS); err != nil {
return nil, err
}
e.tx.AddNS(ctx, e.ns)
// Save the token definition
tkey := &keys.NT{KV: KV, NS: e.ns, TK: ast.Name.VA}
_, err = e.tx.Put(ctx, 0, tkey.Encode(), ast.Encode())
case sql.DATABASE:
if err := e.access(ctx, cnf.AuthDB); err != nil {
return nil, err
}
e.tx.AddDB(ctx, e.ns, e.db)
// Save the token definition
tkey := &keys.DT{KV: KV, NS: e.ns, DB: e.db, TK: ast.Name.VA}
_, err = e.tx.Put(ctx, 0, tkey.Encode(), ast.Encode())
case sql.SCOPE:
if err := e.access(ctx, cnf.AuthDB); err != nil {
return nil, err
}
e.tx.AddDB(ctx, e.ns, e.db)
// Save the token definition
tkey := &keys.ST{KV: KV, NS: e.ns, DB: e.db, SC: ast.What.VA, TK: ast.Name.VA}
_, err = e.tx.Put(ctx, 0, tkey.Encode(), ast.Encode())
}
return
}
func (e *executor) executeDefineScope(ctx context.Context, ast *sql.DefineScopeStatement) (out []interface{}, err error) {
if err := e.access(ctx, cnf.AuthDB); err != nil {
return nil, err
}
ast.Code = rand.New(128)
e.tx.AddDB(ctx, e.ns, e.db)
// Remove the scope definition
skey := &keys.SC{KV: KV, NS: e.ns, DB: e.db, SC: ast.Name.VA}
_, err = e.tx.Put(ctx, 0, skey.Encode(), ast.Encode())
return
}
func (e *executor) executeDefineEvent(ctx context.Context, ast *sql.DefineEventStatement) (out []interface{}, err error) {
if err := e.access(ctx, cnf.AuthDB); err != nil {
return nil, err
}
for _, TB := range ast.What {
e.tx.AddTB(ctx, e.ns, e.db, TB.TB)
// Remove the event definition
ekey := &keys.EV{KV: KV, NS: e.ns, DB: e.db, TB: TB.TB, EV: ast.Name.VA}
if _, err = e.tx.Put(ctx, 0, ekey.Encode(), ast.Encode()); err != nil {
return nil, err
}
}
return
}
func (e *executor) executeDefineField(ctx context.Context, ast *sql.DefineFieldStatement) (out []interface{}, err error) {
if err := e.access(ctx, cnf.AuthDB); err != nil {
return nil, err
}
for _, TB := range ast.What {
e.tx.AddTB(ctx, e.ns, e.db, TB.TB)
// Save the field definition
fkey := &keys.FD{KV: KV, NS: e.ns, DB: e.db, TB: TB.TB, FD: ast.Name.VA}
if _, err = e.tx.Put(ctx, 0, fkey.Encode(), ast.Encode()); err != nil {
return nil, err
}
}
return
}
func (e *executor) executeDefineIndex(ctx context.Context, ast *sql.DefineIndexStatement) (out []interface{}, err error) {
if err := e.access(ctx, cnf.AuthDB); err != nil {
return nil, err
}
for _, TB := range ast.What {
e.tx.AddTB(ctx, e.ns, e.db, TB.TB)
// Save the index definition
ikey := &keys.IX{KV: KV, NS: e.ns, DB: e.db, TB: TB.TB, IX: ast.Name.VA}
if _, err = e.tx.Put(ctx, 0, ikey.Encode(), ast.Encode()); err != nil {
return nil, err
}
// Remove the index resource data
dkey := &keys.Index{KV: KV, NS: e.ns, DB: e.db, TB: TB.TB, IX: ast.Name.VA, FD: keys.Ignore}
if _, err = e.tx.ClrP(ctx, dkey.Encode(), 0); err != nil {
return nil, err
}
// Process the index resource data
uctx := context.WithValue(ctx, ctxKeyForce, true)
ustm := &sql.UpdateStatement{What: []sql.Expr{TB}}
if _, err = e.executeUpdate(uctx, ustm); err != nil {
return nil, err
}
}
return
}
func (e *executor) executeDefineTable(ctx context.Context, ast *sql.DefineTableStatement) (out []interface{}, err error) {
if err := e.access(ctx, cnf.AuthDB); err != nil {
return nil, err
}
e.tx.AddDB(ctx, e.ns, e.db)
for _, TB := range ast.What {
ast.Name = sql.NewIdent(TB.TB)
// Save the table definition
tkey := &keys.TB{KV: KV, NS: e.ns, DB: e.db, TB: TB.TB}
if _, err = e.tx.Put(ctx, 0, tkey.Encode(), ast.Encode()); err != nil {
return nil, err
}
if ast.Lock {
// Remove the table resource data
dkey := &keys.Table{KV: KV, NS: e.ns, DB: e.db, TB: TB.TB}
if _, err = e.tx.ClrP(ctx, dkey.Encode(), 0); err != nil {
return nil, err
}
for _, FT := range ast.From {
// Save the foreign table definition
tkey := &keys.FT{KV: KV, NS: e.ns, DB: e.db, TB: FT.TB, FT: TB.TB}
if _, err = e.tx.Put(ctx, 0, tkey.Encode(), ast.Encode()); err != nil {
return nil, err
}
// Process the table resource data
uctx := context.WithValue(ctx, ctxKeyForce, true)
ustm := &sql.UpdateStatement{What: []sql.Expr{FT}}
if _, err = e.executeUpdate(uctx, ustm); err != nil {
return nil, err
}
}
}
}
return
}

File diff suppressed because it is too large Load diff

View file

@ -1,183 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"fmt"
"context"
"github.com/surrealdb/surrealdb/cnf"
"github.com/surrealdb/surrealdb/sql"
"github.com/surrealdb/surrealdb/util/data"
"github.com/surrealdb/surrealdb/util/keys"
)
func (e *executor) executeDelete(ctx context.Context, stm *sql.DeleteStatement) ([]interface{}, error) {
if err := e.access(ctx, cnf.AuthNO); err != nil {
return nil, err
}
var what sql.Exprs
for _, val := range stm.What {
w, err := e.fetch(ctx, val, nil)
if err != nil {
return nil, err
}
what = append(what, w)
}
i := newIterator(e, ctx, stm, false)
for _, w := range what {
switch what := w.(type) {
default:
return nil, fmt.Errorf("Can not execute DELETE query using value '%v'", what)
case *sql.Table:
key := &keys.Table{KV: KV, NS: e.ns, DB: e.db, TB: what.TB}
i.processTable(ctx, key)
case *sql.Ident:
key := &keys.Table{KV: KV, NS: e.ns, DB: e.db, TB: what.VA}
i.processTable(ctx, key)
case *sql.Thing:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db, TB: what.TB, ID: what.ID}
i.processThing(ctx, key)
case *sql.Model:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db, TB: what.TB, ID: nil}
i.processModel(ctx, key, what)
case *sql.Batch:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db, TB: what.TB, ID: nil}
i.processBatch(ctx, key, what)
// Result of subquery
case []interface{}:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db}
i.processOther(ctx, key, what)
// Result of subquery with LIMIT 1
case map[string]interface{}:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db}
i.processOther(ctx, key, []interface{}{what})
}
}
return i.Yield(ctx)
}
func (e *executor) fetchDelete(ctx context.Context, stm *sql.DeleteStatement, doc *data.Doc) (interface{}, error) {
ctx = dive(ctx)
if doc != nil {
vars := data.New()
vars.Set(doc.Data(), varKeyParent)
if subs := ctx.Value(ctxKeySubs); subs != nil {
if subs, ok := subs.(*data.Doc); ok {
vars.Set(subs.Get(varKeyParents).Data(), varKeyParents)
}
} else {
vars.Array(varKeyParents)
}
vars.Append(doc.Data(), varKeyParents)
ctx = context.WithValue(ctx, ctxKeySubs, vars)
}
out, err := e.executeDelete(ctx, stm)
if err != nil {
return nil, err
}
switch len(out) {
case 1:
return data.Consume(out).Get(docKeyOne, docKeyId).Data(), nil
default:
return data.Consume(out).Get(docKeyAll, docKeyId).Data(), nil
}
}
func (d *document) runDelete(ctx context.Context, stm *sql.DeleteStatement) (interface{}, error) {
var ok bool
var err error
var met = _DELETE
if err = d.init(ctx); err != nil {
return nil, err
}
if err = d.lock(ctx); err != nil {
return nil, err
}
if err = d.setup(ctx); err != nil {
return nil, err
}
if d.val.Exi() == false {
return nil, nil
}
if ok, err = d.check(ctx, stm.Cond); err != nil {
return nil, err
} else if ok == false {
return nil, nil
}
if ok, err = d.allow(ctx, met); err != nil {
return nil, err
} else if ok == false {
return nil, nil
}
if err = d.erase(); err != nil {
return nil, err
}
if err = d.purgeIndex(ctx); err != nil {
return nil, err
}
if err = d.purgeThing(ctx); err != nil {
return nil, err
}
if err = d.table(ctx, met); err != nil {
return nil, err
}
if err = d.lives(ctx, met); err != nil {
return nil, err
}
if err = d.event(ctx, met); err != nil {
return nil, err
}
return d.yield(ctx, stm, stm.Echo)
}

View file

@ -1,157 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestDelete(t *testing.T) {
Convey("Delete with invalid value", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
DELETE 1;
DELETE "one";
DELETE ["many"];
DELETE [{value:"one"}];
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 5)
So(res[1].Status, ShouldEqual, "ERR")
So(res[1].Detail, ShouldEqual, "Can not execute DELETE query using value '1'")
So(res[2].Status, ShouldEqual, "ERR")
So(res[2].Detail, ShouldEqual, "Can not execute DELETE query using value 'one'")
So(res[3].Status, ShouldEqual, "ERR")
So(res[3].Detail, ShouldEqual, "Can not execute DELETE query using value '[many]'")
So(res[4].Status, ShouldEqual, "ERR")
So(res[4].Detail, ShouldEqual, "Can not execute DELETE query using value '[map[value:one]]'")
})
Convey("Delete records using `table`", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE |person:10|;
SELECT * FROM person;
DELETE person;
SELECT * FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 5)
So(res[1].Result, ShouldHaveLength, 10)
So(res[2].Result, ShouldHaveLength, 10)
So(res[4].Result, ShouldHaveLength, 0)
})
Convey("Delete specific record using `thing`", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE person:test;
SELECT * FROM person;
DELETE person:test;
SELECT * FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 5)
So(res[1].Result, ShouldHaveLength, 1)
So(res[2].Result, ShouldHaveLength, 1)
So(res[4].Result, ShouldHaveLength, 0)
})
Convey("Delete unique records using `batch`", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE |person:1..10|;
SELECT * FROM person;
DELETE batch("person", ["1", "2", "person:3"]);
SELECT * FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 5)
So(res[1].Result, ShouldHaveLength, 10)
So(res[2].Result, ShouldHaveLength, 10)
So(res[3].Result, ShouldHaveLength, 0)
So(res[4].Result, ShouldHaveLength, 7)
})
Convey("Delete unique records using `model`", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE |person:1..10|;
SELECT * FROM person;
DELETE |person:1..5|;
SELECT * FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 5)
So(res[1].Result, ShouldHaveLength, 10)
So(res[2].Result, ShouldHaveLength, 10)
So(res[3].Result, ShouldHaveLength, 0)
So(res[4].Result, ShouldHaveLength, 5)
})
Convey("Deleting with a timeout of 1ns returns an error", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
DELETE |person:1..1000| TIMEOUT 1ns;
SELECT * FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 3)
So(res[1].Result, ShouldHaveLength, 0)
So(res[2].Result, ShouldHaveLength, 0)
So(res[1].Status, ShouldEqual, "ERR_TO")
So(res[1].Detail, ShouldEqual, "Query timeout of 1ns exceeded")
})
}

View file

@ -1,444 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"fmt"
"context"
"github.com/surrealdb/surrealdb/kvs"
"github.com/surrealdb/surrealdb/sql"
"github.com/surrealdb/surrealdb/util/data"
"github.com/surrealdb/surrealdb/util/indx"
"github.com/surrealdb/surrealdb/util/keys"
)
type document struct {
i *iterator
id *sql.Thing
enc []byte
key *keys.Thing
val kvs.KV
lck bool
doc *data.Doc
initial *data.Doc
current *data.Doc
changed bool
}
func newDocument(i *iterator, key *keys.Thing, val kvs.KV, doc *data.Doc) (d *document) {
d = documentPool.Get().(*document)
d.i = i
d.id = nil
d.enc = nil
d.key = key
d.val = val
d.doc = doc
d.lck = false
return
}
func (d *document) close() {
documentPool.Put(d)
}
func (d *document) erase() (err error) {
d.changed, d.current = true, data.Consume(nil)
return
}
func (d *document) query(ctx context.Context, stm sql.Statement) (val interface{}, err error) {
defer func() {
if r := recover(); r != nil {
var ok bool
if err, ok = r.(error); !ok {
err = fmt.Errorf("%v", r)
}
}
d.ulock(ctx)
d.close()
}()
switch stm := stm.(type) {
default:
return nil, nil
case *sql.SelectStatement:
return d.runSelect(ctx, stm)
case *sql.CreateStatement:
return d.runCreate(ctx, stm)
case *sql.UpdateStatement:
return d.runUpdate(ctx, stm)
case *sql.DeleteStatement:
return d.runDelete(ctx, stm)
case *sql.RelateStatement:
return d.runRelate(ctx, stm)
case *sql.InsertStatement:
return d.runInsert(ctx, stm)
case *sql.UpsertStatement:
return d.runUpsert(ctx, stm)
}
}
func (d *document) init(ctx context.Context) (err error) {
// A table of records were requested
// so we have the values, but no key
// yet, so we need to decode the KV
// store key into a Thing key.
if d.key == nil && d.val != nil {
d.enc = d.val.Key()
if val, ok := keyCache.Get(d.val.Key()); ok {
d.key = val.(*keys.Thing)
} else {
d.key = &keys.Thing{}
d.key.Decode(d.enc)
keyCache.Set(d.val.Key(), d.key, 0)
}
}
return
}
func (d *document) lock(ctx context.Context) (err error) {
if d.key != nil {
d.lck = true
d.i.e.lock.Lock(ctx, d.key)
}
return
}
func (d *document) ulock(ctx context.Context) (err error) {
if d.key != nil && d.lck {
d.lck = false
d.i.e.lock.Unlock(ctx, d.key)
}
return
}
func (d *document) setup(ctx context.Context) (err error) {
// A specific record has been requested
// and we have a key, but no value has
// been loaded yet, so the record needs
// to be loaded from the KV store.
if d.key != nil && d.val == nil {
d.enc = d.key.Encode()
d.val, err = d.i.e.tx.Get(ctx, d.i.versn, d.enc)
if err != nil {
return
}
}
// A subquery or data param has been
// loaded, and we might not have a key
// or a value, so let's load the data
// into a document, so that we can
// maniuplate the virtual document.
if d.doc != nil {
d.initial = d.doc
d.current = d.doc
}
// The requested record has been loaded
// from the KV store (and not from a
// subquery or data variable), but does
// not exist. So we'll create a document
// for processing any record changes.
if d.doc == nil && d.val != nil && d.val.Exi() == false {
d.initial = data.New()
d.current = data.New()
}
// The requested record has been loaded
// from the KV store (and not from a
// subquery or data variable). So we'll
// load the KV data into a document for
// processing any record changes.
if d.doc == nil && d.val != nil && d.val.Exi() == true {
if val, ok := valCache.Get(d.val.Val()); ok {
d.initial = val.(*data.Doc)
d.current = d.initial
} else {
d.initial = data.New().Decode(d.val.Val())
d.current = d.initial
valCache.Set(d.val.Val(), d.current, 0)
}
}
// Finally if we are dealing with a record
// which is not data from the result of a
// subquery, then generate the ID.
if d.key != nil {
d.id = sql.NewThing(d.key.TB, d.key.ID)
}
return
}
func (d *document) forced(ctx context.Context) bool {
if val := ctx.Value(ctxKeyForce); val != nil {
return val.(bool)
}
return false
}
func (d *document) hasChanged(ctx context.Context) bool {
return d.initial.Same(d.current) == false
}
func (d *document) shouldDrop(ctx context.Context) (bool, error) {
// Check whether it is specified
// that the table should drop
// writes, and if so, then return.
tb, err := d.i.e.tx.GetTB(ctx, d.key.NS, d.key.DB, d.key.TB)
if err != nil {
return false, err
}
return tb.Drop, err
}
func (d *document) shouldVersn(ctx context.Context) (bool, error) {
// Check whether it is specified
// that the table should keep
// all document versions.
tb, err := d.i.e.tx.GetTB(ctx, d.key.NS, d.key.DB, d.key.TB)
if err != nil {
return false, err
}
return tb.Vers, err
}
func (d *document) storeThing(ctx context.Context) (err error) {
defer d.ulock(ctx)
// Check that the record has been
// changed, and if not, return.
if !d.changed {
return
}
// Check that the table should
// drop data being written.
if ok, err := d.shouldDrop(ctx); ok {
return err
}
// Write the value to the data
// layer and return any errors.
if ok, err := d.shouldVersn(ctx); err != nil {
return err
} else if ok == true {
_, err = d.i.e.tx.Put(ctx, d.i.e.time.UnixNano(), d.enc, d.current.Encode())
} else if ok == false {
_, err = d.i.e.tx.Put(ctx, 0, d.enc, d.current.Encode())
}
return
}
func (d *document) purgeThing(ctx context.Context) (err error) {
defer d.ulock(ctx)
// Check that the table should
// drop data being written.
if ok, err := d.shouldDrop(ctx); ok {
return err
}
// Reset the item by writing a
// nil value to the storage.
if ok, err := d.shouldVersn(ctx); err != nil {
return err
} else if ok == true {
_, err = d.i.e.tx.Put(ctx, d.i.e.time.UnixNano(), d.enc, nil)
} else if ok == false {
_, err = d.i.e.tx.Clr(ctx, d.enc)
}
return
}
func (d *document) storeIndex(ctx context.Context) (err error) {
// Check if this query has been run
// in forced mode, or return.
forced := d.forced(ctx)
// Check that the rcord has been
// changed, and if not, return.
if !forced && !d.changed {
return
}
// Check that the table should
// drop data being written.
if ok, err := d.shouldDrop(ctx); ok {
return err
}
// Get the index values specified
// for this table, loop through
// them, and compute the changes.
ixs, err := d.i.e.tx.AllIX(ctx, d.key.NS, d.key.DB, d.key.TB)
if err != nil {
return err
}
for _, ix := range ixs {
del := indx.Build(ix.Cols, d.initial)
add := indx.Build(ix.Cols, d.current)
if !forced {
del, add = indx.Diff(del, add)
}
if ix.Uniq == true {
for _, f := range del {
enfd := data.Consume(f).Encode()
didx := &keys.Index{KV: d.key.KV, NS: d.key.NS, DB: d.key.DB, TB: d.key.TB, IX: ix.Name.VA, FD: enfd}
d.i.e.tx.DelC(ctx, 0, didx.Encode(), d.id.Bytes())
}
for _, f := range add {
enfd := data.Consume(f).Encode()
aidx := &keys.Index{KV: d.key.KV, NS: d.key.NS, DB: d.key.DB, TB: d.key.TB, IX: ix.Name.VA, FD: enfd}
if _, err = d.i.e.tx.PutC(ctx, 0, aidx.Encode(), d.id.Bytes(), nil); err != nil {
return &IndexError{tb: d.key.TB, name: ix.Name, cols: ix.Cols, vals: f}
}
}
}
if ix.Uniq == false {
for _, f := range del {
enfd := data.Consume(f).Encode()
didx := &keys.Point{KV: d.key.KV, NS: d.key.NS, DB: d.key.DB, TB: d.key.TB, IX: ix.Name.VA, FD: enfd, ID: d.key.ID}
d.i.e.tx.DelC(ctx, 0, didx.Encode(), d.id.Bytes())
}
for _, f := range add {
enfd := data.Consume(f).Encode()
aidx := &keys.Point{KV: d.key.KV, NS: d.key.NS, DB: d.key.DB, TB: d.key.TB, IX: ix.Name.VA, FD: enfd, ID: d.key.ID}
if _, err = d.i.e.tx.PutC(ctx, 0, aidx.Encode(), d.id.Bytes(), nil); err != nil {
return &IndexError{tb: d.key.TB, name: ix.Name, cols: ix.Cols, vals: f}
}
}
}
}
return
}
func (d *document) purgeIndex(ctx context.Context) (err error) {
// Check if this query has been run
// in forced mode, or return.
forced := d.forced(ctx)
// Check that the rcord has been
// changed, and if not, return.
if !forced && !d.changed {
return
}
// Check that the table should
// drop data being written.
if ok, err := d.shouldDrop(ctx); ok {
return err
}
// Get the index values specified
// for this table, loop through
// them, and compute the changes.
ixs, err := d.i.e.tx.AllIX(ctx, d.key.NS, d.key.DB, d.key.TB)
if err != nil {
return err
}
for _, ix := range ixs {
del := indx.Build(ix.Cols, d.initial)
if ix.Uniq == true {
for _, f := range del {
enfd := data.Consume(f).Encode()
didx := &keys.Index{KV: d.key.KV, NS: d.key.NS, DB: d.key.DB, TB: d.key.TB, IX: ix.Name.VA, FD: enfd}
d.i.e.tx.DelC(ctx, 0, didx.Encode(), d.id.Bytes())
}
}
if ix.Uniq == false {
for _, f := range del {
enfd := data.Consume(f).Encode()
aidx := &keys.Point{KV: d.key.KV, NS: d.key.NS, DB: d.key.DB, TB: d.key.TB, IX: ix.Name.VA, FD: enfd, ID: d.key.ID}
d.i.e.tx.DelC(ctx, 0, aidx.Encode(), d.id.Bytes())
}
}
}
return
}

View file

@ -1,141 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"fmt"
"time"
"github.com/surrealdb/surrealdb/sql"
)
// LiveError occurs when a query can not be run.
type LiveError struct{}
// Error returns the string representation of the error.
func (e *LiveError) Error() string {
return fmt.Sprintf("Unable to perform live query.")
}
// TimerError occurs when a query times out.
type TimerError struct {
timer time.Duration
}
// Error returns the string representation of the error.
func (e *TimerError) Error() string {
return fmt.Sprintf("Query timeout of %v exceeded", e.timer)
}
// TableError occurs when an table value is unable to be written.
type TableError struct {
table string
}
// Error returns the string representation of the error.
func (e *TableError) Error() string {
return fmt.Sprintf("Unable to write to the '%v' table while it is setup as a view", e.table)
}
// QueryError represents an error that occured when switching access.
type QueryError struct{}
// Error returns the string representation of the error.
func (e *QueryError) Error() string {
return fmt.Sprint("You don't have permission to perform this query type")
}
// BlankError represents an error that occured when switching access.
type BlankError struct{}
// Error returns the string representation of the error.
func (e *BlankError) Error() string {
return fmt.Sprint("You need to specify a namespace and a database to use")
}
// PermsError occurs when a table query is not allowed.
type PermsError struct {
table string
}
// Error returns the string representation of the error.
func (e *PermsError) Error() string {
return fmt.Sprintf("You don't have permission to perform this query on the '%v' table", e.table)
}
// LimitError occurs when a 'limit' expression is invalid.
type LimitError struct {
found interface{}
}
// Error returns the string representation of the error.
func (e *LimitError) Error() string {
return fmt.Sprintf("Found '%v' but LIMIT expression must be a number", e.found)
}
// StartError occurs when a 'start' expression is invalid.
type StartError struct {
found interface{}
}
// Error returns the string representation of the error.
func (e *StartError) Error() string {
return fmt.Sprintf("Found '%v' but START expression must be a number", e.found)
}
// VersnError occurs when a 'version' expression is invalid.
type VersnError struct {
found interface{}
}
// Error returns the string representation of the error.
func (e *VersnError) Error() string {
return fmt.Sprintf("Found '%v' but VERSION expression must be a date or time", e.found)
}
// ExistError occurs when a record already exists.
type ExistError struct {
exist *sql.Thing
}
// Error returns the string representation of the error.
func (e *ExistError) Error() string {
return fmt.Sprintf("Database record '%v' already exists", e.exist)
}
// FieldError occurs when a field does not conform to the specified assertion.
type FieldError struct {
field interface{}
found interface{}
check interface{}
}
// Error returns the string representation of the error.
func (e *FieldError) Error() string {
return fmt.Sprintf("Found '%v' for field '%v' but field must conform to: %s", e.found, e.field, e.check)
}
// IndexError occurs when an index value is unable to be written.
type IndexError struct {
tb string
name *sql.Ident
cols sql.Idents
vals []interface{}
}
// Error returns the string representation of the error.
func (e *IndexError) Error() string {
return fmt.Sprintf("Duplicate entry for [%v] wth values %v in index '%s' on '%s'", e.cols, e.vals, e.name, e.tb)
}

View file

@ -1,105 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"context"
"github.com/surrealdb/surrealdb/cnf"
"github.com/surrealdb/surrealdb/util/data"
)
// Event checks if any triggers are specified for this
// table, and executes them in name order.
func (d *document) event(ctx context.Context, met method) (err error) {
if !d.i.e.opts.events {
return nil
}
// Check if this query has been run
// in forced mode, because of an
// index or foreign table update.
forced := d.forced(ctx)
// If this document has not changed
// then there is no need to perform
// any registered events.
if !forced && !d.changed {
return nil
}
// Get the event values specified
// for this table, loop through
// them, and compute the events.
evs, err := d.i.e.tx.AllEV(ctx, d.key.NS, d.key.DB, d.key.TB)
if err != nil {
return err
}
if len(evs) > 0 {
kind := ""
switch met {
case _CREATE:
kind = "CREATE"
case _UPDATE:
kind = "UPDATE"
case _DELETE:
kind = "DELETE"
}
var id = d.i.e.id
d.i.e.id = d.i.e.id + "-bg"
defer func() { d.i.e.id = id }()
vars := data.New()
vars.Set(d.id, varKeyThis)
vars.Set(kind, varKeyMethod)
vars.Set(d.current.Data(), varKeyAfter)
vars.Set(d.initial.Data(), varKeyBefore)
ctx = context.WithValue(ctx, ctxKeySpec, vars)
ctx = context.WithValue(ctx, ctxKeyKind, cnf.AuthDB)
for _, ev := range evs {
val, err := d.i.e.fetch(ctx, ev.When, d.current)
if err != nil {
return err
}
switch v := val.(type) {
case bool:
switch v {
case true:
_, err = d.i.e.fetch(ctx, ev.Then, d.current)
if err != nil {
return err
}
}
}
}
}
return
}

View file

@ -1,543 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"sync"
"time"
"context"
"runtime/debug"
"github.com/surrealdb/fibre"
"github.com/surrealdb/surrealdb/cnf"
"github.com/surrealdb/surrealdb/kvs"
"github.com/surrealdb/surrealdb/log"
"github.com/surrealdb/surrealdb/sql"
"github.com/surrealdb/surrealdb/txn"
)
type executor struct {
id string
ns string
db string
tx *txn.TX
err error
buf []*Response
time time.Time
lock *mutex
opts *options
data sync.Map
send chan *Response
}
func newExecutor(id, ns, db string) (e *executor) {
e = executorPool.Get().(*executor)
e.id = id
e.ns = ns
e.db = db
e.tx = nil
e.err = nil
e.buf = nil
e.data = sync.Map{}
e.opts = newOptions()
e.send = make(chan *Response)
return
}
func (e *executor) execute(ctx context.Context, ast *sql.Query) {
// Ensure that the executor is added back into
// the executor pool when the executor has
// finished processing the request.
defer executorPool.Put(e)
// Ensure that the query responses channel is
// closed when the full query has been processed
// and dealt with.
defer close(e.send)
// If we are making use of a global transaction
// which is not committed at the end of the
// query set, then cancel the transaction.
defer func() {
if e.tx != nil {
e.tx.Cancel()
clear(e.id)
e.tx = nil
}
}()
// If we have panicked during query execution
// then ensure that we recover from the error
// and print the error to the log.
defer func() {
if err := recover(); err != nil {
if log.IsError() {
log.WithPrefix(logKeyDB).WithFields(map[string]interface{}{
logKeyId: e.id,
logKeyStack: string(debug.Stack()),
logKeyFibre: ctx.Value(ctxKeyFibre).(*fibre.Context),
}).Errorln(err)
}
}
}()
// Loop over the defined query statements and
// process them, while listening for the quit
// channel to see if the client has gone away.
for _, stm := range ast.Statements {
select {
case <-ctx.Done():
return
default:
e.conduct(ctx, stm)
}
}
}
func (e *executor) conduct(ctx context.Context, stm sql.Statement) {
var rsp *Response
var res []interface{}
// If we are not inside a global transaction
// then reset the error to nil so that the
// next statement is not ignored.
if e.tx == nil {
e.err = nil
}
// Check to see if the current statement is
// a TRANSACTION statement, and if it is
// then deal with it and move on to the next.
switch stm.(type) {
case *sql.BeginStatement:
e.err = e.begin(ctx, true)
if e.err != nil {
clear(e.id)
}
return
case *sql.CancelStatement:
e.err = e.cancel(e.send)
if e.err != nil {
clear(e.id)
} else {
clear(e.id)
}
return
case *sql.CommitStatement:
e.err = e.commit(e.send)
if e.err != nil {
clear(e.id)
} else {
flush(e.id)
}
return
}
// If an error has occured and we are inside
// a global transaction, then ignore all
// subsequent statements in the transaction.
if e.err == nil {
res, e.err = e.operate(ctx, stm)
} else {
res, e.err = []interface{}{}, errQueryNotExecuted
}
// Generate the response
rsp = &Response{
Time: time.Since(e.time).String(),
Status: status(e.err),
Detail: detail(e.err),
Result: append([]interface{}{}, res...),
}
// Log the sql statement along with the
// query duration time, and mark it as
// an error if the query failed.
switch e.err.(type) {
default:
if log.IsDebug() {
log.WithPrefix(logKeySql).WithFields(map[string]interface{}{
logKeyId: e.id,
logKeyNS: e.ns,
logKeyDB: e.db,
logKeyKind: ctx.Value(ctxKeyKind),
logKeyVars: ctx.Value(ctxKeyVars),
logKeyTime: time.Since(e.time).String(),
logKeyFibre: ctx.Value(ctxKeyFibre).(*fibre.Context),
}).Debugln(stm)
}
case error:
if log.IsError() {
log.WithPrefix(logKeySql).WithFields(map[string]interface{}{
logKeyId: e.id,
logKeyNS: e.ns,
logKeyDB: e.db,
logKeyKind: ctx.Value(ctxKeyKind),
logKeyVars: ctx.Value(ctxKeyVars),
logKeyTime: time.Since(e.time).String(),
logKeyFibre: ctx.Value(ctxKeyFibre).(*fibre.Context),
logKeyError: detail(e.err),
}).Errorln(stm)
}
}
// If we are not inside a global transaction
// then we can output the statement response
// immediately to the channel.
if e.tx == nil {
e.send <- rsp
}
// If we are inside a global transaction we
// must buffer the responses for output at
// the end of the transaction.
if e.tx != nil {
switch stm.(type) {
case *sql.ReturnStatement:
for i := len(e.buf) - 1; i >= 0; i-- {
e.buf[len(e.buf)-1] = nil
e.buf = e.buf[:len(e.buf)-1]
}
e.buf = append(e.buf, rsp)
default:
e.buf = append(e.buf, rsp)
}
}
}
func (e *executor) operate(ctx context.Context, stm sql.Statement) (res []interface{}, err error) {
var loc bool
var trw bool
var canc context.CancelFunc
// If we are not inside a global transaction
// then grab a new transaction, ensuring that
// it is closed at the end.
if e.tx == nil {
switch stm := stm.(type) {
case sql.WriteableStatement:
loc, trw = true, stm.Writeable()
default:
loc, trw = true, false
}
err = e.begin(ctx, trw)
if err != nil {
return
}
}
// Mark the beginning of this statement so we
// can monitor the running time, and ensure
// it runs no longer than specified.
if cnf.Settings.Query.Timeout > 0 {
if perm(ctx) != cnf.AuthKV {
ctx, canc = context.WithTimeout(ctx, cnf.Settings.Query.Timeout)
defer func() {
if tim := ctx.Err(); err == nil && tim != nil {
res, err = nil, &TimerError{timer: cnf.Settings.Query.Timeout}
}
canc()
}()
}
}
// Mark the beginning of this statement so we
// can monitor the running time, and ensure
// it runs no longer than specified.
if stm, ok := stm.(sql.KillableStatement); ok {
if dur := stm.Duration(); dur > 0 {
ctx, canc = context.WithTimeout(ctx, dur)
defer func() {
if tim := ctx.Err(); err == nil && tim != nil {
res, err = nil, &TimerError{timer: dur}
}
canc()
}()
}
}
// Execute the defined statement, receiving the
// result set, and any errors which occured
// while processing the query.
switch stm := stm.(type) {
case *sql.OptStatement:
res, err = e.executeOpt(ctx, stm)
case *sql.UseStatement:
res, err = e.executeUse(ctx, stm)
case *sql.RunStatement:
res, err = e.executeRun(ctx, stm)
case *sql.InfoStatement:
res, err = e.executeInfo(ctx, stm)
case *sql.LetStatement:
res, err = e.executeLet(ctx, stm)
case *sql.ReturnStatement:
res, err = e.executeReturn(ctx, stm)
case *sql.LiveStatement:
res, err = e.executeLive(ctx, stm)
case *sql.KillStatement:
res, err = e.executeKill(ctx, stm)
case *sql.IfelseStatement:
res, err = e.executeIfelse(ctx, stm)
case *sql.SelectStatement:
res, err = e.executeSelect(ctx, stm)
case *sql.CreateStatement:
res, err = e.executeCreate(ctx, stm)
case *sql.UpdateStatement:
res, err = e.executeUpdate(ctx, stm)
case *sql.DeleteStatement:
res, err = e.executeDelete(ctx, stm)
case *sql.RelateStatement:
res, err = e.executeRelate(ctx, stm)
case *sql.InsertStatement:
res, err = e.executeInsert(ctx, stm)
case *sql.UpsertStatement:
res, err = e.executeUpsert(ctx, stm)
case *sql.DefineNamespaceStatement:
res, err = e.executeDefineNamespace(ctx, stm)
case *sql.RemoveNamespaceStatement:
res, err = e.executeRemoveNamespace(ctx, stm)
case *sql.DefineDatabaseStatement:
res, err = e.executeDefineDatabase(ctx, stm)
case *sql.RemoveDatabaseStatement:
res, err = e.executeRemoveDatabase(ctx, stm)
case *sql.DefineLoginStatement:
res, err = e.executeDefineLogin(ctx, stm)
case *sql.RemoveLoginStatement:
res, err = e.executeRemoveLogin(ctx, stm)
case *sql.DefineTokenStatement:
res, err = e.executeDefineToken(ctx, stm)
case *sql.RemoveTokenStatement:
res, err = e.executeRemoveToken(ctx, stm)
case *sql.DefineScopeStatement:
res, err = e.executeDefineScope(ctx, stm)
case *sql.RemoveScopeStatement:
res, err = e.executeRemoveScope(ctx, stm)
case *sql.DefineTableStatement:
res, err = e.executeDefineTable(ctx, stm)
case *sql.RemoveTableStatement:
res, err = e.executeRemoveTable(ctx, stm)
case *sql.DefineEventStatement:
res, err = e.executeDefineEvent(ctx, stm)
case *sql.RemoveEventStatement:
res, err = e.executeRemoveEvent(ctx, stm)
case *sql.DefineFieldStatement:
res, err = e.executeDefineField(ctx, stm)
case *sql.RemoveFieldStatement:
res, err = e.executeRemoveField(ctx, stm)
case *sql.DefineIndexStatement:
res, err = e.executeDefineIndex(ctx, stm)
case *sql.RemoveIndexStatement:
res, err = e.executeRemoveIndex(ctx, stm)
}
// If the context is already closed or failed,
// then ignore this result, clear all queued
// changes, and reset the transaction.
select {
case <-ctx.Done():
e.tx.Cancel()
clear(e.id)
e.tx = nil
default:
// If this is a local transaction for only the
// current statement, then commit or cancel
// depending on the result error.
if loc && e.tx.Closed() == false {
// If there was an error with the query
// then clear the queued changes and
// return immediately.
if err != nil {
e.tx.Cancel()
clear(e.id)
e.tx = nil
return
}
// Otherwise check if this is a read or
// a write transaction, and attempt to
// Cancel or Commit, returning any errors.
if !trw {
if err = e.tx.Cancel(); err != nil {
clear(e.id)
e.tx = nil
} else {
clear(e.id)
e.tx = nil
}
} else {
if err = e.tx.Commit(); err != nil {
clear(e.id)
e.tx = nil
} else {
flush(e.id)
e.tx = nil
}
}
}
}
return
}
func (e *executor) begin(ctx context.Context, rw bool) (err error) {
e.tx, err = txn.New(ctx, rw)
e.time = time.Now()
e.lock = new(mutex)
return
}
func (e *executor) cancel(chn chan<- *Response) (err error) {
defer func() {
e.tx.Cancel()
e.tx = nil
e.buf = nil
e.err = nil
}()
for _, v := range e.buf {
v.Time = time.Since(e.time).String()
v.Status = "ERR"
v.Result = []interface{}{}
v.Detail = "Transaction cancelled"
chn <- v
}
return
}
func (e *executor) commit(chn chan<- *Response) (err error) {
defer func() {
e.tx.Cancel()
e.tx = nil
e.buf = nil
e.err = nil
}()
if e.err != nil {
err = e.tx.Cancel()
} else {
err = e.tx.Commit()
}
for _, v := range e.buf {
if err != nil {
v.Time = time.Since(e.time).String()
v.Status = "ERR"
v.Result = []interface{}{}
v.Detail = "Transaction failed: " + err.Error()
}
chn <- v
}
return
}
func status(e error) (s string) {
switch e.(type) {
default:
return "OK"
case *kvs.DBError:
return "ERR_DB"
case *PermsError:
return "ERR_PE"
case *ExistError:
return "ERR_EX"
case *FieldError:
return "ERR_FD"
case *IndexError:
return "ERR_IX"
case *TimerError:
return "ERR_TO"
case error:
return "ERR"
}
}
func detail(e error) (s string) {
switch err := e.(type) {
default:
return
case error:
return err.Error()
}
}

View file

@ -1,315 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"fmt"
"math"
"github.com/surrealdb/fibre"
"github.com/surrealdb/surrealdb/cnf"
"github.com/surrealdb/surrealdb/kvs"
"github.com/surrealdb/surrealdb/sql"
"github.com/surrealdb/surrealdb/util/data"
"github.com/surrealdb/surrealdb/util/keys"
)
var sep = `
-- ------------------------------
-- %s
-- ------------------------------
`
func export(c *fibre.Context, NS, DB string) error {
w := c.Response()
ctx := c.Context()
exe := newExecutor(NIL, NS, DB)
err := exe.begin(ctx, false)
defer executorPool.Put(exe)
// ------------------------------
// Options
// ------------------------------
fmt.Fprintf(w, sep, "OPTION")
fmt.Fprintln(w)
fmt.Fprintf(w, "OPTION IMPORT;\n")
// ------------------------------
// Tokens
// ------------------------------
dts, err := exe.tx.AllDT(ctx, NS, DB)
if err != nil {
return err
}
if len(dts) > 0 {
fmt.Fprintf(w, sep, "TOKENS")
fmt.Fprintln(w)
for _, v := range dts {
fmt.Fprintf(w, "%s;\n", v)
}
}
// ------------------------------
// Logins
// ------------------------------
dus, err := exe.tx.AllDU(ctx, NS, DB)
if err != nil {
return err
}
if len(dus) > 0 {
fmt.Fprintf(w, sep, "LOGINS")
fmt.Fprintln(w)
for _, v := range dus {
fmt.Fprintf(w, "%s;\n", v)
}
}
// ------------------------------
// Scopes
// ------------------------------
scs, err := exe.tx.AllSC(ctx, NS, DB)
if err != nil {
return err
}
if len(scs) > 0 {
fmt.Fprintf(w, sep, "SCOPES")
fmt.Fprintln(w)
for _, v := range scs {
fmt.Fprintf(w, "%s;\n", v)
// ------------------------------
// Tokens
// ------------------------------
sct, err := exe.tx.AllST(ctx, NS, DB, v.Name.VA)
if err != nil {
return err
}
if len(sct) > 0 {
fmt.Fprintln(w)
for _, v := range sct {
fmt.Fprintf(w, "%s;\n", v)
}
fmt.Fprintln(w)
}
}
}
// ------------------------------
// Tables
// ------------------------------
tbs, err := exe.tx.AllTB(ctx, NS, DB)
if err != nil {
return err
}
for _, TB := range tbs {
fmt.Fprintf(w, sep, "TABLE: "+TB.Name.VA)
// ------------------------------
// Remove
// ------------------------------
fmt.Fprintln(w)
fmt.Fprintf(w, "%s;\n", &sql.RemoveTableStatement{
What: sql.Tables{&sql.Table{TB.Name.VA}},
})
// ------------------------------
// Define
// ------------------------------
fmt.Fprintln(w)
fmt.Fprintf(w, "%s;\n", TB)
// ------------------------------
// Events
// ------------------------------
evs, err := exe.tx.AllEV(ctx, NS, DB, TB.Name.VA)
if err != nil {
return err
}
if len(evs) > 0 {
fmt.Fprintln(w)
for _, v := range evs {
fmt.Fprintf(w, "%s;\n", v)
}
}
// ------------------------------
// Fields
// ------------------------------
fds, err := exe.tx.AllFD(ctx, NS, DB, TB.Name.VA)
if err != nil {
return err
}
if len(fds) > 0 {
fmt.Fprintln(w)
for _, v := range fds {
fmt.Fprintf(w, "%s;\n", v)
}
}
// ------------------------------
// Indexes
// ------------------------------
ixs, err := exe.tx.AllIX(ctx, NS, DB, TB.Name.VA)
if err != nil {
return err
}
if len(ixs) > 0 {
fmt.Fprintln(w)
for _, v := range ixs {
fmt.Fprintf(w, "%s;\n", v)
}
}
}
// ------------------------------
// BEGIN
// ------------------------------
fmt.Fprintf(w, sep, "TRANSACTION")
fmt.Fprintln(w)
fmt.Fprintf(w, "BEGIN TRANSACTION;\n")
// ------------------------------
// DATA
// ------------------------------
TB:
for _, TB := range tbs {
fmt.Fprintf(w, sep, "TABLE DATA: "+TB.Name.VA)
fmt.Fprintln(w)
beg := &keys.Thing{KV: cnf.Settings.DB.Base, NS: NS, DB: DB, TB: TB.Name.VA, ID: keys.Ignore}
end := &keys.Thing{KV: cnf.Settings.DB.Base, NS: NS, DB: DB, TB: TB.Name.VA, ID: keys.Suffix}
min, max := beg.Encode(), end.Encode()
for x := 0; ; x = 1 {
var err error
var vls []kvs.KV
if TB.Vers {
vls, err = exe.tx.AllR(ctx, min, max, 10000)
} else {
vls, err = exe.tx.GetR(ctx, math.MaxInt64, min, max, 10000)
}
if err != nil {
return err
}
// If there are no further records
// fetched from the data layer, then
// return out of this loop iteration.
if x >= len(vls) {
continue TB
}
// If there is at least 1 key-value
// then loop over all the items and
// process the records.
n := data.New()
for _, kv := range vls {
k := &keys.Thing{}
k.Decode(kv.Key())
v := kv.Ver()
if kv.Exi() {
n = data.New().Decode(kv.Val())
j, _ := n.MarshalJSON()
if TB.Vers {
fmt.Fprintf(w, "UPDATE ⟨%s⟩:⟨%s⟩ CONTENT %s VERSION %d;\n", k.TB, k.ID, j, v)
} else {
fmt.Fprintf(w, "UPDATE ⟨%s⟩:⟨%s⟩ CONTENT %s;\n", k.TB, k.ID, j)
}
} else {
if TB.Vers {
fmt.Fprintf(w, "DELETE ⟨%s⟩:⟨%s⟩ VERSION %d;\n", k.TB, k.ID, v)
} else {
fmt.Fprintf(w, "DELETE ⟨%s⟩:⟨%s⟩;\n", k.TB, k.ID)
}
}
}
// When we loop around, we will use
// the key of the last retrieved key
// to perform the next range request.
beg.Decode(vls[len(vls)-1].Key())
min = append(beg.Encode(), byte(0))
}
}
// ------------------------------
// COMMIT
// ------------------------------
fmt.Fprintf(w, sep, "TRANSACTION")
fmt.Fprintln(w)
fmt.Fprintf(w, "COMMIT TRANSACTION;\n")
return nil
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,18 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
//go:generate go get -u github.com/ugorji/go/codec/codecgen
//go:generate codecgen -o db.gen.go db.go

View file

@ -1,56 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"context"
"github.com/surrealdb/surrealdb/sql"
"github.com/surrealdb/surrealdb/util/data"
)
func (e *executor) executeIfelse(ctx context.Context, stm *sql.IfelseStatement) (out []interface{}, err error) {
val, err := e.fetchIfelse(ctx, stm, nil)
if err != nil {
return nil, err
}
switch val := val.(type) {
case []interface{}:
out = val
case interface{}:
out = append(out, val)
}
return
}
func (e *executor) fetchIfelse(ctx context.Context, stm *sql.IfelseStatement, doc *data.Doc) (interface{}, error) {
for k, v := range stm.Cond {
ife, err := e.fetch(ctx, v, doc)
if err != nil {
return nil, err
}
if calcAsBool(ife) {
return e.fetch(ctx, stm.Then[k], doc)
}
}
return e.fetch(ctx, stm.Else, doc)
}

View file

@ -1,132 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"testing"
"github.com/surrealdb/surrealdb/util/data"
. "github.com/smartystreets/goconvey/convey"
)
func TestIf(t *testing.T) {
Convey("If statement which runs if clause", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE |person:1..3|;
LET temp = 13.753;
IF $temp THEN
(SELECT * FROM person:1)
ELSE
(SELECT * FROM person:3)
END;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[1].Status, ShouldEqual, "OK")
So(res[2].Status, ShouldEqual, "OK")
So(res[3].Status, ShouldEqual, "OK")
So(data.Consume(res[3].Result[0]).Get("meta.id").Data(), ShouldEqual, 1)
})
Convey("If statement which runs if clause", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE |person:1..3|;
LET temp = 13.753;
IF $temp > 10 THEN
(SELECT * FROM person:1)
ELSE IF $temp > 5 THEN
(SELECT * FROM person:2)
ELSE
(SELECT * FROM person:3)
END;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[1].Status, ShouldEqual, "OK")
So(res[2].Status, ShouldEqual, "OK")
So(res[3].Status, ShouldEqual, "OK")
So(data.Consume(res[3].Result[0]).Get("meta.id").Data(), ShouldEqual, 1)
})
Convey("If statement which runs elif clause", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE |person:1..3|;
LET temp = 7.1374;
IF $temp > 10 THEN
(SELECT * FROM person:1)
ELSE IF $temp > 5 THEN
(SELECT * FROM person:2)
ELSE
(SELECT * FROM person:3)
END;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[1].Status, ShouldEqual, "OK")
So(res[2].Status, ShouldEqual, "OK")
So(res[3].Status, ShouldEqual, "OK")
So(data.Consume(res[3].Result[0]).Get("meta.id").Data(), ShouldEqual, 2)
})
Convey("If statement which runs else clause", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE |person:1..3|;
LET temp = true;
IF $temp > 10 THEN
(SELECT * FROM person:1)
ELSE IF $temp > 5 THEN
(SELECT * FROM person:2)
ELSE
(SELECT * FROM person:3)
END;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[1].Status, ShouldEqual, "OK")
So(res[2].Status, ShouldEqual, "OK")
So(res[3].Status, ShouldEqual, "OK")
So(data.Consume(res[3].Result[0]).Get("meta.id").Data(), ShouldEqual, 3)
})
}

View file

@ -1,261 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"context"
"github.com/surrealdb/surrealdb/cnf"
"github.com/surrealdb/surrealdb/sql"
"github.com/surrealdb/surrealdb/util/data"
)
func (e *executor) executeInfo(ctx context.Context, ast *sql.InfoStatement) (out []interface{}, err error) {
switch ast.Kind {
case sql.ALL:
return e.executeInfoKV(ctx, ast)
case sql.NAMESPACE, sql.NS:
return e.executeInfoNS(ctx, ast)
case sql.DATABASE, sql.DB:
return e.executeInfoDB(ctx, ast)
case sql.SCOPE:
return e.executeInfoSC(ctx, ast)
case sql.TABLE:
return e.executeInfoTB(ctx, ast)
}
return
}
func (e *executor) executeInfoKV(ctx context.Context, ast *sql.InfoStatement) (out []interface{}, err error) {
if err := e.access(ctx, cnf.AuthKV); err != nil {
return nil, err
}
ns, err := e.tx.AllNS(ctx)
if err != nil {
return nil, err
}
res := data.New()
nspac := make(map[string]interface{})
for _, v := range ns {
nspac[v.Name.VA] = v.String()
}
res.Set(nspac, "namespace")
return []interface{}{res.Data()}, nil
}
func (e *executor) executeInfoNS(ctx context.Context, ast *sql.InfoStatement) (out []interface{}, err error) {
if err := e.access(ctx, cnf.AuthNS); err != nil {
return nil, err
}
db, err := e.tx.AllDB(ctx, e.ns)
if err != nil {
return nil, err
}
nt, err := e.tx.AllNT(ctx, e.ns)
if err != nil {
return nil, err
}
nu, err := e.tx.AllNU(ctx, e.ns)
if err != nil {
return nil, err
}
res := data.New()
dbase := make(map[string]interface{})
for _, v := range db {
dbase[v.Name.VA] = v.String()
}
token := make(map[string]interface{})
for _, v := range nt {
token[v.Name.VA] = v.String()
}
login := make(map[string]interface{})
for _, v := range nu {
login[v.User.VA] = v.String()
}
res.Set(dbase, "database")
res.Set(token, "token")
res.Set(login, "login")
return []interface{}{res.Data()}, nil
}
func (e *executor) executeInfoDB(ctx context.Context, ast *sql.InfoStatement) (out []interface{}, err error) {
if err := e.access(ctx, cnf.AuthDB); err != nil {
return nil, err
}
tb, err := e.tx.AllTB(ctx, e.ns, e.db)
if err != nil {
return nil, err
}
dt, err := e.tx.AllDT(ctx, e.ns, e.db)
if err != nil {
return nil, err
}
du, err := e.tx.AllDU(ctx, e.ns, e.db)
if err != nil {
return nil, err
}
sc, err := e.tx.AllSC(ctx, e.ns, e.db)
if err != nil {
return nil, err
}
res := data.New()
table := make(map[string]interface{})
for _, v := range tb {
table[v.Name.VA] = v.String()
}
token := make(map[string]interface{})
for _, v := range dt {
token[v.Name.VA] = v.String()
}
login := make(map[string]interface{})
for _, v := range du {
login[v.User.VA] = v.String()
}
scope := make(map[string]interface{})
for _, v := range sc {
scope[v.Name.VA] = v.String()
}
res.Set(table, "table")
res.Set(token, "token")
res.Set(login, "login")
res.Set(scope, "scope")
return []interface{}{res.Data()}, nil
}
func (e *executor) executeInfoSC(ctx context.Context, ast *sql.InfoStatement) (out []interface{}, err error) {
if err := e.access(ctx, cnf.AuthDB); err != nil {
return nil, err
}
st, err := e.tx.AllST(ctx, e.ns, e.db, ast.What.VA)
if err != nil {
return nil, err
}
res := data.New()
token := make(map[string]interface{})
for _, v := range st {
token[v.Name.VA] = v.String()
}
res.Set(token, "token")
return []interface{}{res.Data()}, nil
}
func (e *executor) executeInfoTB(ctx context.Context, ast *sql.InfoStatement) (out []interface{}, err error) {
if err := e.access(ctx, cnf.AuthDB); err != nil {
return nil, err
}
ev, err := e.tx.AllEV(ctx, e.ns, e.db, ast.What.VA)
if err != nil {
return nil, err
}
fd, err := e.tx.AllFD(ctx, e.ns, e.db, ast.What.VA)
if err != nil {
return nil, err
}
ix, err := e.tx.AllIX(ctx, e.ns, e.db, ast.What.VA)
if err != nil {
return nil, err
}
ft, err := e.tx.AllFT(ctx, e.ns, e.db, ast.What.VA)
if err != nil {
return nil, err
}
lv, err := e.tx.AllLV(ctx, e.ns, e.db, ast.What.VA)
if err != nil {
return nil, err
}
res := data.New()
event := make(map[string]interface{})
for _, v := range ev {
event[v.Name.VA] = v.String()
}
field := make(map[string]interface{})
for _, v := range fd {
field[v.Name.VA] = v.String()
}
index := make(map[string]interface{})
for _, v := range ix {
index[v.Name.VA] = v.String()
}
table := make(map[string]interface{})
for _, v := range ft {
table[v.Name.VA] = v.String()
}
lives := make(map[string]interface{})
for _, v := range lv {
lives[v.ID] = v.String()
}
res.Set(event, "event")
res.Set(field, "field")
res.Set(index, "index")
res.Set(table, "table")
res.Set(lives, "lives")
return []interface{}{res.Data()}, nil
}

View file

@ -1,180 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"testing"
"github.com/surrealdb/surrealdb/util/data"
. "github.com/smartystreets/goconvey/convey"
)
func TestInfo(t *testing.T) {
Convey("Info for namespace", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
DEFINE LOGIN test ON NAMESPACE PASSWORD "test";
DEFINE LOGIN test ON NAMESPACE PASSHASH "$2a$10$mofTMm4nilzRSZuU0GyuCuAgHy2mEPeNRqHZH9ETnh.O1MBjy2PDO";
DEFINE TOKEN test ON NAMESPACE TYPE HS512 VALUE "test";
DEFINE DATABASE test;
INFO FOR NAMESPACE;
REMOVE LOGIN test ON NAMESPACE;
REMOVE TOKEN test ON NAMESPACE;
REMOVE DATABASE test;
INFO FOR NAMESPACE;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 10)
So(res[1].Status, ShouldEqual, "OK")
So(res[2].Status, ShouldEqual, "OK")
So(res[3].Status, ShouldEqual, "OK")
So(res[4].Status, ShouldEqual, "OK")
So(res[5].Status, ShouldEqual, "OK")
So(data.Consume(res[5].Result[0]).Get("login").Data(), ShouldHaveLength, 1)
So(data.Consume(res[5].Result[0]).Get("login.test").Data(), ShouldEqual, `DEFINE LOGIN test ON NAMESPACE PASSHASH "$2a$10$mofTMm4nilzRSZuU0GyuCuAgHy2mEPeNRqHZH9ETnh.O1MBjy2PDO"`)
So(data.Consume(res[5].Result[0]).Get("token").Data(), ShouldHaveLength, 1)
So(data.Consume(res[5].Result[0]).Get("token.test").Data(), ShouldEqual, `DEFINE TOKEN test ON NAMESPACE TYPE HS512 VALUE "test"`)
So(data.Consume(res[5].Result[0]).Get("database").Data(), ShouldHaveLength, 1)
So(data.Consume(res[5].Result[0]).Get("database.test").Data(), ShouldEqual, "DEFINE DATABASE test")
So(res[6].Status, ShouldEqual, "OK")
So(res[7].Status, ShouldEqual, "OK")
So(res[8].Status, ShouldEqual, "OK")
So(res[9].Status, ShouldEqual, "OK")
So(data.Consume(res[9].Result[0]).Get("login").Data(), ShouldHaveLength, 0)
So(data.Consume(res[9].Result[0]).Get("token").Data(), ShouldHaveLength, 0)
So(data.Consume(res[9].Result[0]).Get("database").Data(), ShouldHaveLength, 0)
})
Convey("Info for database", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
DEFINE LOGIN test ON DATABASE PASSWORD "test";
DEFINE LOGIN test ON DATABASE PASSHASH "$2a$10$mofTMm4nilzRSZuU0GyuCuAgHy2mEPeNRqHZH9ETnh.O1MBjy2PDO";
DEFINE TOKEN test ON DATABASE TYPE HS512 VALUE "test";
DEFINE SCOPE test;
DEFINE TABLE test;
INFO FOR DATABASE;
REMOVE LOGIN test ON DATABASE;
REMOVE TOKEN test ON DATABASE;
REMOVE SCOPE test;
REMOVE TABLE test;
INFO FOR DATABASE;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 12)
So(res[1].Status, ShouldEqual, "OK")
So(res[2].Status, ShouldEqual, "OK")
So(res[3].Status, ShouldEqual, "OK")
So(res[4].Status, ShouldEqual, "OK")
So(res[5].Status, ShouldEqual, "OK")
So(res[6].Status, ShouldEqual, "OK")
So(data.Consume(res[6].Result[0]).Get("login").Data(), ShouldHaveLength, 1)
So(data.Consume(res[6].Result[0]).Get("login.test").Data(), ShouldEqual, `DEFINE LOGIN test ON DATABASE PASSHASH "$2a$10$mofTMm4nilzRSZuU0GyuCuAgHy2mEPeNRqHZH9ETnh.O1MBjy2PDO"`)
So(data.Consume(res[6].Result[0]).Get("token").Data(), ShouldHaveLength, 1)
So(data.Consume(res[6].Result[0]).Get("token.test").Data(), ShouldEqual, `DEFINE TOKEN test ON DATABASE TYPE HS512 VALUE "test"`)
So(data.Consume(res[6].Result[0]).Get("scope").Data(), ShouldHaveLength, 1)
So(data.Consume(res[6].Result[0]).Get("scope.test").Data(), ShouldEqual, "DEFINE SCOPE test")
So(data.Consume(res[6].Result[0]).Get("table").Data(), ShouldHaveLength, 1)
So(data.Consume(res[6].Result[0]).Get("table.test").Data(), ShouldEqual, "DEFINE TABLE test")
So(res[7].Status, ShouldEqual, "OK")
So(res[8].Status, ShouldEqual, "OK")
So(res[9].Status, ShouldEqual, "OK")
So(res[10].Status, ShouldEqual, "OK")
So(res[11].Status, ShouldEqual, "OK")
So(data.Consume(res[11].Result[0]).Get("login").Data(), ShouldHaveLength, 0)
So(data.Consume(res[11].Result[0]).Get("token").Data(), ShouldHaveLength, 0)
So(data.Consume(res[11].Result[0]).Get("scope").Data(), ShouldHaveLength, 0)
So(data.Consume(res[11].Result[0]).Get("table").Data(), ShouldHaveLength, 0)
})
Convey("Info for scope", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
DEFINE SCOPE test;
DEFINE TOKEN test ON SCOPE test TYPE HS512 VALUE "test";
INFO FOR SCOPE test;
REMOVE TOKEN test ON SCOPE test;
INFO FOR SCOPE test;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 6)
So(res[1].Status, ShouldEqual, "OK")
So(res[2].Status, ShouldEqual, "OK")
So(data.Consume(res[3].Result[0]).Get("token").Data(), ShouldHaveLength, 1)
So(data.Consume(res[3].Result[0]).Get("token.test").Data(), ShouldEqual, `DEFINE TOKEN test ON SCOPE test TYPE HS512 VALUE "test"`)
So(res[4].Status, ShouldEqual, "OK")
So(res[5].Status, ShouldEqual, "OK")
So(data.Consume(res[5].Result[0]).Get("token").Data(), ShouldHaveLength, 0)
})
Convey("Info for table", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
DEFINE EVENT test ON test WHEN true THEN (CREATE test);
DEFINE FIELD test ON test;
DEFINE INDEX test ON test COLUMNS id;
INFO FOR TABLE test;
REMOVE EVENT test ON test;
REMOVE FIELD test ON test;
REMOVE INDEX test ON test;
INFO FOR TABLE test;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 9)
So(res[1].Status, ShouldEqual, "OK")
So(res[2].Status, ShouldEqual, "OK")
So(res[3].Status, ShouldEqual, "OK")
So(res[4].Status, ShouldEqual, "OK")
So(data.Consume(res[4].Result[0]).Get("event").Data(), ShouldHaveLength, 1)
So(data.Consume(res[4].Result[0]).Get("event.test").Data(), ShouldEqual, "DEFINE EVENT test ON test WHEN true THEN (CREATE test)")
So(data.Consume(res[4].Result[0]).Get("field").Data(), ShouldHaveLength, 1)
So(data.Consume(res[4].Result[0]).Get("field.test").Data(), ShouldEqual, "DEFINE FIELD test ON test")
So(data.Consume(res[4].Result[0]).Get("index").Data(), ShouldHaveLength, 1)
So(data.Consume(res[4].Result[0]).Get("index.test").Data(), ShouldEqual, "DEFINE INDEX test ON test COLUMNS id")
So(res[5].Status, ShouldEqual, "OK")
So(res[6].Status, ShouldEqual, "OK")
So(res[7].Status, ShouldEqual, "OK")
So(res[8].Status, ShouldEqual, "OK")
So(data.Consume(res[8].Result[0]).Get("event").Data(), ShouldHaveLength, 0)
So(data.Consume(res[8].Result[0]).Get("field").Data(), ShouldHaveLength, 0)
So(data.Consume(res[8].Result[0]).Get("index").Data(), ShouldHaveLength, 0)
})
}

View file

@ -1,146 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"fmt"
"context"
"github.com/surrealdb/surrealdb/cnf"
"github.com/surrealdb/surrealdb/sql"
"github.com/surrealdb/surrealdb/util/data"
"github.com/surrealdb/surrealdb/util/keys"
)
func (e *executor) executeInsert(ctx context.Context, stm *sql.InsertStatement) ([]interface{}, error) {
if err := e.access(ctx, cnf.AuthNO); err != nil {
return nil, err
}
data, err := e.fetch(ctx, stm.Data, nil)
if err != nil {
return nil, err
}
i := newIterator(e, ctx, stm, false)
switch data := data.(type) {
default:
return nil, fmt.Errorf("Can not execute INSERT query using value '%v'", data)
case []interface{}:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db, TB: stm.Into.TB}
i.processArray(ctx, key, data)
case map[string]interface{}:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db, TB: stm.Into.TB}
i.processArray(ctx, key, []interface{}{data})
}
return i.Yield(ctx)
}
func (e *executor) fetchInsert(ctx context.Context, stm *sql.InsertStatement, doc *data.Doc) (interface{}, error) {
ctx = dive(ctx)
if doc != nil {
vars := data.New()
vars.Set(doc.Data(), varKeyParent)
if subs := ctx.Value(ctxKeySubs); subs != nil {
if subs, ok := subs.(*data.Doc); ok {
vars.Set(subs.Get(varKeyParents).Data(), varKeyParents)
}
} else {
vars.Array(varKeyParents)
}
vars.Append(doc.Data(), varKeyParents)
ctx = context.WithValue(ctx, ctxKeySubs, vars)
}
out, err := e.executeInsert(ctx, stm)
if err != nil {
return nil, err
}
switch len(out) {
case 1:
return data.Consume(out).Get(docKeyOne, docKeyId).Data(), nil
default:
return data.Consume(out).Get(docKeyAll, docKeyId).Data(), nil
}
}
func (d *document) runInsert(ctx context.Context, stm *sql.InsertStatement) (interface{}, error) {
var ok bool
var err error
var met = _CREATE
if err = d.init(ctx); err != nil {
return nil, err
}
if err = d.lock(ctx); err != nil {
return nil, err
}
if err = d.setup(ctx); err != nil {
return nil, err
}
if d.val.Exi() == true {
return nil, &ExistError{exist: d.id}
}
if err = d.merge(ctx, met, nil); err != nil {
return nil, err
}
if ok, err = d.allow(ctx, met); err != nil {
return nil, err
} else if ok == false {
return nil, nil
}
if err = d.storeIndex(ctx); err != nil {
return nil, err
}
if err = d.storeThing(ctx); err != nil {
return nil, err
}
if err = d.table(ctx, met); err != nil {
return nil, err
}
if err = d.lives(ctx, met); err != nil {
return nil, err
}
if err = d.event(ctx, met); err != nil {
return nil, err
}
return d.yield(ctx, stm, stm.Echo)
}

View file

@ -1,242 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"testing"
"github.com/surrealdb/surrealdb/sql"
"github.com/surrealdb/surrealdb/util/data"
. "github.com/smartystreets/goconvey/convey"
)
func TestInsert(t *testing.T) {
Convey("Insert with invalid value", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
INSERT 1 INTO user;
INSERT "one" INTO user;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 3)
So(res[1].Status, ShouldEqual, "ERR")
So(res[1].Detail, ShouldEqual, "Can not execute INSERT query using value '1'")
So(res[2].Status, ShouldEqual, "ERR")
So(res[2].Detail, ShouldEqual, "Can not execute INSERT query using value 'one'")
})
Convey("Insert a set of ids from one table to another table", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE person:1 SET test="one";
CREATE person:2 SET test="two";
CREATE person:3 SET test="tre";
INSERT (SELECT id FROM person) INTO user;
SELECT * FROM person, user;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 6)
So(res[1].Result, ShouldHaveLength, 1)
So(res[2].Result, ShouldHaveLength, 1)
So(res[3].Result, ShouldHaveLength, 1)
So(res[4].Result, ShouldHaveLength, 3)
So(data.Consume(res[4].Result[0]).Get("test").Data(), ShouldEqual, nil)
So(data.Consume(res[4].Result[0]).Get("id").Data(), ShouldResemble, sql.NewThing("user", 1))
So(data.Consume(res[4].Result[1]).Get("test").Data(), ShouldEqual, nil)
So(data.Consume(res[4].Result[1]).Get("id").Data(), ShouldResemble, sql.NewThing("user", 2))
So(data.Consume(res[4].Result[2]).Get("test").Data(), ShouldEqual, nil)
So(data.Consume(res[4].Result[2]).Get("id").Data(), ShouldResemble, sql.NewThing("user", 3))
So(res[5].Result, ShouldHaveLength, 6)
So(data.Consume(res[5].Result[0]).Get("test").Data(), ShouldEqual, "one")
So(data.Consume(res[5].Result[0]).Get("id").Data(), ShouldResemble, sql.NewThing("person", 1))
So(data.Consume(res[5].Result[1]).Get("test").Data(), ShouldEqual, "two")
So(data.Consume(res[5].Result[1]).Get("id").Data(), ShouldResemble, sql.NewThing("person", 2))
So(data.Consume(res[5].Result[2]).Get("test").Data(), ShouldEqual, "tre")
So(data.Consume(res[5].Result[2]).Get("id").Data(), ShouldResemble, sql.NewThing("person", 3))
So(data.Consume(res[5].Result[3]).Get("test").Data(), ShouldEqual, nil)
So(data.Consume(res[5].Result[3]).Get("id").Data(), ShouldResemble, sql.NewThing("user", 1))
So(data.Consume(res[5].Result[4]).Get("test").Data(), ShouldEqual, nil)
So(data.Consume(res[5].Result[4]).Get("id").Data(), ShouldResemble, sql.NewThing("user", 2))
So(data.Consume(res[5].Result[5]).Get("test").Data(), ShouldEqual, nil)
So(data.Consume(res[5].Result[5]).Get("id").Data(), ShouldResemble, sql.NewThing("user", 3))
})
Convey("Insert a set of records from one table to another table", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE person:1 SET test="one";
CREATE person:2 SET test="two";
CREATE person:3 SET test="tre";
INSERT (SELECT * FROM person) INTO user;
SELECT * FROM person, user;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 6)
So(res[1].Result, ShouldHaveLength, 1)
So(res[2].Result, ShouldHaveLength, 1)
So(res[3].Result, ShouldHaveLength, 1)
So(res[4].Result, ShouldHaveLength, 3)
So(data.Consume(res[4].Result[0]).Get("test").Data(), ShouldEqual, "one")
So(data.Consume(res[4].Result[0]).Get("id").Data(), ShouldResemble, sql.NewThing("user", 1))
So(data.Consume(res[4].Result[1]).Get("test").Data(), ShouldEqual, "two")
So(data.Consume(res[4].Result[1]).Get("id").Data(), ShouldResemble, sql.NewThing("user", 2))
So(data.Consume(res[4].Result[2]).Get("test").Data(), ShouldEqual, "tre")
So(data.Consume(res[4].Result[2]).Get("id").Data(), ShouldResemble, sql.NewThing("user", 3))
So(res[5].Result, ShouldHaveLength, 6)
So(data.Consume(res[5].Result[0]).Get("test").Data(), ShouldEqual, "one")
So(data.Consume(res[5].Result[0]).Get("id").Data(), ShouldResemble, sql.NewThing("person", 1))
So(data.Consume(res[5].Result[1]).Get("test").Data(), ShouldEqual, "two")
So(data.Consume(res[5].Result[1]).Get("id").Data(), ShouldResemble, sql.NewThing("person", 2))
So(data.Consume(res[5].Result[2]).Get("test").Data(), ShouldEqual, "tre")
So(data.Consume(res[5].Result[2]).Get("id").Data(), ShouldResemble, sql.NewThing("person", 3))
So(data.Consume(res[5].Result[3]).Get("test").Data(), ShouldEqual, "one")
So(data.Consume(res[5].Result[3]).Get("id").Data(), ShouldResemble, sql.NewThing("user", 1))
So(data.Consume(res[5].Result[4]).Get("test").Data(), ShouldEqual, "two")
So(data.Consume(res[5].Result[4]).Get("id").Data(), ShouldResemble, sql.NewThing("user", 2))
So(data.Consume(res[5].Result[5]).Get("test").Data(), ShouldEqual, "tre")
So(data.Consume(res[5].Result[5]).Get("id").Data(), ShouldResemble, sql.NewThing("user", 3))
})
Convey("Insert a set of records from data with an ID", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
LET data = { "id": 360, "admin":true, "login":"joe" };
INSERT $data INTO users;
INSERT $data INTO users;
SELECT * FROM users;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 5)
So(res[2].Result, ShouldHaveLength, 1)
So(data.Consume(res[2].Result[0]).Get("login").Data(), ShouldEqual, "joe")
So(data.Consume(res[2].Result[0]).Get("meta.id").Data(), ShouldEqual, 360)
So(res[3].Status, ShouldEqual, "ERR_EX")
So(res[3].Detail, ShouldEqual, "Database record 'users:360' already exists")
So(res[4].Result, ShouldHaveLength, 1)
So(data.Consume(res[4].Result[0]).Get("login").Data(), ShouldEqual, "joe")
So(data.Consume(res[4].Result[0]).Get("meta.id").Data(), ShouldEqual, 360)
})
Convey("Insert a set of records from data without an ID", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
LET data = { "admin":true, "login":"tom" };
INSERT $data INTO users;
SELECT * FROM users;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[2].Result, ShouldHaveLength, 1)
So(data.Consume(res[2].Result[0]).Get("login").Data(), ShouldEqual, "tom")
So(data.Consume(res[2].Result[0]).Get("meta.id").Data(), ShouldHaveLength, 20)
So(res[3].Result, ShouldHaveLength, 1)
So(data.Consume(res[3].Result[0]).Get("login").Data(), ShouldEqual, "tom")
So(data.Consume(res[3].Result[0]).Get("meta.id").Data(), ShouldHaveLength, 20)
})
Convey("Insert a set of records from an array of data with IDs", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
LET data = [
{ "id": 360, "admin":true, "login":"joe" },
{ "id": 200, "admin":false, "login":"mike" },
{ "id": "test", "admin":false, "login":"tester" },
];
INSERT $data INTO users;
INSERT $data INTO users;
SELECT * FROM users;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 5)
So(res[2].Result, ShouldHaveLength, 3)
So(data.Consume(res[2].Result[0]).Get("login").Data(), ShouldEqual, "joe")
So(data.Consume(res[2].Result[0]).Get("meta.id").Data(), ShouldEqual, 360)
So(data.Consume(res[2].Result[1]).Get("login").Data(), ShouldEqual, "mike")
So(data.Consume(res[2].Result[1]).Get("meta.id").Data(), ShouldEqual, 200)
So(data.Consume(res[2].Result[2]).Get("login").Data(), ShouldEqual, "tester")
So(data.Consume(res[2].Result[2]).Get("meta.id").Data(), ShouldEqual, "test")
So(res[3].Status, ShouldEqual, "ERR_EX")
So(res[4].Result, ShouldHaveLength, 3)
So(data.Consume(res[4].Result[0]).Get("login").Data(), ShouldEqual, "mike")
So(data.Consume(res[4].Result[0]).Get("meta.id").Data(), ShouldEqual, 200)
So(data.Consume(res[4].Result[1]).Get("login").Data(), ShouldEqual, "joe")
So(data.Consume(res[4].Result[1]).Get("meta.id").Data(), ShouldEqual, 360)
So(data.Consume(res[4].Result[2]).Get("login").Data(), ShouldEqual, "tester")
So(data.Consume(res[4].Result[2]).Get("meta.id").Data(), ShouldEqual, "test")
})
Convey("Insert a set of records from an array of data without IDs", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
LET data = [
{ "admin":true, "login":"tom" },
];
INSERT $data INTO users;
SELECT * FROM users;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[2].Result, ShouldHaveLength, 1)
So(data.Consume(res[2].Result[0]).Get("login").Data(), ShouldEqual, "tom")
So(data.Consume(res[2].Result[0]).Get("meta.id").Data(), ShouldHaveLength, 20)
So(res[3].Result, ShouldHaveLength, 1)
So(data.Consume(res[3].Result[0]).Get("login").Data(), ShouldEqual, "tom")
So(data.Consume(res[3].Result[0]).Get("meta.id").Data(), ShouldHaveLength, 20)
})
}

File diff suppressed because it is too large Load diff

View file

@ -1,43 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"context"
"github.com/surrealdb/surrealdb/sql"
"github.com/surrealdb/surrealdb/util/data"
)
func (e *executor) executeLet(ctx context.Context, stm *sql.LetStatement) (out []interface{}, err error) {
var vars = ctx.Value(ctxKeyVars).(*data.Doc)
switch what := stm.What.(type) {
case *sql.Void:
vars.Del(stm.Name.VA)
case *sql.Empty:
vars.Del(stm.Name.VA)
default:
val, err := e.fetch(ctx, what, nil)
if err != nil {
return nil, err
}
vars.Set(val, stm.Name.VA)
}
return
}

View file

@ -1,86 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestLet(t *testing.T) {
Convey("Let to create a new variable", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
LET temp = "test";
RETURN $temp;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 3)
So(res[1].Status, ShouldEqual, "OK")
So(res[2].Status, ShouldEqual, "OK")
So(res[2].Result, ShouldResemble, []interface{}{"test"})
})
Convey("Let to create and VOID a variable", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
LET temp = "test";
LET temp = VOID;
RETURN $temp;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[1].Status, ShouldEqual, "OK")
So(res[2].Status, ShouldEqual, "OK")
So(res[3].Status, ShouldEqual, "OK")
So(res[3].Result, ShouldResemble, []interface{}{nil})
})
Convey("Let to create and EMPTY a variable", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
LET temp = "test";
LET temp = EMPTY;
RETURN $temp;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[1].Status, ShouldEqual, "OK")
So(res[2].Status, ShouldEqual, "OK")
So(res[3].Status, ShouldEqual, "OK")
So(res[3].Result, ShouldResemble, []interface{}{nil})
})
}

View file

@ -1,78 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"sync"
"context"
"github.com/surrealdb/fibre"
"github.com/surrealdb/surrealdb/cnf"
"github.com/surrealdb/surrealdb/sql"
)
var sockets sync.Map
func register(fib *fibre.Context, id string) func() {
return func() {
sockets.Store(id, &socket{
fibre: fib,
sends: make(map[string][]interface{}),
items: make(map[string][]interface{}),
lives: make(map[string]*sql.LiveStatement),
})
}
}
func deregister(fib *fibre.Context, id string) func() {
return func() {
if sck, ok := sockets.Load(id); ok {
sck.(*socket).deregister(id)
}
}
}
func (e *executor) executeLive(ctx context.Context, stm *sql.LiveStatement) (out []interface{}, err error) {
if err := e.access(ctx, cnf.AuthNO); err != nil {
return nil, err
}
if sck, ok := sockets.Load(e.id); ok {
return sck.(*socket).executeLive(e, ctx, stm)
}
return nil, &LiveError{}
}
func (e *executor) executeKill(ctx context.Context, stm *sql.KillStatement) (out []interface{}, err error) {
if err := e.access(ctx, cnf.AuthNO); err != nil {
return nil, err
}
if sck, ok := sockets.Load(e.id); ok {
return sck.(*socket).executeKill(e, ctx, stm)
}
return nil, &LiveError{}
}

View file

@ -1,139 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"context"
"github.com/surrealdb/surrealdb/sql"
)
// Lives checks if any table views are specified for
// this table, and executes them in name order.
func (d *document) lives(ctx context.Context, when method) (err error) {
// Check if this query has been run
// in forced mode, because of an
// index or foreign table update.
forced := d.forced(ctx)
// If this document has not changed
// then there is no need to update
// any registered live queries.
if !forced && !d.changed {
return nil
}
// Get the foreign read-only tables
// specified for this table, and
// update values which have changed.
lvs, err := d.i.e.tx.AllLV(ctx, d.key.NS, d.key.DB, d.key.TB)
if err != nil {
return err
}
// Loop over the currently running
// live queries so that we can pass
// change notifications to the socket.
for _, lv := range lvs {
// Check whether the change was made by
// the same connection as the live query,
// and if it is then don't notify changes.
if d.i.e.id == lv.FB {
continue
}
// Load the socket which owns the live
// query so that we can check the socket
// permissions, and send the notifications.
if sck, ok := sockets.Load(lv.FB); ok {
var out interface{}
// Create a new context for this socket
// which has the correct connection
// variables, and auth levels.
ctx = sck.(*socket).ctx()
// Check whether this live query has the
// necessary permissions to view this
// document, or continue to the next query.
ok, err = d.grant(ctx, when)
if err != nil {
continue
} else if !ok {
continue
}
// Check whether this document matches the
// filter conditions for the live query and
// if not, then continue to the next query.
ok, err = d.check(ctx, lv.Cond)
if err != nil {
continue
} else if !ok {
continue
}
switch lv.Diff {
// If the live query has specified to only
// receive diff changes, then there will be
// no projected fields for this query.
case true:
out, _ = d.yield(ctx, lv, sql.DIFF)
// If the query has projected fields which it
// wants to receive, then let's fetch these
// fields, and return them to the socket.
case false:
out, _ = d.yield(ctx, lv, sql.ILLEGAL)
}
switch when {
case _DELETE:
sck.(*socket).queue(d.i.e.id, lv.ID, "DELETE", d.id)
case _CREATE:
if out != nil {
sck.(*socket).queue(d.i.e.id, lv.ID, "CREATE", out)
}
case _UPDATE:
if out != nil {
sck.(*socket).queue(d.i.e.id, lv.ID, "UPDATE", out)
}
}
}
}
return
}

View file

@ -1,444 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"sort"
"context"
"github.com/surrealdb/surrealdb/cnf"
"github.com/surrealdb/surrealdb/sql"
"github.com/surrealdb/surrealdb/util/conv"
"github.com/surrealdb/surrealdb/util/data"
"github.com/surrealdb/surrealdb/util/diff"
)
var main = map[string]struct{}{
"id": {},
"meta": {},
"meta.tb": {},
"meta.id": {},
}
func (d *document) merge(ctx context.Context, met method, data sql.Expr) (err error) {
if err = d.defDoc(ctx, met); err != nil {
return
}
if err = d.defFld(ctx, met); err != nil {
return
}
switch expr := data.(type) {
case *sql.DataExpression:
if err = d.mrgSet(ctx, met, expr); err != nil {
return err
}
case *sql.DiffExpression:
if err = d.mrgDpm(ctx, met, expr); err != nil {
return err
}
case *sql.MergeExpression:
if err = d.mrgAny(ctx, met, expr); err != nil {
return err
}
case *sql.ContentExpression:
if err = d.mrgAll(ctx, met, expr); err != nil {
return err
}
}
if err = d.defFld(ctx, met); err != nil {
return
}
if err = d.mrgFld(ctx, met); err != nil {
return
}
if err = d.defFld(ctx, met); err != nil {
return
}
if err = d.delFld(ctx, met); err != nil {
return
}
d.changed = d.hasChanged(ctx)
return
}
func (d *document) defDoc(ctx context.Context, met method) (err error) {
d.current = d.current.Copy()
return
}
func (d *document) defFld(ctx context.Context, met method) (err error) {
switch d.i.vir {
case true:
d.current.Set(d.id, "id")
d.current.Set(d.id.TB, "meta.tb")
d.current.Set(d.id.ID, "meta.id")
case false:
d.current.Del("meta")
d.current.Set(d.id, "id")
d.current.Set(d.id.TB, "meta.tb")
d.current.Set(d.id.ID, "meta.id")
}
return
}
func (d *document) delFld(ctx context.Context, met method) (err error) {
tb, err := d.i.e.tx.GetTB(ctx, d.key.NS, d.key.DB, d.key.TB)
if err != nil {
return err
}
if tb.Full {
var keys = map[string]struct{}{}
// Get the defined fields
fds, err := d.i.e.tx.AllFD(ctx, d.key.NS, d.key.DB, d.key.TB)
if err != nil {
return err
}
// Loop over the allowed keys
for _, fd := range fds {
d.current.Walk(func(key string, val interface{}, ok bool) (err error) {
keys[key] = struct{}{}
return
}, fd.Name.VA)
}
// Delete any keys which aren't allowed
d.current.Each(func(key string, val interface{}) (err error) {
if _, ok := main[key]; !ok {
if _, ok := keys[key]; !ok {
d.current.Del(key)
}
}
return
})
}
return
}
func (d *document) mrgAll(ctx context.Context, met method, expr *sql.ContentExpression) (err error) {
var obj map[string]interface{}
switch v := expr.Data.(type) {
case map[string]interface{}:
obj = v
case *sql.Param:
p, err := d.i.e.fetch(ctx, v, d.current)
if err != nil {
return err
}
switch v := p.(type) {
case map[string]interface{}:
obj = v
}
}
d.current.Reset()
for k, v := range obj {
d.current.Set(v, k)
}
return
}
func (d *document) mrgAny(ctx context.Context, met method, expr *sql.MergeExpression) (err error) {
var obj map[string]interface{}
switch v := expr.Data.(type) {
case map[string]interface{}:
obj = v
case *sql.Param:
p, err := d.i.e.fetch(ctx, v, d.current)
if err != nil {
return err
}
switch v := p.(type) {
case map[string]interface{}:
obj = v
}
}
for k, v := range obj {
d.current.Set(v, k)
}
return
}
func (d *document) mrgDpm(ctx context.Context, met method, expr *sql.DiffExpression) (err error) {
var obj []interface{}
var old map[string]interface{}
var now map[string]interface{}
switch v := expr.Data.(type) {
case []interface{}:
obj = v
case *sql.Param:
p, err := d.i.e.fetch(ctx, v, d.current)
if err != nil {
return err
}
switch v := p.(type) {
case []interface{}:
obj = v
}
}
old = d.current.Data().(map[string]interface{})
now = diff.Patch(old, obj)
d.current = data.Consume(now)
return
}
func (d *document) mrgSet(ctx context.Context, met method, expr *sql.DataExpression) (err error) {
for _, v := range expr.Data {
if i, ok := v.LHS.(*sql.Ident); ok {
n, err := d.i.e.fetch(ctx, v.RHS, d.current)
if err != nil {
return err
}
switch v.Op {
case sql.EQ:
switch n.(type) {
default:
d.current.Set(n, i.VA)
case *sql.Void:
d.current.Del(i.VA)
}
case sql.INC:
d.current.Inc(n, i.VA)
case sql.DEC:
d.current.Dec(n, i.VA)
}
}
}
return
}
func (d *document) mrgFld(ctx context.Context, met method) (err error) {
fds, err := d.i.e.tx.AllFD(ctx, d.key.NS, d.key.DB, d.key.TB)
if err != nil {
return err
}
// Sort the fields according to their
// priority so that fields which depend
// on another field can be processed
// after that field in a specific order.
sort.Slice(fds, func(i, j int) bool {
return fds[i].Priority < fds[j].Priority
})
// Loop through each field and check to
// see if it might be a specific type.
// This is because when updating records
// using json, there is no specific type
// for a 'datetime' and 'record'.
d.current.Each(func(key string, val interface{}) (err error) {
if val, ok := conv.MightBe(val); ok {
d.current.Iff(val, key)
}
return nil
})
// Loop over each of the defined fields
// and process them fully, applying the
// VALUE and ASSERT clauses sequentially.
for _, fd := range fds {
err = d.current.Walk(func(key string, val interface{}, exi bool) error {
var old = d.initial.Get(key).Data()
// Ensure object and arrays are set
val = conv.MustBe(fd.Type, val)
// We are setting the value of the field
if fd.Value != nil && d.i.e.opts.fields {
// Reset the variables
vars := data.New()
vars.Set(val, varKeyValue)
vars.Set(val, varKeyAfter)
vars.Set(old, varKeyBefore)
ctx = context.WithValue(ctx, ctxKeySpec, vars)
if now, err := d.i.e.fetch(ctx, fd.Value, d.current); err != nil {
return err
} else {
val = now
}
}
// Ensure the field is the correct type
if val != nil {
if now, err := conv.ConvertTo(fd.Type, fd.Kind, val); err != nil {
val = nil
} else {
val = now
}
}
// We are checking the value of the field
if fd.Assert != nil && d.i.e.opts.fields {
// Reset the variables
vars := data.New()
vars.Set(val, varKeyValue)
vars.Set(val, varKeyAfter)
vars.Set(old, varKeyBefore)
ctx = context.WithValue(ctx, ctxKeySpec, vars)
if chk, err := d.i.e.fetch(ctx, fd.Assert, d.current); err != nil {
return err
} else if chk, ok := chk.(bool); ok && !chk {
return &FieldError{field: key, found: val, check: fd.Assert}
}
}
// We are checking the permissions of the field
if fd.Perms != nil && perm(ctx) > cnf.AuthDB {
// Reset the variables
vars := data.New()
vars.Set(val, varKeyValue)
vars.Set(val, varKeyAfter)
vars.Set(old, varKeyBefore)
ctx = context.WithValue(ctx, ctxKeySpec, vars)
switch p := fd.Perms.(type) {
case *sql.PermExpression:
switch met {
case _CREATE:
if v, err := d.i.e.fetch(ctx, p.Create, d.current); err != nil {
return err
} else {
if b, ok := v.(bool); !ok || !b {
val = old
}
}
case _UPDATE:
if v, err := d.i.e.fetch(ctx, p.Update, d.current); err != nil {
return err
} else {
if b, ok := v.(bool); !ok || !b {
val = old
}
}
case _DELETE:
if v, err := d.i.e.fetch(ctx, p.Delete, d.current); err != nil {
return err
} else {
if b, ok := v.(bool); !ok || !b {
val = old
}
}
}
}
}
// We are setting the value of the field
switch val.(type) {
default:
if exi {
d.current.Set(val, key)
} else {
d.current.Iff(val, key)
}
case *sql.Void:
d.current.Del(key)
}
return nil
}, fd.Name.VA)
if err != nil {
return
}
}
return
}

View file

@ -1,88 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"context"
"fmt"
"sync"
"sync/atomic"
)
type mutex struct {
m sync.Map
}
type value struct {
v uint32
q chan struct{}
l chan struct{}
}
func (m *mutex) Lock(ctx context.Context, key fmt.Stringer) {
_, v := m.item(ctx, key)
select {
case <-ctx.Done():
return
case <-v.q:
return
case v.l <- struct{}{}:
atomic.StoreUint32(&v.v, vers(ctx))
return
default:
if atomic.LoadUint32(&v.v) < vers(ctx) {
close(v.q)
panic(errRaceCondition)
}
}
select {
case <-ctx.Done():
return
case <-v.q:
return
case v.l <- struct{}{}:
atomic.StoreUint32(&v.v, vers(ctx))
return
}
}
func (m *mutex) Unlock(ctx context.Context, key fmt.Stringer) {
_, v := m.item(ctx, key)
select {
case <-ctx.Done():
return
case <-v.q:
return
case <-v.l:
return
}
}
func (m *mutex) item(ctx context.Context, key fmt.Stringer) (string, *value) {
k := key.String()
v, _ := m.m.LoadOrStore(k, &value{
v: vers(ctx),
q: make(chan struct{}),
l: make(chan struct{}, 1),
})
return k, v.(*value)
}

View file

@ -1,386 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"context"
"sync"
"testing"
"github.com/surrealdb/surrealdb/util/data"
. "github.com/smartystreets/goconvey/convey"
)
type stringer struct{}
func (this stringer) String() string {
return "test"
}
func TestMutex(t *testing.T) {
var n = 10
Convey("Context diving works correctly", t, func() {
ctx := context.Background()
So(vers(ctx), ShouldEqual, 0)
for i := vers(ctx); i <= maxRecursiveQueries; i++ {
So(func() { ctx = dive(ctx) }, ShouldNotPanic)
So(vers(ctx), ShouldEqual, i+1)
}
So(func() { dive(ctx) }, ShouldPanicWith, errRecursiveOverload)
})
Convey("Allow basic mutex", t, func() {
m := new(mutex)
ctx := context.Background()
m.Lock(ctx, new(stringer))
m.Unlock(ctx, new(stringer))
})
Convey("Allow concurrent mutex", t, func() {
m := new(mutex)
wg := new(sync.WaitGroup)
ctx := context.Background()
wg.Add(n)
for i := 0; i < n; i++ {
go func() {
defer wg.Done()
m.Lock(ctx, new(stringer))
m.Unlock(ctx, new(stringer))
}()
}
wg.Wait()
So(nil, ShouldBeNil)
})
Convey("Allow fixed-level mutex", t, func() {
m := new(mutex)
ctx := context.Background()
for i := 0; i < n; i++ {
ctx = dive(ctx)
So(func() { m.Lock(ctx, new(stringer)) }, ShouldNotPanic)
So(func() { m.Unlock(ctx, new(stringer)) }, ShouldNotPanic)
}
So(nil, ShouldBeNil)
})
Convey("Prevent nested-recursive mutex", t, func() {
m := new(mutex)
ctx := context.Background()
m.Lock(ctx, new(stringer))
ctx = dive(ctx)
So(func() { m.Lock(ctx, new(stringer)) }, ShouldPanic)
So(func() { m.Unlock(ctx, new(stringer)) }, ShouldNotPanic)
So(func() { m.Unlock(ctx, new(stringer)) }, ShouldNotPanic)
So(nil, ShouldBeNil)
})
Convey("Ensure document locking when multiple events attempt to write to the same document", t, func() {
setupDB(workerCount)
txt := `
USE NS test DB test;
DEFINE EVENT created ON person WHEN $method = "CREATE" THEN (UPDATE $after.fk SET fks += $this);
DEFINE EVENT deleted ON person WHEN $method = "DELETE" THEN (UPDATE $before.fk SET fks -= $this);
UPDATE |person:1..100| SET fk = other:test;
SELECT * FROM other;
DELETE FROM person;
SELECT * FROM other;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 7)
So(res[1].Status, ShouldEqual, "OK")
So(res[2].Status, ShouldEqual, "OK")
So(res[3].Status, ShouldEqual, "OK")
So(res[3].Result, ShouldHaveLength, 100)
So(res[4].Status, ShouldEqual, "OK")
So(res[4].Result, ShouldHaveLength, 1)
So(data.Consume(res[4].Result[0]).Get("fks").Data(), ShouldHaveLength, 100)
So(res[5].Status, ShouldEqual, "OK")
So(res[5].Result, ShouldHaveLength, 0)
So(res[6].Status, ShouldEqual, "OK")
So(res[6].Result, ShouldHaveLength, 1)
So(data.Consume(res[6].Result[0]).Get("fks").Data(), ShouldHaveLength, 0)
})
Convey("Ability to select the same document in a SELECT subquery", t, func() {
setupDB(workerCount)
txt := `
USE NS test DB test;
CREATE person:test;
SELECT * FROM (SELECT * FROM (SELECT * FROM person));
SELECT * FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[1].Status, ShouldEqual, "OK")
So(res[1].Result, ShouldHaveLength, 1)
So(res[2].Status, ShouldEqual, "OK")
So(res[3].Status, ShouldEqual, "OK")
So(res[3].Result, ShouldHaveLength, 1)
})
Convey("Ability to update the same document in a SELECT subquery", t, func() {
setupDB(workerCount)
txt := `
USE NS test DB test;
CREATE person:test;
SELECT * FROM (UPDATE person SET test=true);
SELECT * FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[1].Status, ShouldEqual, "OK")
So(res[1].Result, ShouldHaveLength, 1)
So(res[2].Status, ShouldEqual, "OK")
So(data.Consume(res[2].Result[0]).Get("temp").Data(), ShouldBeNil)
So(res[3].Status, ShouldEqual, "OK")
So(res[3].Result, ShouldHaveLength, 1)
So(data.Consume(res[3].Result[0]).Get("temp").Data(), ShouldBeNil)
So(data.Consume(res[3].Result[0]).Get("test").Data(), ShouldEqual, true)
})
Convey("Ability to update the same document in a SELECT subquery", t, func() {
setupDB(workerCount)
txt := `
USE NS test DB test;
CREATE person:test;
SELECT *, (UPDATE $parent.id SET test=true) AS test FROM person;
SELECT * FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[1].Status, ShouldEqual, "OK")
So(res[1].Result, ShouldHaveLength, 1)
So(res[2].Status, ShouldEqual, "OK")
So(res[2].Result, ShouldHaveLength, 1)
So(res[3].Status, ShouldEqual, "OK")
So(res[3].Result, ShouldHaveLength, 1)
So(data.Consume(res[3].Result[0]).Get("temp").Data(), ShouldBeNil)
So(data.Consume(res[3].Result[0]).Get("test").Data(), ShouldEqual, true)
})
Convey("Inability to update the same document in an UPDATE subquery", t, func() {
setupDB(workerCount)
txt := `
USE NS test DB test;
CREATE person:test;
UPDATE person SET temp = (UPDATE person SET test=true);
SELECT * FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[1].Status, ShouldEqual, "OK")
So(res[1].Result, ShouldHaveLength, 1)
So(res[2].Status, ShouldEqual, "ERR")
So(res[2].Detail, ShouldEqual, "Failed to update the same document recursively")
So(res[3].Status, ShouldEqual, "OK")
So(res[3].Result, ShouldHaveLength, 1)
So(data.Consume(res[3].Result[0]).Get("test").Data(), ShouldBeNil)
So(data.Consume(res[3].Result[0]).Get("temp").Data(), ShouldBeNil)
})
Convey("Ability to update the same document in an event", t, func() {
setupDB(workerCount)
txt := `
USE NS test DB test;
DEFINE EVENT test ON person WHEN $before.test != $after.test THEN (UPDATE $this SET temp = true);
UPDATE person:test SET test=true;
SELECT * FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[1].Status, ShouldEqual, "OK")
So(res[2].Status, ShouldEqual, "OK")
So(data.Consume(res[2].Result[0]).Get("temp").Data(), ShouldBeNil)
So(data.Consume(res[2].Result[0]).Get("test").Data(), ShouldEqual, true)
So(res[3].Status, ShouldEqual, "OK")
So(data.Consume(res[3].Result[0]).Get("temp").Data(), ShouldEqual, true)
So(data.Consume(res[3].Result[0]).Get("test").Data(), ShouldEqual, true)
})
Convey("Subqueries for an event should be on the same level", t, func() {
setupDB(workerCount)
txt := `
USE NS test DB test;
DEFINE EVENT test ON person WHEN $method = "CREATE" THEN (CREATE tester);
CREATE |person:100|;
SELECT * FROM person;
SELECT * FROM tester;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 5)
So(res[1].Status, ShouldEqual, "OK")
So(res[2].Status, ShouldEqual, "OK")
So(res[2].Result, ShouldHaveLength, 100)
So(res[3].Status, ShouldEqual, "OK")
So(res[3].Result, ShouldHaveLength, 100)
So(res[4].Status, ShouldEqual, "OK")
So(res[4].Result, ShouldHaveLength, 100)
})
Convey("Subqueries for an event on a different level create an infinite loop", t, func() {
setupDB(workerCount)
txt := `
USE NS test DB test;
DEFINE EVENT test ON person WHEN $method = "CREATE" THEN (CREATE person);
CREATE person:test;
SELECT * FROM person;
SELECT * FROM tester;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 5)
So(res[1].Status, ShouldEqual, "OK")
So(res[2].Status, ShouldEqual, "ERR")
So(res[2].Detail, ShouldEqual, "Infinite loop when running recursive subqueries")
So(res[3].Status, ShouldEqual, "OK")
So(res[3].Result, ShouldHaveLength, 0)
So(res[4].Status, ShouldEqual, "OK")
So(res[4].Result, ShouldHaveLength, 0)
})
Convey("Subqueries for recursive events on a different level create an infinite loop", t, func() {
setupDB(workerCount)
txt := `
USE NS test DB test;
DEFINE EVENT test ON person WHEN $method = "UPDATE" THEN (UPDATE tester SET temp=time.now());
DEFINE EVENT test ON tester WHEN $method = "UPDATE" THEN (UPDATE person SET temp=time.now());
CREATE person:test, tester:test SET temp=time.now();
UPDATE person:test SET temp=time.now();
SELECT * FROM person;
SELECT * FROM tester;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 7)
So(res[1].Status, ShouldEqual, "OK")
So(res[2].Status, ShouldEqual, "OK")
So(res[3].Status, ShouldEqual, "OK")
So(res[4].Status, ShouldEqual, "ERR")
So(res[4].Detail, ShouldEqual, "Infinite loop when running recursive subqueries")
So(res[5].Status, ShouldEqual, "OK")
So(res[5].Result, ShouldHaveLength, 1)
So(res[6].Status, ShouldEqual, "OK")
So(res[6].Result, ShouldHaveLength, 1)
})
Convey("Ability to define complex dependent events which should run consecutively and succeed", t, func() {
setupDB(workerCount)
txt := `
USE NS test DB test;
CREATE global:test SET tests=[], temps=[];
DEFINE EVENT test ON tester WHEN $after.global != EMPTY THEN (
UPDATE $after.global SET tests+=$this;
UPDATE temper SET tester=$this, global=$after.global;
);
DEFINE EVENT test ON temper WHEN $after.global != EMPTY THEN (
UPDATE $after.global SET temps+=$this;
);
CREATE |temper:1..5|;
CREATE tester:test SET global=global:test;
SELECT * FROM global;
SELECT * FROM tester;
SELECT * FROM temper;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 9)
So(res[1].Status, ShouldEqual, "OK")
So(res[1].Result, ShouldHaveLength, 1)
So(res[2].Status, ShouldEqual, "OK")
So(res[3].Status, ShouldEqual, "OK")
So(res[4].Status, ShouldEqual, "OK")
So(res[4].Result, ShouldHaveLength, 5)
So(res[5].Status, ShouldEqual, "OK")
So(res[5].Result, ShouldHaveLength, 1)
So(res[6].Status, ShouldEqual, "OK")
So(res[6].Result, ShouldHaveLength, 1)
So(res[7].Status, ShouldEqual, "OK")
So(res[7].Result, ShouldHaveLength, 1)
So(res[8].Status, ShouldEqual, "OK")
So(res[8].Result, ShouldHaveLength, 5)
})
}

View file

@ -1,67 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"context"
"strings"
"github.com/surrealdb/surrealdb/cnf"
"github.com/surrealdb/surrealdb/sql"
)
type options struct {
fields bool
events bool
tables bool
}
func newOptions() *options {
return &options{
fields: true,
events: true,
tables: true,
}
}
func (e *executor) executeOpt(ctx context.Context, stm *sql.OptStatement) (out []interface{}, err error) {
if perm(ctx) >= cnf.AuthSC {
return nil, new(QueryError)
}
switch strings.ToUpper(stm.Name) {
case "FIELD_QUERIES":
e.opts.fields = stm.What
case "EVENT_QUERIES":
e.opts.events = stm.What
case "TABLE_QUERIES":
e.opts.tables = stm.What
case "IMPORT":
switch stm.What {
case true:
e.opts.fields = false // Set field queries
e.opts.events = false // Set event queries
e.opts.tables = true // Set table queries
case false:
e.opts.fields = true // Set field queries
e.opts.events = true // Set event queries
e.opts.tables = true // Set table queries
}
}
return
}

View file

@ -1,99 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"context"
"github.com/surrealdb/surrealdb/cnf"
"github.com/surrealdb/surrealdb/sql"
"github.com/surrealdb/surrealdb/util/data"
)
func (d *document) perms(ctx context.Context, doc *data.Doc) (err error) {
// If this is a document loaded from
// a subquery or data param, and not
// from the KV store, then there is
// no need to check permissions.
if d.key == nil {
return nil
}
// If we are authenticated using DB, NS,
// or KV permissions level, then we can
// ignore all permissions checks.
if perm(ctx) < cnf.AuthSC {
return nil
}
// Get the field definitions so we can
// check if the permissions allow us
// to view each field.
fds, err := d.i.e.tx.AllFD(ctx, d.key.NS, d.key.DB, d.key.TB)
if err != nil {
return err
}
// Once we have the table we reset the
// context to DB level so that no other
// embedded permissions are checked on
// records within these permissions.
ctx = context.WithValue(ctx, ctxKeyKind, cnf.AuthDB)
// We then try to process the relevant
// permissions dependent on the query
// that we are currently processing. If
// there are no permissions specified
// for this table, then because this is
// a scoped request, return an error.
for _, fd := range fds {
if fd.Perms != nil {
err = doc.Walk(func(key string, val interface{}, exi bool) error {
// We are checking the permissions of the field
if p, ok := fd.Perms.(*sql.PermExpression); ok {
vars := data.New()
vars.Set(val, varKeyValue)
ctx = context.WithValue(ctx, ctxKeySpec, vars)
if v, err := d.i.e.fetch(ctx, p.Select, doc); err != nil {
return err
} else if b, ok := v.(bool); !ok || !b {
doc.Del(key)
}
}
return nil
}, fd.Name.VA)
}
}
return nil
}

View file

@ -1,35 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import "sync"
var executorPool = sync.Pool{
New: func() interface{} {
return new(executor)
},
}
var iteratorPool = sync.Pool{
New: func() interface{} {
return new(iterator)
},
}
var documentPool = sync.Pool{
New: func() interface{} {
return new(document)
},
}

View file

@ -1,176 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"fmt"
"context"
"github.com/surrealdb/surrealdb/cnf"
"github.com/surrealdb/surrealdb/sql"
"github.com/surrealdb/surrealdb/util/data"
"github.com/surrealdb/surrealdb/util/guid"
"github.com/surrealdb/surrealdb/util/keys"
)
func (e *executor) executeRelate(ctx context.Context, stm *sql.RelateStatement) ([]interface{}, error) {
if err := e.access(ctx, cnf.AuthNO); err != nil {
return nil, err
}
var from, with sql.Exprs
for _, val := range stm.From {
f, err := e.fetch(ctx, val, nil)
if err != nil {
return nil, err
}
from = append(from, f)
}
for _, val := range stm.With {
w, err := e.fetch(ctx, val, nil)
if err != nil {
return nil, err
}
with = append(with, w)
}
i := newIterator(e, ctx, stm, false)
switch what := stm.Type.(type) {
default:
return nil, fmt.Errorf("Can not execute RELATE query using value '%v'", what)
case *sql.Table:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db, TB: what.TB, ID: guid.New().String()}
i.processThing(ctx, key)
case *sql.Ident:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db, TB: what.VA, ID: guid.New().String()}
i.processThing(ctx, key)
// Result of subquery
case []interface{}:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db}
i.processOther(ctx, key, what)
// Result of subquery with LIMIT 1
case map[string]interface{}:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db}
i.processOther(ctx, key, []interface{}{what})
}
return i.Yield(ctx)
}
func (e *executor) fetchRelate(ctx context.Context, stm *sql.RelateStatement, doc *data.Doc) (interface{}, error) {
ctx = dive(ctx)
if doc != nil {
vars := data.New()
vars.Set(doc.Data(), varKeyParent)
if subs := ctx.Value(ctxKeySubs); subs != nil {
if subs, ok := subs.(*data.Doc); ok {
vars.Set(subs.Get(varKeyParents).Data(), varKeyParents)
}
} else {
vars.Array(varKeyParents)
}
vars.Append(doc.Data(), varKeyParents)
ctx = context.WithValue(ctx, ctxKeySubs, vars)
}
out, err := e.executeRelate(ctx, stm)
if err != nil {
return nil, err
}
switch len(out) {
case 1:
return data.Consume(out).Get(docKeyOne, docKeyId).Data(), nil
default:
return data.Consume(out).Get(docKeyAll, docKeyId).Data(), nil
}
}
func (d *document) runRelate(ctx context.Context, stm *sql.RelateStatement) (interface{}, error) {
var ok bool
var err error
var met = _CREATE
if err = d.init(ctx); err != nil {
return nil, err
}
if err = d.lock(ctx); err != nil {
return nil, err
}
if err = d.setup(ctx); err != nil {
return nil, err
}
if d.val.Exi() == true {
met = _UPDATE
}
if ok, err = d.allow(ctx, met); err != nil {
return nil, err
} else if ok == false {
return nil, nil
}
if err = d.merge(ctx, met, stm.Data); err != nil {
return nil, err
}
if ok, err = d.allow(ctx, met); err != nil {
return nil, err
} else if ok == false {
return nil, nil
}
if err = d.storeIndex(ctx); err != nil {
return nil, err
}
if err = d.storeThing(ctx); err != nil {
return nil, err
}
if err = d.table(ctx, met); err != nil {
return nil, err
}
if err = d.lives(ctx, met); err != nil {
return nil, err
}
if err = d.event(ctx, met); err != nil {
return nil, err
}
return d.yield(ctx, stm, stm.Echo)
}

View file

@ -1,270 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"context"
"github.com/surrealdb/surrealdb/cnf"
"github.com/surrealdb/surrealdb/sql"
"github.com/surrealdb/surrealdb/util/keys"
)
func (e *executor) executeRemoveNamespace(ctx context.Context, ast *sql.RemoveNamespaceStatement) (out []interface{}, err error) {
if err := e.access(ctx, cnf.AuthKV); err != nil {
return nil, err
}
e.tx.DelNS(ast.Name.VA)
// Remove the namespace definition
nkey := &keys.NS{KV: KV, NS: ast.Name.VA}
_, err = e.tx.Clr(ctx, nkey.Encode())
// Remove the namespace resource data
akey := &keys.Namespace{KV: KV, NS: ast.Name.VA}
_, err = e.tx.ClrP(ctx, akey.Encode(), 0)
return
}
func (e *executor) executeRemoveDatabase(ctx context.Context, ast *sql.RemoveDatabaseStatement) (out []interface{}, err error) {
if err := e.access(ctx, cnf.AuthNS); err != nil {
return nil, err
}
e.tx.DelDB(e.ns, ast.Name.VA)
// Remove the database definition
dkey := &keys.DB{KV: KV, NS: e.ns, DB: ast.Name.VA}
_, err = e.tx.Clr(ctx, dkey.Encode())
// Remove the database resource data
akey := &keys.Database{KV: KV, NS: e.ns, DB: ast.Name.VA}
_, err = e.tx.ClrP(ctx, akey.Encode(), 0)
return
}
func (e *executor) executeRemoveLogin(ctx context.Context, ast *sql.RemoveLoginStatement) (out []interface{}, err error) {
switch ast.Kind {
case sql.NAMESPACE:
if err := e.access(ctx, cnf.AuthNS); err != nil {
return nil, err
}
// Remove the login definition
ukey := &keys.NU{KV: KV, NS: e.ns, US: ast.User.VA}
_, err = e.tx.ClrP(ctx, ukey.Encode(), 0)
case sql.DATABASE:
if err := e.access(ctx, cnf.AuthDB); err != nil {
return nil, err
}
// Remove the login definition
ukey := &keys.DU{KV: KV, NS: e.ns, DB: e.db, US: ast.User.VA}
_, err = e.tx.ClrP(ctx, ukey.Encode(), 0)
}
return
}
func (e *executor) executeRemoveToken(ctx context.Context, ast *sql.RemoveTokenStatement) (out []interface{}, err error) {
if err := e.access(ctx, cnf.AuthNO); err != nil {
return nil, err
}
switch ast.Kind {
case sql.NAMESPACE:
if err := e.access(ctx, cnf.AuthNS); err != nil {
return nil, err
}
// Remove the token definition
tkey := &keys.NT{KV: KV, NS: e.ns, TK: ast.Name.VA}
_, err = e.tx.ClrP(ctx, tkey.Encode(), 0)
case sql.DATABASE:
if err := e.access(ctx, cnf.AuthDB); err != nil {
return nil, err
}
// Remove the token definition
tkey := &keys.DT{KV: KV, NS: e.ns, DB: e.db, TK: ast.Name.VA}
_, err = e.tx.ClrP(ctx, tkey.Encode(), 0)
case sql.SCOPE:
if err := e.access(ctx, cnf.AuthDB); err != nil {
return nil, err
}
// Remove the token definition
tkey := &keys.ST{KV: KV, NS: e.ns, DB: e.db, SC: ast.What.VA, TK: ast.Name.VA}
_, err = e.tx.ClrP(ctx, tkey.Encode(), 0)
}
return
}
func (e *executor) executeRemoveScope(ctx context.Context, ast *sql.RemoveScopeStatement) (out []interface{}, err error) {
if err := e.access(ctx, cnf.AuthDB); err != nil {
return nil, err
}
// Remove the scope definition
skey := &keys.SC{KV: KV, NS: e.ns, DB: e.db, SC: ast.Name.VA}
_, err = e.tx.ClrP(ctx, skey.Encode(), 0)
return
}
func (e *executor) executeRemoveEvent(ctx context.Context, ast *sql.RemoveEventStatement) (out []interface{}, err error) {
if err := e.access(ctx, cnf.AuthDB); err != nil {
return nil, err
}
for _, TB := range ast.What {
e.tx.DelEV(e.ns, e.db, TB.TB, ast.Name.VA)
// Remove the event definition
ekey := &keys.EV{KV: KV, NS: e.ns, DB: e.db, TB: TB.TB, EV: ast.Name.VA}
if _, err = e.tx.ClrP(ctx, ekey.Encode(), 0); err != nil {
return nil, err
}
}
return
}
func (e *executor) executeRemoveField(ctx context.Context, ast *sql.RemoveFieldStatement) (out []interface{}, err error) {
if err := e.access(ctx, cnf.AuthDB); err != nil {
return nil, err
}
for _, TB := range ast.What {
e.tx.DelFD(e.ns, e.db, TB.TB, ast.Name.VA)
// Remove the field definition
fkey := &keys.FD{KV: KV, NS: e.ns, DB: e.db, TB: TB.TB, FD: ast.Name.VA}
if _, err = e.tx.ClrP(ctx, fkey.Encode(), 0); err != nil {
return nil, err
}
}
return
}
func (e *executor) executeRemoveIndex(ctx context.Context, ast *sql.RemoveIndexStatement) (out []interface{}, err error) {
if err := e.access(ctx, cnf.AuthDB); err != nil {
return nil, err
}
for _, TB := range ast.What {
e.tx.DelIX(e.ns, e.db, TB.TB, ast.Name.VA)
// Remove the index definition
ikey := &keys.IX{KV: KV, NS: e.ns, DB: e.db, TB: TB.TB, IX: ast.Name.VA}
if _, err = e.tx.ClrP(ctx, ikey.Encode(), 0); err != nil {
return nil, err
}
// Remove the index resource data
dkey := &keys.Index{KV: KV, NS: e.ns, DB: e.db, TB: TB.TB, IX: ast.Name.VA, FD: keys.Ignore}
if _, err = e.tx.ClrP(ctx, dkey.Encode(), 0); err != nil {
return nil, err
}
}
return
}
func (e *executor) executeRemoveTable(ctx context.Context, ast *sql.RemoveTableStatement) (out []interface{}, err error) {
if err := e.access(ctx, cnf.AuthDB); err != nil {
return nil, err
}
for _, TB := range ast.What {
e.tx.DelTB(e.ns, e.db, TB.TB)
tb, err := e.tx.GetTB(ctx, e.ns, e.db, TB.TB)
if err != nil {
return nil, err
}
// Remove the table definition
tkey := &keys.TB{KV: KV, NS: e.ns, DB: e.db, TB: TB.TB}
_, err = e.tx.Clr(ctx, tkey.Encode())
if err != nil {
return nil, err
}
// Remove the table resource data
dkey := &keys.Table{KV: KV, NS: e.ns, DB: e.db, TB: TB.TB}
_, err = e.tx.ClrP(ctx, dkey.Encode(), 0)
if err != nil {
return nil, err
}
if tb.Lock {
for _, FT := range tb.From {
// Remove the foreign table definition
tkey := &keys.FT{KV: KV, NS: e.ns, DB: e.db, TB: FT.TB, FT: TB.TB}
if _, err = e.tx.ClrP(ctx, tkey.Encode(), 0); err != nil {
return nil, err
}
}
}
}
return
}

View file

@ -1,96 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"testing"
"github.com/surrealdb/surrealdb/util/data"
. "github.com/smartystreets/goconvey/convey"
)
func TestRemove(t *testing.T) {
Convey("Remove a namespace", t, func() {
setupDB(workerCount)
txt := `
USE NS test DB test;
CREATE |person:10|;
REMOVE NAMESPACE test;
SELECT * FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[1].Status, ShouldEqual, "OK")
So(res[2].Status, ShouldEqual, "OK")
So(res[3].Status, ShouldEqual, "OK")
So(res[3].Result, ShouldHaveLength, 0)
})
Convey("Remove a database", t, func() {
setupDB(workerCount)
txt := `
USE NS test DB test;
CREATE |person:10|;
REMOVE DATABASE test;
INFO FOR NAMESPACE;
SELECT * FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 5)
So(res[1].Status, ShouldEqual, "OK")
So(res[2].Status, ShouldEqual, "OK")
So(res[3].Status, ShouldEqual, "OK")
So(res[3].Result, ShouldHaveLength, 1)
So(data.Consume(res[3].Result[0]).Get("database").Data(), ShouldHaveLength, 0)
So(res[4].Status, ShouldEqual, "OK")
So(res[4].Result, ShouldHaveLength, 0)
})
Convey("Remove a table", t, func() {
setupDB(workerCount)
txt := `
USE NS test DB test;
CREATE |person:10|;
REMOVE TABLE person;
INFO FOR DATABASE;
SELECT * FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 5)
So(res[1].Status, ShouldEqual, "OK")
So(res[2].Status, ShouldEqual, "OK")
So(res[3].Result, ShouldHaveLength, 1)
So(data.Consume(res[3].Result[0]).Get("table").Data(), ShouldHaveLength, 0)
So(res[4].Status, ShouldEqual, "OK")
So(res[4].Result, ShouldHaveLength, 0)
})
}

View file

@ -1,44 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"context"
"github.com/surrealdb/surrealdb/sql"
)
func (e *executor) executeReturn(ctx context.Context, stm *sql.ReturnStatement) (out []interface{}, err error) {
for _, w := range stm.What {
switch what := w.(type) {
case *sql.Void:
// Ignore
case *sql.Empty:
// Ignore
default:
val, err := e.fetch(ctx, what, nil)
if err != nil {
return nil, err
}
out = append(out, val)
}
}
return
}

View file

@ -1,93 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestReturn(t *testing.T) {
Convey("Return a string", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
RETURN "test";
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Status, ShouldEqual, "OK")
So(res[1].Result, ShouldResemble, []interface{}{"test"})
})
Convey("Return a number", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
RETURN 33693;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Status, ShouldEqual, "OK")
So(res[1].Result, ShouldResemble, []interface{}{33693.0})
})
Convey("Return a VOID expression", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
RETURN VOID;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Status, ShouldEqual, "OK")
So(res[1].Result, ShouldResemble, []interface{}{})
})
Convey("Return an EMPTY expression", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
RETURN EMPTY;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Status, ShouldEqual, "OK")
So(res[1].Result, ShouldResemble, []interface{}{})
})
}

View file

@ -1,39 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"context"
"github.com/surrealdb/surrealdb/sql"
)
func (e *executor) executeRun(ctx context.Context, stm *sql.RunStatement) (out []interface{}, err error) {
val, err := e.fetch(ctx, stm, nil)
if err != nil {
return nil, err
}
switch val := val.(type) {
case []interface{}:
out = val
case interface{}:
out = append(out, val)
}
return
}

View file

@ -1,106 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"fmt"
"sync"
"testing"
"time"
"net/http"
"net/http/httptest"
. "github.com/smartystreets/goconvey/convey"
)
type Handler struct {
sync.Mutex
count int
}
func (s *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
s.Lock()
defer s.Unlock()
s.count++
fmt.Fprintf(w, "SERVER OK: %d", s.count)
}
func TestRun(t *testing.T) {
handler := &Handler{}
server := httptest.NewServer(handler)
defer server.Close()
Convey("Run statement which runs http requests", t, func() {
setupDB(1)
func() {
txt := `
USE NS test DB test;
DEFINE TABLE test PERMISSIONS FULL;
DEFINE TABLE temp PERMISSIONS FOR SELECT FULL;
DEFINE EVENT done ON test WHEN true THEN (
CREATE temp:main;
RUN http.get("` + server.URL + `");
RUN http.put("` + server.URL + `");
RUN http.post("` + server.URL + `");
RUN http.delete("` + server.URL + `");
RUN http.async.get("` + server.URL + `");
RUN http.async.put("` + server.URL + `");
RUN http.async.post("` + server.URL + `");
RUN http.async.delete("` + server.URL + `");
);
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[1].Status, ShouldEqual, "OK")
So(res[2].Status, ShouldEqual, "OK")
}()
func() {
txt := `
USE NS test DB test;
SELECT * FROM test, temp;
CREATE test:main;
SELECT * FROM test, temp;
`
res, err := Execute(permsSC(), txt, nil)
time.Sleep(1 * time.Second)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[1].Status, ShouldEqual, "OK")
So(res[1].Result, ShouldHaveLength, 0)
So(res[2].Status, ShouldEqual, "OK")
So(res[2].Result, ShouldHaveLength, 1)
So(res[3].Status, ShouldEqual, "OK")
So(res[3].Result, ShouldHaveLength, 2)
handler.Lock()
So(handler.count, ShouldEqual, 8)
handler.Unlock()
}()
})
}

View file

@ -1,250 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"testing"
"github.com/surrealdb/surrealdb/sql"
"github.com/surrealdb/surrealdb/util/data"
. "github.com/smartystreets/goconvey/convey"
)
func TestScope(t *testing.T) {
Convey("Select records from an array of strings", t, func() {
setupDB(1)
func() {
txt := `
USE NS test DB test;
DEFINE NAMESPACE test;
DEFINE DATABASE test;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 3)
}()
func() {
txt := `
USE NS test DB test;
SELECT * FROM [
"one",
"two",
"tre",
];
`
res, err := Execute(permsSC(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Result, ShouldHaveLength, 3)
So(data.Consume(res[1].Result[0]).Data(), ShouldResemble, "one")
So(data.Consume(res[1].Result[1]).Data(), ShouldResemble, "two")
So(data.Consume(res[1].Result[2]).Data(), ShouldResemble, "tre")
}()
})
Convey("Select records from an array of objects with an id key", t, func() {
setupDB(1)
func() {
txt := `
USE NS test DB test;
DEFINE NAMESPACE test;
DEFINE DATABASE test;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 3)
}()
func() {
txt := `
USE NS test DB test;
SELECT * FROM [
{ id: "one" },
{ id: "two" },
{ id: "tre" },
];
`
res, err := Execute(permsSC(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Result, ShouldHaveLength, 3)
So(data.Consume(res[1].Result[0]).Data(), ShouldResemble, map[string]interface{}{"id": "one"})
So(data.Consume(res[1].Result[1]).Data(), ShouldResemble, map[string]interface{}{"id": "two"})
So(data.Consume(res[1].Result[2]).Data(), ShouldResemble, map[string]interface{}{"id": "tre"})
}()
})
Convey("Select records from an array of objects with no id key", t, func() {
setupDB(1)
func() {
txt := `
USE NS test DB test;
DEFINE NAMESPACE test;
DEFINE DATABASE test;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 3)
}()
func() {
txt := `
USE NS test DB test;
SELECT * FROM [
{ test: "one" },
{ test: "two" },
{ test: "tre" },
];
`
res, err := Execute(permsSC(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Result, ShouldHaveLength, 3)
So(data.Consume(res[1].Result[0]).Data(), ShouldResemble, map[string]interface{}{"test": "one"})
So(data.Consume(res[1].Result[1]).Data(), ShouldResemble, map[string]interface{}{"test": "two"})
So(data.Consume(res[1].Result[2]).Data(), ShouldResemble, map[string]interface{}{"test": "tre"})
}()
})
Convey("Select records from an array of virtual record things with no permissions", t, func() {
setupDB(1)
func() {
txt := `
USE NS test DB test;
DEFINE NAMESPACE test;
DEFINE DATABASE test;
CREATE test:one, test:two, test:tre;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
}()
func() {
txt := `
USE NS test DB test;
SELECT * FROM array(
thing("test", "one"),
thing("test", "two"),
thing("test", "tre")
);
`
res, err := Execute(permsSC(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Result, ShouldHaveLength, 0)
}()
})
Convey("Select records from an array of virtual record things with full permissions", t, func() {
setupDB(1)
func() {
txt := `
USE NS test DB test;
DEFINE NAMESPACE test;
DEFINE DATABASE test;
DEFINE TABLE test PERMISSIONS FULL;
CREATE test:one, test:two, test:tre;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 5)
}()
func() {
txt := `
USE NS test DB test;
SELECT * FROM array(
thing("test", "one"),
thing("test", "two"),
thing("test", "tre")
);
`
res, err := Execute(permsSC(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Result, ShouldHaveLength, 3)
So(data.Consume(res[1].Result[0]).Data(), ShouldResemble, map[string]interface{}{
"id": sql.NewThing("test", "one"),
"meta": map[string]interface{}{
"tb": "test",
"id": "one",
},
})
So(data.Consume(res[1].Result[1]).Data(), ShouldResemble, map[string]interface{}{
"id": sql.NewThing("test", "two"),
"meta": map[string]interface{}{
"tb": "test",
"id": "two",
},
})
So(data.Consume(res[1].Result[2]).Data(), ShouldResemble, map[string]interface{}{
"id": sql.NewThing("test", "tre"),
"meta": map[string]interface{}{
"tb": "test",
"id": "tre",
},
})
}()
})
}

View file

@ -1,185 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"context"
"github.com/surrealdb/surrealdb/cnf"
"github.com/surrealdb/surrealdb/sql"
"github.com/surrealdb/surrealdb/util/data"
"github.com/surrealdb/surrealdb/util/keys"
)
func (e *executor) executeSelect(ctx context.Context, stm *sql.SelectStatement) ([]interface{}, error) {
if err := e.access(ctx, cnf.AuthNO); err != nil {
return nil, err
}
ctx = context.WithValue(ctx, ctxKeyVersion, stm.Version)
var what sql.Exprs
for _, val := range stm.What {
w, err := e.fetch(ctx, val, nil)
if err != nil {
return nil, err
}
what = append(what, w)
}
i := newIterator(e, ctx, stm, false)
for _, w := range what {
switch what := w.(type) {
default:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db}
i.processQuery(ctx, key, []interface{}{what})
case *sql.Table:
key := &keys.Table{KV: KV, NS: e.ns, DB: e.db, TB: what.TB}
i.processTable(ctx, key)
case *sql.Ident:
key := &keys.Table{KV: KV, NS: e.ns, DB: e.db, TB: what.VA}
i.processTable(ctx, key)
case *sql.Thing:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db, TB: what.TB, ID: what.ID}
i.processThing(ctx, key)
case *sql.Model:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db, TB: what.TB, ID: nil}
i.processModel(ctx, key, what)
case *sql.Batch:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db, TB: what.TB, ID: nil}
i.processBatch(ctx, key, what)
// Result of subquery
case []interface{}:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db}
i.processQuery(ctx, key, what)
// Result of subquery with LIMIT 1
case map[string]interface{}:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db}
i.processQuery(ctx, key, []interface{}{what})
}
}
return i.Yield(ctx)
}
func (e *executor) fetchSelect(ctx context.Context, stm *sql.SelectStatement, doc *data.Doc) (interface{}, error) {
ctx = dive(ctx)
if doc != nil {
vars := data.New()
vars.Set(doc.Data(), varKeyParent)
if subs := ctx.Value(ctxKeySubs); subs != nil {
if subs, ok := subs.(*data.Doc); ok {
vars.Set(subs.Get(varKeyParents).Data(), varKeyParents)
}
} else {
vars.Array(varKeyParents)
}
vars.Append(doc.Data(), varKeyParents)
ctx = context.WithValue(ctx, ctxKeySubs, vars)
}
out, err := e.executeSelect(ctx, stm)
if err != nil {
return nil, err
}
cnt, err := e.fetchOutputs(ctx, stm)
if err != nil {
return nil, err
}
switch cnt {
case 1:
switch len(stm.Expr) {
case 1:
f := stm.Expr[0]
switch f.Expr.(type) {
default:
return data.Consume(out).Get(docKeyOne, f.Field).Data(), nil
case *sql.All:
return data.Consume(out).Get(docKeyOne).Data(), nil
}
default:
return data.Consume(out).Get(docKeyOne).Data(), nil
}
default:
switch len(stm.Expr) {
case 1:
f := stm.Expr[0]
switch f.Expr.(type) {
default:
return data.Consume(out).Get(docKeyAll, f.Field).Data(), nil
case *sql.All:
return data.Consume(out).Get(docKeyAll).Data(), nil
}
default:
return data.Consume(out).Get(docKeyAll).Data(), nil
}
}
return out, err
}
func (d *document) runSelect(ctx context.Context, stm *sql.SelectStatement) (interface{}, error) {
var ok bool
var err error
var met = _SELECT
if err = d.init(ctx); err != nil {
return nil, err
}
if err = d.setup(ctx); err != nil {
return nil, err
}
if d.doc == nil && !d.val.Exi() {
return nil, nil
}
if ok, err = d.check(ctx, stm.Cond); err != nil {
return nil, err
} else if ok == false {
return nil, nil
}
if ok, err = d.allow(ctx, met); err != nil {
return nil, err
} else if ok == false {
return nil, nil
}
return d.yield(ctx, stm, sql.ILLEGAL)
}

File diff suppressed because it is too large Load diff

View file

@ -1,33 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"github.com/surrealdb/fibre"
)
func session(c *fibre.Context) (out map[string]interface{}) {
out = make(map[string]interface{})
out[varKeyIp] = c.IP().String()
out[varKeyId] = c.Get(varKeyUniq)
out[varKeyOrigin] = c.Origin()
return
}

View file

@ -1,421 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"fmt"
"sync"
"context"
"github.com/surrealdb/fibre"
"github.com/surrealdb/surrealdb/cnf"
"github.com/surrealdb/surrealdb/kvs"
"github.com/surrealdb/surrealdb/sql"
"github.com/surrealdb/surrealdb/txn"
"github.com/surrealdb/surrealdb/util/data"
"github.com/surrealdb/surrealdb/util/keys"
"github.com/surrealdb/surrealdb/util/uuid"
)
type socket struct {
mutex sync.Mutex
fibre *fibre.Context
sends map[string][]interface{}
items map[string][]interface{}
lives map[string]*sql.LiveStatement
}
func clear(id string) {
go func() {
sockets.Range(func(key, val interface{}) bool {
val.(*socket).clear(id + "-bg")
val.(*socket).clear(id)
return true
})
}()
}
func flush(id string) {
go func() {
sockets.Range(func(key, val interface{}) bool {
val.(*socket).flush(id + "-bg")
val.(*socket).flush(id)
return true
})
}()
}
func send(id string) {
go func() {
sockets.Range(func(key, val interface{}) bool {
val.(*socket).send(id + "-bg")
val.(*socket).send(id)
return true
})
}()
}
// TODO remove this when distributed
// We need to remove this when moving
// to a distributed cluster as
// websockets might be managed by an
// alternative server, and should not
// be removed on node startup.
func tidy() error {
ctx := context.Background()
txn, _ := txn.New(ctx, true)
defer txn.Commit()
nss, err := txn.AllNS(ctx)
if err != nil {
return err
}
for _, ns := range nss {
dbs, err := txn.AllDB(ctx, ns.Name.VA)
if err != nil {
return err
}
for _, db := range dbs {
tbs, err := txn.AllTB(ctx, ns.Name.VA, db.Name.VA)
if err != nil {
return err
}
for _, tb := range tbs {
key := &keys.LV{KV: KV, NS: ns.Name.VA, DB: db.Name.VA, TB: tb.Name.VA, LV: keys.Ignore}
if _, err = txn.ClrP(ctx, key.Encode(), 0); err != nil {
return err
}
}
}
}
return nil
}
func (s *socket) ctx() (ctx context.Context) {
ctx = context.Background()
auth := s.fibre.Get(ctxKeyAuth).(*cnf.Auth)
sess := s.fibre.Get(ctxKeyVars).(map[string]interface{})
vars := data.Consume(sess)
vars.Set(ENV, varKeyEnv)
vars.Set(auth.Data, varKeyAuth)
vars.Set(auth.Scope, varKeyScope)
vars.Set(session(s.fibre), varKeySession)
ctx = context.WithValue(ctx, ctxKeyVars, vars)
ctx = context.WithValue(ctx, ctxKeyKind, auth.Kind)
return
}
func (s *socket) queue(id, query, action string, result interface{}) {
s.mutex.Lock()
defer s.mutex.Unlock()
s.items[id] = append(s.items[id], &Dispatch{
Query: query,
Action: action,
Result: result,
})
}
func (s *socket) clear(id string) (err error) {
s.mutex.Lock()
defer s.mutex.Unlock()
delete(s.items, id)
return
}
func (s *socket) flush(id string) (err error) {
s.mutex.Lock()
defer s.mutex.Unlock()
s.sends[id] = append(s.sends[id], s.items[id]...)
delete(s.items, id)
return
}
func (s *socket) send(id string) (err error) {
s.mutex.Lock()
defer s.mutex.Unlock()
// If there are no pending message
// notifications for this socket
// then ignore this method call.
if len(s.sends[id]) == 0 {
return nil
}
// Create a new rpc notification
// object so that we can send the
// batch changes in one go.
obj := &fibre.RPCNotification{
Method: "notify",
Params: s.sends[id],
}
// Notify the websocket connection
// y sending an RPCNotification type
// to the notify channel.
s.fibre.Socket().Notify(obj)
// Make sure that we clear all the
// pending message notifications
// for this socket when done.
delete(s.sends, id)
return
}
func (s *socket) check(e *executor, ctx context.Context, ns, db, tb string) (err error) {
var tbv *sql.DefineTableStatement
// If we are authenticated using DB, NS,
// or KV permissions level, then we can
// ignore all permissions checks.
if perm(ctx) < cnf.AuthSC {
return nil
}
// First check that the NS exists, as
// otherwise, the scoped authentication
// request can not do anything.
_, err = e.tx.GetNS(ctx, ns)
if err != nil {
return err
}
// Next check that the DB exists, as
// otherwise, the scoped authentication
// request can not do anything.
_, err = e.tx.GetDB(ctx, ns, db)
if err != nil {
return err
}
// Then check that the TB exists, as
// otherwise, the scoped authentication
// request can not do anything.
tbv, err = e.tx.GetTB(ctx, ns, db, tb)
if err != nil {
return err
}
// If the table has any permissions
// specified, then let's check if this
// query is allowed access to the table.
switch p := tbv.Perms.(type) {
case *sql.PermExpression:
return e.fetchPerms(ctx, p.Select, tbv.Name)
default:
return &PermsError{table: tb}
}
}
func (s *socket) deregister(id string) {
sockets.Delete(id)
ctx := context.Background()
txn, _ := kvs.Begin(ctx, true)
defer txn.Commit()
for id, stm := range s.lives {
for _, w := range stm.What {
switch what := w.(type) {
case *sql.Table:
key := &keys.LV{KV: KV, NS: stm.NS, DB: stm.DB, TB: what.TB, LV: id}
txn.Clr(ctx, key.Encode())
case *sql.Ident:
key := &keys.LV{KV: KV, NS: stm.NS, DB: stm.DB, TB: what.VA, LV: id}
txn.Clr(ctx, key.Encode())
}
}
}
}
func (s *socket) executeLive(e *executor, ctx context.Context, stm *sql.LiveStatement) (out []interface{}, err error) {
stm.FB = e.id
stm.NS = e.ns
stm.DB = e.db
s.mutex.Lock()
defer s.mutex.Unlock()
// Generate a new query uuid.
stm.ID = uuid.New().String()
// Store the live query on the socket.
s.lives[stm.ID] = stm
// Return the query id to the user.
out = append(out, stm.ID)
// Store the live query in the database layer.
for key, val := range stm.What {
w, err := e.fetch(ctx, val, nil)
if err != nil {
return nil, err
}
stm.What[key] = w
}
for _, w := range stm.What {
switch what := w.(type) {
default:
return nil, fmt.Errorf("Can not execute LIVE query using value '%v'", what)
case *sql.Table:
key := &keys.LV{KV: KV, NS: stm.NS, DB: stm.DB, TB: what.TB, LV: stm.ID}
if _, err = e.tx.Put(ctx, 0, key.Encode(), stm.Encode()); err != nil {
return nil, err
}
case *sql.Ident:
key := &keys.LV{KV: KV, NS: stm.NS, DB: stm.DB, TB: what.VA, LV: stm.ID}
if _, err = e.tx.Put(ctx, 0, key.Encode(), stm.Encode()); err != nil {
return nil, err
}
}
}
return
}
func (s *socket) executeKill(e *executor, ctx context.Context, stm *sql.KillStatement) (out []interface{}, err error) {
s.mutex.Lock()
defer s.mutex.Unlock()
// Remove the live query from the database layer.
var what sql.Exprs
for _, val := range stm.What {
w, err := e.fetch(ctx, val, nil)
if err != nil {
return nil, err
}
what = append(what, w)
}
for _, w := range what {
switch what := w.(type) {
default:
return nil, fmt.Errorf("Can not execute KILL query using value '%v'", what)
case string:
if qry, ok := s.lives[what]; ok {
// Delete the live query from the saved queries.
delete(s.lives, qry.ID)
// Delete the live query from the database layer.
for _, w := range qry.What {
switch what := w.(type) {
case *sql.Table:
key := &keys.LV{KV: KV, NS: qry.NS, DB: qry.DB, TB: what.TB, LV: qry.ID}
_, err = e.tx.Clr(ctx, key.Encode())
case *sql.Ident:
key := &keys.LV{KV: KV, NS: qry.NS, DB: qry.DB, TB: what.VA, LV: qry.ID}
_, err = e.tx.Clr(ctx, key.Encode())
}
}
}
}
}
return
}

View file

@ -1,668 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"fmt"
"context"
"github.com/surrealdb/surrealdb/sql"
"github.com/surrealdb/surrealdb/util/data"
"github.com/surrealdb/surrealdb/util/fncs"
"github.com/surrealdb/surrealdb/util/keys"
)
// Table checks if any table views are specified for
// this table, and executes them in name order.
func (d *document) table(ctx context.Context, when method) (err error) {
if !d.i.e.opts.tables {
return nil
}
// Check if this query has been run
// in forced mode, because of an
// index or foreign table update.
forced := d.forced(ctx)
// If this document has not changed
// then there is no need to update
// any registered foreign tables.
if !forced && !d.changed {
return nil
}
// Get the foreign read-only tables
// specified for this table, and
// update values which have changed.
fts, err := d.i.e.tx.AllFT(ctx, d.key.NS, d.key.DB, d.key.TB)
if err != nil {
return err
}
for _, ft := range fts {
var ok bool
var prv *sql.Thing
var doc *sql.Thing
ok, err = d.check(ctx, ft.Cond)
if err != nil {
return err
}
if len(ft.Group) > 0 {
// If there are GROUP BY clauses then
// let's calculate the
old := make([]interface{}, len(ft.Group))
now := make([]interface{}, len(ft.Group))
for k, e := range ft.Group {
old[k], _ = d.i.e.fetch(ctx, e.Expr, d.initial)
now[k], _ = d.i.e.fetch(ctx, e.Expr, d.current)
}
prv = sql.NewThing(ft.Name.VA, fmt.Sprintf("%v", old))
doc = sql.NewThing(ft.Name.VA, fmt.Sprintf("%v", now))
} else {
// Otherwise let's use the id of the
// current record as the basis of the
// new record in the other table.
doc = sql.NewThing(ft.Name.VA, d.id.ID)
}
switch ok {
// If the document does not match the table
// WHERE condition, then remove it from
// the table, or remove it from the aggregate.
case false:
if len(ft.Group) > 0 {
if !forced && when != _CREATE {
err = d.tableModify(ctx, prv, ft.Expr, _REMOVE)
if err != nil {
return err
}
}
} else {
err = d.tableDelete(ctx, doc, ft.Expr)
if err != nil {
return err
}
}
// If the document does match the table
// WHERE condition, then add it to the
// table, or add it to the aggregate.
case true:
if len(ft.Group) > 0 {
if !forced && when != _CREATE {
err = d.tableModify(ctx, prv, ft.Expr, _REMOVE)
if err != nil {
return err
}
}
if when != _DELETE {
err = d.tableModify(ctx, doc, ft.Expr, _CHANGE)
if err != nil {
return err
}
}
} else {
err = d.tableUpdate(ctx, doc, ft.Expr)
if err != nil {
return err
}
}
}
}
return
}
func (d *document) tableDelete(ctx context.Context, tng *sql.Thing, exp sql.Fields) (err error) {
stm := &sql.DeleteStatement{
What: sql.Exprs{tng},
}
key := &keys.Thing{KV: KV, NS: d.i.e.ns, DB: d.i.e.db, TB: tng.TB, ID: tng.ID}
i := newIterator(d.i.e, ctx, stm, true)
i.processThing(ctx, key)
_, err = i.Yield(ctx)
return err
}
func (d *document) tableUpdate(ctx context.Context, tng *sql.Thing, exp sql.Fields) (err error) {
res, err := d.yield(ctx, &sql.SelectStatement{Expr: exp}, sql.ILLEGAL)
if err != nil {
return err
}
stm := &sql.UpdateStatement{
What: sql.Exprs{tng},
Data: &sql.ContentExpression{Data: res},
}
key := &keys.Thing{KV: KV, NS: d.i.e.ns, DB: d.i.e.db, TB: tng.TB, ID: tng.ID}
i := newIterator(d.i.e, ctx, stm, true)
i.processThing(ctx, key)
_, err = i.Yield(ctx)
return err
}
func (d *document) tableModify(ctx context.Context, tng *sql.Thing, exp sql.Fields, when modify) (err error) {
var doc *data.Doc
switch when {
case _REMOVE:
doc = d.initial
case _CHANGE:
doc = d.current
}
set := &sql.DataExpression{}
for _, e := range exp {
if f, ok := e.Expr.(*sql.FuncExpression); ok && f.Aggr {
var v interface{}
args := make([]interface{}, len(f.Args))
for x := 0; x < len(f.Args); x++ {
args[x], _ = d.i.e.fetch(ctx, f.Args[x], doc)
}
// If the function is math.stddev() or
// math.variance(), then we need to work
// out the value as a whole, and not the
// result of each record separately.
switch f.Name {
default:
v, err = fncs.Run(ctx, f.Name, args...)
case "math.stddev":
v = args[0]
case "math.variance":
v = args[0]
}
if err != nil {
return err
}
switch f.Name {
case "distinct":
tableChg(set, e.Field, v, when)
case "count":
tableChg(set, e.Field, v, when)
case "count.if":
tableChg(set, e.Field, v, when)
case "count.not":
tableChg(set, e.Field, v, when)
case "math.sum":
tableChg(set, e.Field, v, when)
case "math.min":
tableMin(set, e.Field, v, when)
case "math.max":
tableMax(set, e.Field, v, when)
case "math.mean":
tableMean(set, e.Field, v, when)
case "math.stddev":
switch a := v.(type) {
case []interface{}:
for _, v := range a {
tableStddev(set, e.Field, v, when)
}
default:
tableStddev(set, e.Field, v, when)
}
case "math.variance":
switch a := v.(type) {
case []interface{}:
for _, v := range a {
tableVariance(set, e.Field, v, when)
}
default:
tableVariance(set, e.Field, v, when)
}
}
continue
}
o, err := d.i.e.fetch(ctx, e.Expr, doc)
if err != nil {
return err
}
tableSet(set, e.Field, o, when)
}
stm := &sql.UpdateStatement{
What: sql.Exprs{tng},
Data: set,
}
key := &keys.Thing{KV: KV, NS: d.i.e.ns, DB: d.i.e.db, TB: tng.TB, ID: tng.ID}
i := newIterator(d.i.e, ctx, stm, true)
i.processThing(ctx, key)
_, err = i.Yield(ctx)
return err
}
func tableSet(set *sql.DataExpression, key string, val interface{}, when modify) {
set.Data = append(set.Data, &sql.ItemExpression{
LHS: sql.NewIdent(key),
Op: sql.EQ,
RHS: val,
})
}
func tableChg(set *sql.DataExpression, key string, val interface{}, when modify) {
var op sql.Token
switch when {
case _REMOVE:
op = sql.DEC
case _CHANGE:
op = sql.INC
}
set.Data = append(set.Data, &sql.ItemExpression{
LHS: sql.NewIdent(key),
Op: op,
RHS: val,
})
}
func tableMin(set *sql.DataExpression, key string, val interface{}, when modify) {
if when == _CHANGE {
set.Data = append(set.Data, &sql.ItemExpression{
LHS: sql.NewIdent(key),
Op: sql.EQ,
RHS: &sql.SubExpression{
&sql.IfelseStatement{
Cond: sql.Exprs{
&sql.BinaryExpression{
LHS: &sql.BinaryExpression{
LHS: sql.NewIdent(key),
Op: sql.EQ,
RHS: &sql.Empty{},
},
Op: sql.OR,
RHS: &sql.BinaryExpression{
LHS: sql.NewIdent(key),
Op: sql.GT,
RHS: val,
},
},
},
Then: sql.Exprs{
val,
},
Else: sql.NewIdent(key),
},
},
})
}
}
func tableMax(set *sql.DataExpression, key string, val interface{}, when modify) {
if when == _CHANGE {
set.Data = append(set.Data, &sql.ItemExpression{
LHS: sql.NewIdent(key),
Op: sql.EQ,
RHS: &sql.SubExpression{
&sql.IfelseStatement{
Cond: sql.Exprs{
&sql.BinaryExpression{
LHS: &sql.BinaryExpression{
LHS: sql.NewIdent(key),
Op: sql.EQ,
RHS: &sql.Empty{},
},
Op: sql.OR,
RHS: &sql.BinaryExpression{
LHS: sql.NewIdent(key),
Op: sql.LT,
RHS: val,
},
},
},
Then: sql.Exprs{
val,
},
Else: sql.NewIdent(key),
},
},
})
}
}
func tableMean(set *sql.DataExpression, key string, val interface{}, when modify) {
var op sql.Token
switch when {
case _REMOVE:
op = sql.DEC
case _CHANGE:
op = sql.INC
}
set.Data = append(set.Data, &sql.ItemExpression{
LHS: sql.NewIdent("meta.__." + key + ".c"),
Op: op,
RHS: 1,
})
switch when {
case _REMOVE:
op = sql.SUB
case _CHANGE:
op = sql.ADD
}
set.Data = append(set.Data, &sql.ItemExpression{
LHS: sql.NewIdent(key),
Op: sql.EQ,
RHS: &sql.BinaryExpression{
LHS: &sql.SubExpression{
Expr: &sql.BinaryExpression{
LHS: &sql.BinaryExpression{
LHS: sql.NewIdent(key),
Op: sql.MUL,
RHS: &sql.BinaryExpression{
LHS: sql.NewIdent("meta.__." + key + ".c"),
Op: sql.SUB,
RHS: 1,
},
},
Op: op,
RHS: val,
},
},
Op: sql.DIV,
RHS: sql.NewIdent("meta.__." + key + ".c"),
},
})
set.Data = append(set.Data, &sql.ItemExpression{
LHS: sql.NewIdent(key),
Op: sql.EQ,
RHS: &sql.SubExpression{
&sql.IfelseStatement{
Cond: sql.Exprs{
&sql.BinaryExpression{
LHS: sql.NewIdent(key),
Op: sql.EQ,
RHS: &sql.Empty{},
},
},
Then: sql.Exprs{0},
Else: sql.NewIdent(key),
},
},
})
}
func tableStddev(set *sql.DataExpression, key string, val interface{}, when modify) {
var op sql.Token
switch when {
case _REMOVE:
op = sql.DEC
case _CHANGE:
op = sql.INC
}
set.Data = append(set.Data, &sql.ItemExpression{
LHS: sql.NewIdent("meta.__." + key + ".c"),
Op: op,
RHS: 1,
})
set.Data = append(set.Data, &sql.ItemExpression{
LHS: sql.NewIdent("meta.__." + key + ".t"),
Op: op,
RHS: val,
})
set.Data = append(set.Data, &sql.ItemExpression{
LHS: sql.NewIdent("meta.__." + key + ".m"),
Op: op,
RHS: &sql.BinaryExpression{
LHS: val,
Op: sql.MUL,
RHS: val,
},
})
// FIXME Need to ensure removed values update correctly
switch when {
case _REMOVE:
op = sql.SUB // FIXME This is incorrect
case _CHANGE:
op = sql.ADD
}
set.Data = append(set.Data, &sql.ItemExpression{
LHS: sql.NewIdent(key),
Op: sql.EQ,
RHS: &sql.FuncExpression{
Name: "math.sqrt",
Args: sql.Exprs{
&sql.BinaryExpression{
LHS: &sql.BinaryExpression{
LHS: &sql.BinaryExpression{
LHS: sql.NewIdent("meta.__." + key + ".c"),
Op: sql.MUL,
RHS: sql.NewIdent("meta.__." + key + ".m"),
},
Op: sql.SUB,
RHS: &sql.BinaryExpression{
LHS: sql.NewIdent("meta.__." + key + ".t"),
Op: sql.MUL,
RHS: sql.NewIdent("meta.__." + key + ".t"),
},
},
Op: sql.DIV,
RHS: &sql.BinaryExpression{
LHS: sql.NewIdent("meta.__." + key + ".c"),
Op: sql.MUL,
RHS: &sql.BinaryExpression{
LHS: sql.NewIdent("meta.__." + key + ".c"),
Op: sql.SUB,
RHS: 1,
},
},
},
},
},
})
set.Data = append(set.Data, &sql.ItemExpression{
LHS: sql.NewIdent(key),
Op: sql.EQ,
RHS: &sql.SubExpression{
&sql.IfelseStatement{
Cond: sql.Exprs{
&sql.BinaryExpression{
LHS: sql.NewIdent(key),
Op: sql.EQ,
RHS: &sql.Empty{},
},
},
Then: sql.Exprs{0},
Else: sql.NewIdent(key),
},
},
})
}
func tableVariance(set *sql.DataExpression, key string, val interface{}, when modify) {
var op sql.Token
switch when {
case _REMOVE:
op = sql.DEC
case _CHANGE:
op = sql.INC
}
set.Data = append(set.Data, &sql.ItemExpression{
LHS: sql.NewIdent("meta.__." + key + ".c"),
Op: op,
RHS: 1,
})
set.Data = append(set.Data, &sql.ItemExpression{
LHS: sql.NewIdent("meta.__." + key + ".t"),
Op: op,
RHS: val,
})
set.Data = append(set.Data, &sql.ItemExpression{
LHS: sql.NewIdent("meta.__." + key + ".m"),
Op: op,
RHS: &sql.BinaryExpression{
LHS: val,
Op: sql.MUL,
RHS: val,
},
})
// FIXME Need to ensure removed values update correctly
switch when {
case _REMOVE:
op = sql.SUB // FIXME This is incorrect
case _CHANGE:
op = sql.ADD
}
set.Data = append(set.Data, &sql.ItemExpression{
LHS: sql.NewIdent(key),
Op: sql.EQ,
RHS: &sql.BinaryExpression{
LHS: &sql.BinaryExpression{
LHS: &sql.BinaryExpression{
LHS: &sql.BinaryExpression{
LHS: sql.NewIdent("meta.__." + key + ".c"),
Op: sql.MUL,
RHS: sql.NewIdent("meta.__." + key + ".m"),
},
Op: sql.SUB,
RHS: &sql.BinaryExpression{
LHS: sql.NewIdent("meta.__." + key + ".t"),
Op: sql.MUL,
RHS: sql.NewIdent("meta.__." + key + ".t"),
},
},
Op: sql.DIV,
RHS: &sql.BinaryExpression{
LHS: sql.NewIdent("meta.__." + key + ".c"),
Op: sql.SUB,
RHS: 1,
},
},
Op: sql.DIV,
RHS: sql.NewIdent("meta.__." + key + ".c"),
},
})
set.Data = append(set.Data, &sql.ItemExpression{
LHS: sql.NewIdent(key),
Op: sql.EQ,
RHS: &sql.SubExpression{
&sql.IfelseStatement{
Cond: sql.Exprs{
&sql.BinaryExpression{
LHS: sql.NewIdent(key),
Op: sql.EQ,
RHS: &sql.Empty{},
},
},
Then: sql.Exprs{0},
Else: sql.NewIdent(key),
},
},
})
}

View file

@ -1,189 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"fmt"
"context"
"github.com/surrealdb/surrealdb/cnf"
"github.com/surrealdb/surrealdb/sql"
"github.com/surrealdb/surrealdb/util/data"
"github.com/surrealdb/surrealdb/util/keys"
)
func (e *executor) executeUpdate(ctx context.Context, stm *sql.UpdateStatement) ([]interface{}, error) {
if err := e.access(ctx, cnf.AuthNO); err != nil {
return nil, err
}
var what sql.Exprs
for _, val := range stm.What {
w, err := e.fetch(ctx, val, nil)
if err != nil {
return nil, err
}
what = append(what, w)
}
i := newIterator(e, ctx, stm, false)
for _, w := range what {
switch what := w.(type) {
default:
return nil, fmt.Errorf("Can not execute UPDATE query using value '%v'", what)
case *sql.Table:
key := &keys.Table{KV: KV, NS: e.ns, DB: e.db, TB: what.TB}
i.processTable(ctx, key)
case *sql.Ident:
key := &keys.Table{KV: KV, NS: e.ns, DB: e.db, TB: what.VA}
i.processTable(ctx, key)
case *sql.Thing:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db, TB: what.TB, ID: what.ID}
i.processThing(ctx, key)
case *sql.Model:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db, TB: what.TB, ID: nil}
i.processModel(ctx, key, what)
case *sql.Batch:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db, TB: what.TB, ID: nil}
i.processBatch(ctx, key, what)
// Result of subquery
case []interface{}:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db}
i.processOther(ctx, key, what)
// Result of subquery with LIMIT 1
case map[string]interface{}:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db}
i.processOther(ctx, key, []interface{}{what})
}
}
return i.Yield(ctx)
}
func (e *executor) fetchUpdate(ctx context.Context, stm *sql.UpdateStatement, doc *data.Doc) (interface{}, error) {
ctx = dive(ctx)
if doc != nil {
vars := data.New()
vars.Set(doc.Data(), varKeyParent)
if subs := ctx.Value(ctxKeySubs); subs != nil {
if subs, ok := subs.(*data.Doc); ok {
vars.Set(subs.Get(varKeyParents).Data(), varKeyParents)
}
} else {
vars.Array(varKeyParents)
}
vars.Append(doc.Data(), varKeyParents)
ctx = context.WithValue(ctx, ctxKeySubs, vars)
}
out, err := e.executeUpdate(ctx, stm)
if err != nil {
return nil, err
}
switch len(out) {
case 1:
return data.Consume(out).Get(docKeyOne, docKeyId).Data(), nil
default:
return data.Consume(out).Get(docKeyAll, docKeyId).Data(), nil
}
}
func (d *document) runUpdate(ctx context.Context, stm *sql.UpdateStatement) (interface{}, error) {
var ok bool
var err error
var met = _CREATE
if err = d.init(ctx); err != nil {
return nil, err
}
if err = d.lock(ctx); err != nil {
return nil, err
}
if err = d.setup(ctx); err != nil {
return nil, err
}
if d.val.Exi() == true {
met = _UPDATE
}
if ok, err = d.check(ctx, stm.Cond); err != nil {
return nil, err
} else if ok == false {
return nil, nil
}
if ok, err = d.allow(ctx, met); err != nil {
return nil, err
} else if ok == false {
return nil, nil
}
if err = d.merge(ctx, met, stm.Data); err != nil {
return nil, err
}
if ok, err = d.allow(ctx, met); err != nil {
return nil, err
} else if ok == false {
return nil, nil
}
if err = d.storeIndex(ctx); err != nil {
return nil, err
}
if err = d.storeThing(ctx); err != nil {
return nil, err
}
if err = d.table(ctx, met); err != nil {
return nil, err
}
if err = d.lives(ctx, met); err != nil {
return nil, err
}
if err = d.event(ctx, met); err != nil {
return nil, err
}
return d.yield(ctx, stm, stm.Echo)
}

View file

@ -1,664 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"time"
"testing"
"github.com/surrealdb/surrealdb/sql"
"github.com/surrealdb/surrealdb/util/data"
. "github.com/smartystreets/goconvey/convey"
)
func TestUpdate(t *testing.T) {
Convey("Update with invalid value", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
UPDATE 1;
UPDATE "one";
UPDATE ["many"];
UPDATE [{value:"one"}];
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 5)
So(res[1].Status, ShouldEqual, "ERR")
So(res[1].Detail, ShouldEqual, "Can not execute UPDATE query using value '1'")
So(res[2].Status, ShouldEqual, "ERR")
So(res[2].Detail, ShouldEqual, "Can not execute UPDATE query using value 'one'")
So(res[3].Status, ShouldEqual, "ERR")
So(res[3].Detail, ShouldEqual, "Can not execute UPDATE query using value '[many]'")
So(res[4].Status, ShouldEqual, "ERR")
So(res[4].Detail, ShouldEqual, "Can not execute UPDATE query using value '[map[value:one]]'")
})
Convey("Update record when it exists", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
UPDATE person:test;
UPDATE person:test;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 3)
So(res[1].Result, ShouldHaveLength, 1)
So(res[2].Result, ShouldHaveLength, 1)
})
Convey("Update unique record using `table`", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
UPDATE person, table("person");
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Result, ShouldHaveLength, 0)
})
Convey("Update specific record using `thing`", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
UPDATE person:test, thing("person", "test");
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Result, ShouldHaveLength, 2)
So(data.Consume(res[1].Result[0]).Get("meta.id").Data(), ShouldHaveLength, 4)
So(data.Consume(res[1].Result[0]).Get("meta.tb").Data(), ShouldEqual, "person")
So(data.Consume(res[1].Result[1]).Get("meta.id").Data(), ShouldHaveLength, 4)
So(data.Consume(res[1].Result[1]).Get("meta.tb").Data(), ShouldEqual, "person")
})
Convey("Update unique records using `batch`", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
UPDATE batch("person", ["1", "2", "person:3"]);
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Result, ShouldHaveLength, 3)
So(data.Consume(res[1].Result[0]).Get("meta.id").Data(), ShouldEqual, 1)
So(data.Consume(res[1].Result[2]).Get("meta.id").Data(), ShouldEqual, 3)
})
Convey("Update unique records using `model`", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
UPDATE |person:100|;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Result, ShouldHaveLength, 100)
So(data.Consume(res[1].Result[0]).Get("meta.id").Data(), ShouldHaveLength, 20)
So(data.Consume(res[1].Result[99]).Get("meta.id").Data(), ShouldHaveLength, 20)
})
Convey("Update sequential ascending records using `model`", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
UPDATE |person:1..100|;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Result, ShouldHaveLength, 100)
So(data.Consume(res[1].Result[0]).Get("meta.id").Data(), ShouldEqual, 1)
So(data.Consume(res[1].Result[99]).Get("meta.id").Data(), ShouldEqual, 100)
})
Convey("Update sequential descending records using `model`", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
UPDATE |person:100..1|;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Result, ShouldHaveLength, 100)
So(data.Consume(res[1].Result[0]).Get("meta.id").Data(), ShouldEqual, 100)
So(data.Consume(res[1].Result[99]).Get("meta.id").Data(), ShouldEqual, 1)
})
Convey("Update sequential ascending negative-to-positive records using `model`", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
UPDATE |person:-50..50|;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Result, ShouldHaveLength, 101)
So(data.Consume(res[1].Result[0]).Get("meta.id").Data(), ShouldEqual, -50)
So(data.Consume(res[1].Result[1]).Get("meta.id").Data(), ShouldEqual, -49)
So(data.Consume(res[1].Result[100]).Get("meta.id").Data(), ShouldEqual, 50)
})
Convey("Update sequential ascending decimal records using `model`", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
UPDATE |person:1,0.5..50|;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Result, ShouldHaveLength, 99)
So(data.Consume(res[1].Result[0]).Get("meta.id").Data(), ShouldEqual, 1)
So(data.Consume(res[1].Result[1]).Get("meta.id").Data(), ShouldEqual, 1.5)
So(data.Consume(res[1].Result[98]).Get("meta.id").Data(), ShouldEqual, 50)
})
Convey("Update sequential descending decimal records using `model`", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
UPDATE |person:50,0.5..1|;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Result, ShouldHaveLength, 99)
So(data.Consume(res[1].Result[0]).Get("meta.id").Data(), ShouldEqual, 50)
So(data.Consume(res[1].Result[1]).Get("meta.id").Data(), ShouldEqual, 49.5)
So(data.Consume(res[1].Result[98]).Get("meta.id").Data(), ShouldEqual, 1)
})
Convey("Update sequential ascending decimal negative-to-positive records using `model`", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
UPDATE |person:-50,0.5..50|;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 2)
So(res[1].Result, ShouldHaveLength, 201)
So(data.Consume(res[1].Result[0]).Get("meta.id").Data(), ShouldEqual, -50)
So(data.Consume(res[1].Result[1]).Get("meta.id").Data(), ShouldEqual, -49.5)
So(data.Consume(res[1].Result[200]).Get("meta.id").Data(), ShouldEqual, 50)
})
Convey("Parsing same ID using ints, floats, and strings", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
UPDATE person:1;
UPDATE person:1.0;
UPDATE person:1;
UPDATE person:1.0;
UPDATE person:1.0000;
SELECT name FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 7)
So(res[1].Result, ShouldHaveLength, 1)
So(res[2].Result, ShouldHaveLength, 1)
So(res[3].Result, ShouldHaveLength, 1)
So(res[4].Result, ShouldHaveLength, 1)
So(res[5].Result, ShouldHaveLength, 1)
So(res[6].Result, ShouldHaveLength, 1)
})
Convey("Updating with a timeout of 1ms returns an error", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
UPDATE |person:1..1000| TIMEOUT 1ms;
SELECT * FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 3)
So(res[1].Result, ShouldHaveLength, 0)
So(res[2].Result, ShouldHaveLength, 0)
So(res[1].Status, ShouldEqual, "ERR_TO")
So(res[1].Detail, ShouldEqual, "Query timeout of 1ms exceeded")
})
Convey("Update a record using CONTENT", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE person:test SET test="text";
UPDATE person:test CONTENT {"other":true};
SELECT * FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[1].Result, ShouldHaveLength, 1)
So(data.Consume(res[1].Result[0]).Get("test").Data(), ShouldEqual, "text")
So(data.Consume(res[1].Result[0]).Get("other").Data(), ShouldEqual, nil)
So(res[2].Result, ShouldHaveLength, 1)
So(data.Consume(res[2].Result[0]).Get("test").Data(), ShouldEqual, nil)
So(data.Consume(res[2].Result[0]).Get("other").Data(), ShouldEqual, true)
So(res[3].Result, ShouldHaveLength, 1)
So(data.Consume(res[3].Result[0]).Get("test").Data(), ShouldEqual, nil)
So(data.Consume(res[3].Result[0]).Get("other").Data(), ShouldEqual, true)
})
Convey("Update records using CONTENT", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE person:1 SET test="one";
CREATE person:2 SET test="two";
CREATE person:3 SET test="tre";
UPDATE person CONTENT {"other":true};
SELECT * FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 6)
So(res[1].Result, ShouldHaveLength, 1)
So(data.Consume(res[1].Result[0]).Get("test").Data(), ShouldEqual, "one")
So(data.Consume(res[1].Result[0]).Get("other").Data(), ShouldEqual, nil)
So(res[2].Result, ShouldHaveLength, 1)
So(data.Consume(res[2].Result[0]).Get("test").Data(), ShouldEqual, "two")
So(data.Consume(res[2].Result[0]).Get("other").Data(), ShouldEqual, nil)
So(res[3].Result, ShouldHaveLength, 1)
So(data.Consume(res[3].Result[0]).Get("test").Data(), ShouldEqual, "tre")
So(data.Consume(res[3].Result[0]).Get("other").Data(), ShouldEqual, nil)
So(res[4].Result, ShouldHaveLength, 3)
So(data.Consume(res[4].Result[0]).Get("test").Data(), ShouldEqual, nil)
So(data.Consume(res[4].Result[0]).Get("other").Data(), ShouldEqual, true)
So(data.Consume(res[4].Result[1]).Get("test").Data(), ShouldEqual, nil)
So(data.Consume(res[4].Result[1]).Get("other").Data(), ShouldEqual, true)
So(data.Consume(res[4].Result[2]).Get("test").Data(), ShouldEqual, nil)
So(data.Consume(res[4].Result[2]).Get("other").Data(), ShouldEqual, true)
So(res[5].Result, ShouldHaveLength, 3)
So(data.Consume(res[5].Result[0]).Get("test").Data(), ShouldEqual, nil)
So(data.Consume(res[5].Result[0]).Get("other").Data(), ShouldEqual, true)
So(data.Consume(res[5].Result[1]).Get("test").Data(), ShouldEqual, nil)
So(data.Consume(res[5].Result[1]).Get("other").Data(), ShouldEqual, true)
So(data.Consume(res[5].Result[2]).Get("test").Data(), ShouldEqual, nil)
So(data.Consume(res[5].Result[2]).Get("other").Data(), ShouldEqual, true)
})
Convey("Update a record using CONTENT stored in a $param", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
Let data = {"other":true};
CREATE person:test SET test="text";
UPDATE person:test CONTENT $data;
SELECT * FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 5)
So(res[2].Result, ShouldHaveLength, 1)
So(data.Consume(res[2].Result[0]).Get("test").Data(), ShouldEqual, "text")
So(data.Consume(res[2].Result[0]).Get("other").Data(), ShouldEqual, nil)
So(res[3].Result, ShouldHaveLength, 1)
So(data.Consume(res[3].Result[0]).Get("test").Data(), ShouldEqual, nil)
So(data.Consume(res[3].Result[0]).Get("other").Data(), ShouldEqual, true)
So(res[4].Result, ShouldHaveLength, 1)
So(data.Consume(res[4].Result[0]).Get("test").Data(), ShouldEqual, nil)
So(data.Consume(res[4].Result[0]).Get("other").Data(), ShouldEqual, true)
})
Convey("Update a record using MERGE", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE person:test SET test="text";
UPDATE person:test MERGE {"other":true};
SELECT * FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[1].Result, ShouldHaveLength, 1)
So(data.Consume(res[1].Result[0]).Get("test").Data(), ShouldEqual, "text")
So(data.Consume(res[1].Result[0]).Get("other").Data(), ShouldEqual, nil)
So(res[2].Result, ShouldHaveLength, 1)
So(data.Consume(res[2].Result[0]).Get("test").Data(), ShouldEqual, "text")
So(data.Consume(res[2].Result[0]).Get("other").Data(), ShouldEqual, true)
So(res[3].Result, ShouldHaveLength, 1)
So(data.Consume(res[3].Result[0]).Get("test").Data(), ShouldEqual, "text")
So(data.Consume(res[3].Result[0]).Get("other").Data(), ShouldEqual, true)
})
Convey("Update records using MERGE", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE person:1 SET test="one";
CREATE person:2 SET test="two";
CREATE person:3 SET test="tre";
UPDATE person MERGE {"other":true};
SELECT * FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 6)
So(res[1].Result, ShouldHaveLength, 1)
So(res[2].Result, ShouldHaveLength, 1)
So(res[3].Result, ShouldHaveLength, 1)
So(res[4].Result, ShouldHaveLength, 3)
So(data.Consume(res[4].Result[0]).Get("test").Data(), ShouldEqual, "one")
So(data.Consume(res[4].Result[0]).Get("other").Data(), ShouldEqual, true)
So(data.Consume(res[4].Result[1]).Get("test").Data(), ShouldEqual, "two")
So(data.Consume(res[4].Result[1]).Get("other").Data(), ShouldEqual, true)
So(data.Consume(res[4].Result[2]).Get("test").Data(), ShouldEqual, "tre")
So(data.Consume(res[4].Result[2]).Get("other").Data(), ShouldEqual, true)
So(res[5].Result, ShouldHaveLength, 3)
So(data.Consume(res[5].Result[0]).Get("test").Data(), ShouldEqual, "one")
So(data.Consume(res[5].Result[0]).Get("other").Data(), ShouldEqual, true)
So(data.Consume(res[5].Result[1]).Get("test").Data(), ShouldEqual, "two")
So(data.Consume(res[5].Result[1]).Get("other").Data(), ShouldEqual, true)
So(data.Consume(res[5].Result[2]).Get("test").Data(), ShouldEqual, "tre")
So(data.Consume(res[5].Result[2]).Get("other").Data(), ShouldEqual, true)
})
Convey("Update a record using MERGE stored in a $param", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
LET data = {"other":true};
CREATE person:test SET test="text";
UPDATE person:test MERGE $data;
SELECT * FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 5)
So(res[2].Result, ShouldHaveLength, 1)
So(data.Consume(res[2].Result[0]).Get("test").Data(), ShouldEqual, "text")
So(data.Consume(res[2].Result[0]).Get("other").Data(), ShouldEqual, nil)
So(res[3].Result, ShouldHaveLength, 1)
So(data.Consume(res[3].Result[0]).Get("test").Data(), ShouldEqual, "text")
So(data.Consume(res[3].Result[0]).Get("other").Data(), ShouldEqual, true)
So(res[4].Result, ShouldHaveLength, 1)
So(data.Consume(res[4].Result[0]).Get("test").Data(), ShouldEqual, "text")
So(data.Consume(res[4].Result[0]).Get("other").Data(), ShouldEqual, true)
})
Convey("Update a record using DIFF", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE person:test SET test="text";
UPDATE person:test DIFF [{"op":"add","path":"/other","value":true}];
SELECT * FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[1].Result, ShouldHaveLength, 1)
So(data.Consume(res[1].Result[0]).Get("test").Data(), ShouldEqual, "text")
So(data.Consume(res[1].Result[0]).Get("other").Data(), ShouldEqual, nil)
So(res[2].Result, ShouldHaveLength, 1)
So(data.Consume(res[2].Result[0]).Get("test").Data(), ShouldEqual, "text")
So(data.Consume(res[2].Result[0]).Get("other").Data(), ShouldEqual, true)
So(res[3].Result, ShouldHaveLength, 1)
So(data.Consume(res[3].Result[0]).Get("test").Data(), ShouldEqual, "text")
So(data.Consume(res[3].Result[0]).Get("other").Data(), ShouldEqual, true)
})
Convey("Update a record using DIFF stored in a $param", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
LET data = [{"op":"add","path":"/other","value":true}];
CREATE person:test SET test="text";
UPDATE person:test DIFF $data;
SELECT * FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 5)
So(res[2].Result, ShouldHaveLength, 1)
So(data.Consume(res[2].Result[0]).Get("test").Data(), ShouldEqual, "text")
So(data.Consume(res[2].Result[0]).Get("other").Data(), ShouldEqual, nil)
So(res[3].Result, ShouldHaveLength, 1)
So(data.Consume(res[3].Result[0]).Get("test").Data(), ShouldEqual, "text")
So(data.Consume(res[3].Result[0]).Get("other").Data(), ShouldEqual, true)
So(res[4].Result, ShouldHaveLength, 1)
So(data.Consume(res[4].Result[0]).Get("test").Data(), ShouldEqual, "text")
So(data.Consume(res[4].Result[0]).Get("other").Data(), ShouldEqual, true)
})
Convey("Update records using DIFF", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE person:1 SET test="one";
CREATE person:2 SET test="two";
CREATE person:3 SET test="tre";
UPDATE person DIFF [{"op":"add","path":"/other","value":true}];
SELECT * FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 6)
So(res[1].Result, ShouldHaveLength, 1)
So(res[2].Result, ShouldHaveLength, 1)
So(res[3].Result, ShouldHaveLength, 1)
So(res[4].Result, ShouldHaveLength, 3)
So(data.Consume(res[4].Result[0]).Get("test").Data(), ShouldEqual, "one")
So(data.Consume(res[4].Result[0]).Get("other").Data(), ShouldEqual, true)
So(data.Consume(res[4].Result[1]).Get("test").Data(), ShouldEqual, "two")
So(data.Consume(res[4].Result[1]).Get("other").Data(), ShouldEqual, true)
So(data.Consume(res[4].Result[2]).Get("test").Data(), ShouldEqual, "tre")
So(data.Consume(res[4].Result[2]).Get("other").Data(), ShouldEqual, true)
So(res[5].Result, ShouldHaveLength, 3)
So(data.Consume(res[5].Result[0]).Get("test").Data(), ShouldEqual, "one")
So(data.Consume(res[5].Result[0]).Get("other").Data(), ShouldEqual, true)
So(data.Consume(res[5].Result[1]).Get("test").Data(), ShouldEqual, "two")
So(data.Consume(res[5].Result[1]).Get("other").Data(), ShouldEqual, true)
So(data.Consume(res[5].Result[2]).Get("test").Data(), ShouldEqual, "tre")
So(data.Consume(res[5].Result[2]).Get("other").Data(), ShouldEqual, true)
})
Convey("Update a record using NULL to unset a field", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE person:test SET test = true;
UPDATE person:test SET test = NULL;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 3)
So(res[1].Result, ShouldHaveLength, 1)
So(data.Consume(res[1].Result[0]).Get("test").Data(), ShouldEqual, true)
So(res[2].Result, ShouldHaveLength, 1)
So(data.Consume(res[2].Result[0]).Get("test").Data(), ShouldEqual, nil)
_, ok := res[2].Result[0].(map[string]interface{})["test"]
So(ok, ShouldEqual, true)
})
Convey("Update a record using VOID to remove a field", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE person:test SET test = true;
UPDATE person:test SET test = VOID;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 3)
So(res[1].Result, ShouldHaveLength, 1)
So(data.Consume(res[1].Result[0]).Get("test").Data(), ShouldEqual, true)
So(res[2].Result, ShouldHaveLength, 1)
So(data.Consume(res[2].Result[0]).Get("test").Data(), ShouldEqual, nil)
_, ok := res[2].Result[0].(map[string]interface{})["test"]
So(ok, ShouldEqual, false)
})
Convey("Update a set of records, but only if they exist", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE person:1 SET test = true;
UPDATE |person:1..3| SET test = false WHERE id != VOID;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 3)
So(res[1].Result, ShouldHaveLength, 1)
So(data.Consume(res[1].Result[0]).Get("test").Data(), ShouldEqual, true)
So(res[2].Result, ShouldHaveLength, 1)
So(data.Consume(res[2].Result[0]).Get("test").Data(), ShouldEqual, false)
})
Convey("Update a set of records using CONTENT with embedded times / records", t, func() {
clock, _ := time.Parse(time.RFC3339, "1987-06-22T08:00:00.123456789Z")
setupDB(1)
txt := `
USE NS test DB test;
CREATE person:test CONTENT {"time":"1987-06-22T08:00:00.123456789Z","test":"person:other"};
SELECT * FROM person;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 3)
So(res[1].Result, ShouldHaveLength, 1)
So(data.Consume(res[1].Result[0]).Get("time").Data(), ShouldEqual, clock)
So(data.Consume(res[1].Result[0]).Get("test").Data(), ShouldResemble, sql.NewThing("person", "other"))
So(res[2].Result, ShouldHaveLength, 1)
So(data.Consume(res[2].Result[0]).Get("time").Data(), ShouldEqual, clock)
So(data.Consume(res[2].Result[0]).Get("test").Data(), ShouldResemble, sql.NewThing("person", "other"))
})
}

View file

@ -1,152 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"fmt"
"context"
"github.com/surrealdb/surrealdb/cnf"
"github.com/surrealdb/surrealdb/sql"
"github.com/surrealdb/surrealdb/util/data"
"github.com/surrealdb/surrealdb/util/keys"
)
func (e *executor) executeUpsert(ctx context.Context, stm *sql.UpsertStatement) ([]interface{}, error) {
if err := e.access(ctx, cnf.AuthNO); err != nil {
return nil, err
}
data, err := e.fetch(ctx, stm.Data, nil)
if err != nil {
return nil, err
}
i := newIterator(e, ctx, stm, false)
switch data := data.(type) {
default:
return nil, fmt.Errorf("Can not execute UPSERT query using value '%v'", data)
case []interface{}:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db, TB: stm.Into.TB}
i.processArray(ctx, key, data)
case map[string]interface{}:
key := &keys.Thing{KV: KV, NS: e.ns, DB: e.db, TB: stm.Into.TB}
i.processArray(ctx, key, []interface{}{data})
}
return i.Yield(ctx)
}
func (e *executor) fetchUpsert(ctx context.Context, stm *sql.UpsertStatement, doc *data.Doc) (interface{}, error) {
ctx = dive(ctx)
if doc != nil {
vars := data.New()
vars.Set(doc.Data(), varKeyParent)
if subs := ctx.Value(ctxKeySubs); subs != nil {
if subs, ok := subs.(*data.Doc); ok {
vars.Set(subs.Get(varKeyParents).Data(), varKeyParents)
}
} else {
vars.Array(varKeyParents)
}
vars.Append(doc.Data(), varKeyParents)
ctx = context.WithValue(ctx, ctxKeySubs, vars)
}
out, err := e.executeUpsert(ctx, stm)
if err != nil {
return nil, err
}
switch len(out) {
case 1:
return data.Consume(out).Get(docKeyOne, docKeyId).Data(), nil
default:
return data.Consume(out).Get(docKeyAll, docKeyId).Data(), nil
}
}
func (d *document) runUpsert(ctx context.Context, stm *sql.UpsertStatement) (interface{}, error) {
var ok bool
var err error
var met = _CREATE
if err = d.init(ctx); err != nil {
return nil, err
}
if err = d.lock(ctx); err != nil {
return nil, err
}
if err = d.setup(ctx); err != nil {
return nil, err
}
if d.val.Exi() == true {
met = _UPDATE
}
if ok, err = d.allow(ctx, met); err != nil {
return nil, err
} else if ok == false {
return nil, nil
}
if err = d.merge(ctx, met, nil); err != nil {
return nil, err
}
if ok, err = d.allow(ctx, met); err != nil {
return nil, err
} else if ok == false {
return nil, nil
}
if err = d.storeIndex(ctx); err != nil {
return nil, err
}
if err = d.storeThing(ctx); err != nil {
return nil, err
}
if err = d.table(ctx, met); err != nil {
return nil, err
}
if err = d.lives(ctx, met); err != nil {
return nil, err
}
if err = d.event(ctx, met); err != nil {
return nil, err
}
return d.yield(ctx, stm, stm.Echo)
}

View file

@ -1,249 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"testing"
"github.com/surrealdb/surrealdb/sql"
"github.com/surrealdb/surrealdb/util/data"
. "github.com/smartystreets/goconvey/convey"
)
func TestUpsert(t *testing.T) {
Convey("Upsert with invalid value", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
UPSERT 1 INTO user;
UPSERT "one" INTO user;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 3)
So(res[1].Status, ShouldEqual, "ERR")
So(res[1].Detail, ShouldEqual, "Can not execute UPSERT query using value '1'")
So(res[2].Status, ShouldEqual, "ERR")
So(res[2].Detail, ShouldEqual, "Can not execute UPSERT query using value 'one'")
})
Convey("Upsert a set of ids from one table to another table", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE person:1 SET test="one";
CREATE person:2 SET test="two";
CREATE person:3 SET test="tre";
UPSERT (SELECT id FROM person) INTO user;
SELECT * FROM person, user;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 6)
So(res[1].Result, ShouldHaveLength, 1)
So(res[2].Result, ShouldHaveLength, 1)
So(res[3].Result, ShouldHaveLength, 1)
So(res[4].Result, ShouldHaveLength, 3)
So(data.Consume(res[4].Result[0]).Get("test").Data(), ShouldEqual, nil)
So(data.Consume(res[4].Result[0]).Get("id").Data(), ShouldResemble, sql.NewThing("user", 1))
So(data.Consume(res[4].Result[1]).Get("test").Data(), ShouldEqual, nil)
So(data.Consume(res[4].Result[1]).Get("id").Data(), ShouldResemble, sql.NewThing("user", 2))
So(data.Consume(res[4].Result[2]).Get("test").Data(), ShouldEqual, nil)
So(data.Consume(res[4].Result[2]).Get("id").Data(), ShouldResemble, sql.NewThing("user", 3))
So(res[5].Result, ShouldHaveLength, 6)
So(data.Consume(res[5].Result[0]).Get("test").Data(), ShouldEqual, "one")
So(data.Consume(res[5].Result[0]).Get("id").Data(), ShouldResemble, sql.NewThing("person", 1))
So(data.Consume(res[5].Result[1]).Get("test").Data(), ShouldEqual, "two")
So(data.Consume(res[5].Result[1]).Get("id").Data(), ShouldResemble, sql.NewThing("person", 2))
So(data.Consume(res[5].Result[2]).Get("test").Data(), ShouldEqual, "tre")
So(data.Consume(res[5].Result[2]).Get("id").Data(), ShouldResemble, sql.NewThing("person", 3))
So(data.Consume(res[5].Result[3]).Get("test").Data(), ShouldEqual, nil)
So(data.Consume(res[5].Result[3]).Get("id").Data(), ShouldResemble, sql.NewThing("user", 1))
So(data.Consume(res[5].Result[4]).Get("test").Data(), ShouldEqual, nil)
So(data.Consume(res[5].Result[4]).Get("id").Data(), ShouldResemble, sql.NewThing("user", 2))
So(data.Consume(res[5].Result[5]).Get("test").Data(), ShouldEqual, nil)
So(data.Consume(res[5].Result[5]).Get("id").Data(), ShouldResemble, sql.NewThing("user", 3))
})
Convey("Upsert a set of records from one table to another table", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE person:1 SET test="one";
CREATE person:2 SET test="two";
CREATE person:3 SET test="tre";
UPSERT (SELECT * FROM person) INTO user;
SELECT * FROM person, user;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 6)
So(res[1].Result, ShouldHaveLength, 1)
So(res[2].Result, ShouldHaveLength, 1)
So(res[3].Result, ShouldHaveLength, 1)
So(res[4].Result, ShouldHaveLength, 3)
So(data.Consume(res[4].Result[0]).Get("test").Data(), ShouldEqual, "one")
So(data.Consume(res[4].Result[0]).Get("id").Data(), ShouldResemble, sql.NewThing("user", 1))
So(data.Consume(res[4].Result[1]).Get("test").Data(), ShouldEqual, "two")
So(data.Consume(res[4].Result[1]).Get("id").Data(), ShouldResemble, sql.NewThing("user", 2))
So(data.Consume(res[4].Result[2]).Get("test").Data(), ShouldEqual, "tre")
So(data.Consume(res[4].Result[2]).Get("id").Data(), ShouldResemble, sql.NewThing("user", 3))
So(res[5].Result, ShouldHaveLength, 6)
So(data.Consume(res[5].Result[0]).Get("test").Data(), ShouldEqual, "one")
So(data.Consume(res[5].Result[0]).Get("id").Data(), ShouldResemble, sql.NewThing("person", 1))
So(data.Consume(res[5].Result[1]).Get("test").Data(), ShouldEqual, "two")
So(data.Consume(res[5].Result[1]).Get("id").Data(), ShouldResemble, sql.NewThing("person", 2))
So(data.Consume(res[5].Result[2]).Get("test").Data(), ShouldEqual, "tre")
So(data.Consume(res[5].Result[2]).Get("id").Data(), ShouldResemble, sql.NewThing("person", 3))
So(data.Consume(res[5].Result[3]).Get("test").Data(), ShouldEqual, "one")
So(data.Consume(res[5].Result[3]).Get("id").Data(), ShouldResemble, sql.NewThing("user", 1))
So(data.Consume(res[5].Result[4]).Get("test").Data(), ShouldEqual, "two")
So(data.Consume(res[5].Result[4]).Get("id").Data(), ShouldResemble, sql.NewThing("user", 2))
So(data.Consume(res[5].Result[5]).Get("test").Data(), ShouldEqual, "tre")
So(data.Consume(res[5].Result[5]).Get("id").Data(), ShouldResemble, sql.NewThing("user", 3))
})
Convey("Upsert a set of records from data with an ID", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
LET data = { "id": 360, "admin":true, "login":"joe" };
UPSERT $data INTO users;
UPSERT $data INTO users;
SELECT * FROM users;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 5)
So(res[2].Result, ShouldHaveLength, 1)
So(data.Consume(res[2].Result[0]).Get("login").Data(), ShouldEqual, "joe")
So(data.Consume(res[2].Result[0]).Get("meta.id").Data(), ShouldEqual, 360)
So(res[3].Result, ShouldHaveLength, 1)
So(data.Consume(res[3].Result[0]).Get("login").Data(), ShouldEqual, "joe")
So(data.Consume(res[3].Result[0]).Get("meta.id").Data(), ShouldEqual, 360)
So(res[4].Result, ShouldHaveLength, 1)
So(data.Consume(res[4].Result[0]).Get("login").Data(), ShouldEqual, "joe")
So(data.Consume(res[4].Result[0]).Get("meta.id").Data(), ShouldEqual, 360)
})
Convey("Upsert a set of records from data without an ID", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
LET data = { "admin":true, "login":"tom" };
UPSERT $data INTO users;
SELECT * FROM users;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[2].Result, ShouldHaveLength, 1)
So(data.Consume(res[2].Result[0]).Get("login").Data(), ShouldEqual, "tom")
So(data.Consume(res[2].Result[0]).Get("meta.id").Data(), ShouldHaveLength, 20)
So(res[3].Result, ShouldHaveLength, 1)
So(data.Consume(res[3].Result[0]).Get("login").Data(), ShouldEqual, "tom")
So(data.Consume(res[3].Result[0]).Get("meta.id").Data(), ShouldHaveLength, 20)
})
Convey("Upsert a set of records from an array of data with IDs", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
LET data = [
{ "id": 360, "admin":true, "login":"joe" },
{ "id": 200, "admin":false, "login":"mike" },
{ "id": "test", "admin":false, "login":"tester" },
];
UPSERT $data INTO users;
UPSERT $data INTO users;
SELECT * FROM users;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 5)
So(res[2].Result, ShouldHaveLength, 3)
So(data.Consume(res[2].Result[0]).Get("login").Data(), ShouldEqual, "joe")
So(data.Consume(res[2].Result[0]).Get("meta.id").Data(), ShouldEqual, 360)
So(data.Consume(res[2].Result[1]).Get("login").Data(), ShouldEqual, "mike")
So(data.Consume(res[2].Result[1]).Get("meta.id").Data(), ShouldEqual, 200)
So(data.Consume(res[2].Result[2]).Get("login").Data(), ShouldEqual, "tester")
So(data.Consume(res[2].Result[2]).Get("meta.id").Data(), ShouldEqual, "test")
So(res[3].Result, ShouldHaveLength, 3)
So(data.Consume(res[3].Result[0]).Get("login").Data(), ShouldEqual, "joe")
So(data.Consume(res[3].Result[0]).Get("meta.id").Data(), ShouldEqual, 360)
So(data.Consume(res[3].Result[1]).Get("login").Data(), ShouldEqual, "mike")
So(data.Consume(res[3].Result[1]).Get("meta.id").Data(), ShouldEqual, 200)
So(data.Consume(res[3].Result[2]).Get("login").Data(), ShouldEqual, "tester")
So(data.Consume(res[3].Result[2]).Get("meta.id").Data(), ShouldEqual, "test")
So(res[4].Result, ShouldHaveLength, 3)
So(data.Consume(res[4].Result[0]).Get("login").Data(), ShouldEqual, "mike")
So(data.Consume(res[4].Result[0]).Get("meta.id").Data(), ShouldEqual, 200)
So(data.Consume(res[4].Result[1]).Get("login").Data(), ShouldEqual, "joe")
So(data.Consume(res[4].Result[1]).Get("meta.id").Data(), ShouldEqual, 360)
So(data.Consume(res[4].Result[2]).Get("login").Data(), ShouldEqual, "tester")
So(data.Consume(res[4].Result[2]).Get("meta.id").Data(), ShouldEqual, "test")
})
Convey("Upsert a set of records from an array of data without IDs", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
LET data = [
{ "admin":true, "login":"tom" },
];
UPSERT $data INTO users;
SELECT * FROM users;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[2].Result, ShouldHaveLength, 1)
So(data.Consume(res[2].Result[0]).Get("login").Data(), ShouldEqual, "tom")
So(data.Consume(res[2].Result[0]).Get("meta.id").Data(), ShouldHaveLength, 20)
So(res[3].Result, ShouldHaveLength, 1)
So(data.Consume(res[3].Result[0]).Get("login").Data(), ShouldEqual, "tom")
So(data.Consume(res[3].Result[0]).Get("meta.id").Data(), ShouldHaveLength, 20)
})
}

View file

@ -1,98 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"context"
"github.com/surrealdb/surrealdb/cnf"
"github.com/surrealdb/surrealdb/sql"
)
func (e *executor) executeUse(ctx context.Context, stm *sql.UseStatement) (out []interface{}, err error) {
// If a NAMESPACE has been defined, then
// process the permissions, or return an
// error if we can't specify a namespace.
if len(stm.NS) > 0 {
if perm(ctx) == cnf.AuthKV {
e.ns = stm.NS
}
if perm(ctx) == cnf.AuthNS {
if e.ns != stm.NS {
err = new(QueryError)
}
}
if perm(ctx) == cnf.AuthDB {
if e.ns != stm.NS {
err = new(QueryError)
}
}
if perm(ctx) == cnf.AuthSC {
if e.ns != stm.NS {
err = new(QueryError)
}
}
if perm(ctx) == cnf.AuthNO {
e.ns = stm.NS
}
}
// If a DATABASE has been defined, then
// process the permissions, or return an
// error if we can't specify a database.
if len(stm.DB) > 0 {
if perm(ctx) == cnf.AuthKV {
e.db = stm.DB
}
if perm(ctx) == cnf.AuthNS {
e.db = stm.DB
}
if perm(ctx) == cnf.AuthDB {
if e.db != stm.DB {
err = new(QueryError)
}
}
if perm(ctx) == cnf.AuthSC {
if e.db != stm.DB {
err = new(QueryError)
}
}
if perm(ctx) == cnf.AuthNO {
e.db = stm.DB
}
}
if err != nil {
e.ns, e.db = "", ""
}
return
}

View file

@ -1,123 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"errors"
"runtime"
)
type method int8
const (
_SELECT method = iota
_CREATE
_UPDATE
_DELETE
)
type modify int8
const (
_REMOVE modify = iota
_CHANGE
)
const (
docKeyId = "id"
docKeyOne = "0"
docKeyAll = "*"
)
const (
logKeyId = "id"
logKeyNS = "ns"
logKeyDB = "db"
logKeySql = "sql"
logKeyExe = "exe"
logKeyKind = "kind"
logKeyVars = "vars"
logKeyTime = "time"
logKeySpan = "span"
logKeyTrace = "trace"
logKeyError = "error"
logKeyStack = "stack"
logKeyFibre = "fibre"
)
const (
ctxKeyId = "id"
ctxKeyDive = "dive"
ctxKeyVars = "vars"
ctxKeySubs = "subs"
ctxKeySpec = "spec"
ctxKeyAuth = "auth"
ctxKeyKind = "kind"
ctxKeyForce = "force"
ctxKeyFibre = "fibre"
ctxKeyVersion = "version"
)
const (
varKeyId = "id"
varKeyIp = "ip"
varKeyEnv = "ENV"
varKeyAuth = "auth"
varKeyUniq = "uniq"
varKeyThis = "this"
varKeyScope = "scope"
varKeyValue = "value"
varKeyAfter = "after"
varKeyBefore = "before"
varKeyMethod = "method"
varKeyOrigin = "origin"
varKeyParent = "parent"
varKeyParents = "parents"
varKeySession = "session"
)
var (
// workerCount specifies how many workers should be used
// to process each query statement concurrently.
workerCount = runtime.NumCPU()
// maxRecursiveQueries specifies how many queries will be
// processed recursively before the query is cancelled.
maxRecursiveQueries = uint32(16)
// queryIdentFailed occurs when a permission query asks
// for a field, meaning a document has to be fetched.
queryIdentFailed = errors.New("Found ident but no doc available")
// errQueryNotExecuted occurs when a transaction has
// failed, and the following queries are not executed.
errQueryNotExecuted = errors.New("Query not executed")
// errRaceCondition occurs when a record which is locked
// for editing, is updated from within a subquery.
errRaceCondition = errors.New("Failed to update the same document recursively")
// errRecursiveOverload occurs when too many subqueries
// are executed within one other, causing an endless loop.
errRecursiveOverload = errors.New("Infinite loop when running recursive subqueries")
// errFeatureNotImplemented occurs when a feature which
// has not yet been implemented, has been used in a query.
errFeatureNotImplemented = errors.New("Feature is not yet implemented")
// paramSearchKeys specifies the order in which context
// variables should be checked for any specified value.
paramSearchKeys = []string{ctxKeySpec, ctxKeySubs, ctxKeyVars}
)

View file

@ -1,239 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"context"
"github.com/surrealdb/surrealdb/sql"
"github.com/surrealdb/surrealdb/util/data"
)
func (d *document) cold(ctx context.Context) (doc *data.Doc, err error) {
// We need to copy the document so that
// we can add and remove the fields which
// are relevant to the particular query.
doc = d.initial.Copy()
// If we are not authenticated using DB,
// NS, or KV level, then we need to check
// document permissions for this query.
if err = d.perms(ctx, doc); err != nil {
return nil, err
}
return
}
func (d *document) cnow(ctx context.Context) (doc *data.Doc, err error) {
// We need to copy the document so that
// we can add and remove the fields which
// are relevant to the particular query.
doc = d.current.Copy()
// If we are not authenticated using DB,
// NS, or KV level, then we need to check
// document permissions for this query.
if err = d.perms(ctx, doc); err != nil {
return nil, err
}
return
}
func (d *document) yield(ctx context.Context, stm sql.Statement, output sql.Token) (interface{}, error) {
var exps sql.Fields
var grps sql.Groups
var fchs sql.Fetchs
switch stm := stm.(type) {
case *sql.LiveStatement:
exps = stm.Expr
fchs = stm.Fetch
case *sql.SelectStatement:
exps = stm.Expr
grps = stm.Group
fchs = stm.Fetch
}
// If there are no field expressions
// then this was not a LIVE or SELECT
// query, and therefore the query will
// have an output format specified.
if len(exps) == 0 {
switch output {
default:
return nil, nil
case sql.AFTER:
doc, err := d.cnow(ctx)
if err != nil {
return nil, err
}
return doc.Data(), nil
case sql.BEFORE:
doc, err := d.cold(ctx)
if err != nil {
return nil, err
}
return doc.Data(), nil
}
}
// But if there are field expresions
// then this query is a LIVE or SELECT
// query, and we must output only the
// desired fields in the output.
var out = data.New()
doc, err := d.cnow(ctx)
if err != nil {
return nil, err
}
// First of all, check to see if an ALL
// expression has been specified, and if
// it has then use the full document.
for _, e := range exps {
if _, ok := e.Expr.(*sql.All); ok {
out = doc
break
}
}
// Ensure that all output fields are
// available in subsequent expressions
// using the $this parameter.
vars := data.New()
vars.Set(out.Data(), varKeyThis)
ctx = context.WithValue(ctx, ctxKeySpec, vars)
// Next let's see the field expressions
// which have been requested, and add
// these to the output document.
for _, e := range exps {
switch v := e.Expr.(type) {
case *sql.All:
break
default:
// If the query has a GROUP BY expression
// then let's check if this is an aggregate
// function, and if it is then pass the
// first argument directly through.
if len(grps) > 0 {
if f, ok := e.Expr.(*sql.FuncExpression); ok && f.Aggr {
v, err := d.i.e.fetch(ctx, f.Args[0], doc)
if err != nil {
return nil, err
}
out.Set(v, f.String())
continue
}
}
// Otherwise treat the field normally, and
// calculate the value to be inserted into
// the final output document.
o, err := d.i.e.fetch(ctx, v, doc)
if err != nil {
return nil, err
}
switch o {
case doc:
out.Set(nil, e.Field)
default:
out.Set(o, e.Field)
}
}
}
// Finally let's see if there are any
// FETCH expressions, so that we can
// follow links to other records.
for _, e := range fchs {
switch v := e.Expr.(type) {
case *sql.All:
break
case *sql.Ident:
out.Walk(func(key string, val interface{}, exi bool) error {
switch res := val.(type) {
case []interface{}:
val := make([]interface{}, len(res))
for k, v := range res {
switch tng := v.(type) {
case *sql.Thing:
val[k], _ = d.i.e.fetchThing(ctx, tng, doc)
default:
val[k] = v
}
}
out.Set(val, key)
case *sql.Thing:
val, _ = d.i.e.fetchThing(ctx, res, doc)
out.Set(val, key)
}
return nil
}, v.VA)
}
}
// Remove all temporary metadata from
// the record. This is not visible when
// outputting, but is stored in the DB.
doc.Del("meta.__")
// Output the document with the correct
// specified fields, linked records and
// any aggregated group by clauses.
return out.Data(), nil
}

View file

@ -1,51 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"testing"
"github.com/surrealdb/surrealdb/util/data"
. "github.com/smartystreets/goconvey/convey"
)
func TestYield(t *testing.T) {
Convey("Yield different responses when modifying a record", t, func() {
setupDB(1)
txt := `
USE NS test DB test;
CREATE person:test SET test=1 RETURN AFTER;
UPDATE person:test SET test=2 RETURN BEFORE;
UPDATE person:test SET test=3 RETURN NONE;
DELETE person:test RETURN BEFORE;
`
res, err := Execute(permsKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 5)
So(res[1].Result, ShouldHaveLength, 1)
So(data.Consume(res[1].Result[0]).Get("test").Data(), ShouldEqual, 1)
So(res[2].Result, ShouldHaveLength, 1)
So(data.Consume(res[2].Result[0]).Get("test").Data(), ShouldEqual, 1)
So(res[3].Result, ShouldHaveLength, 0)
So(res[4].Result, ShouldHaveLength, 1)
So(data.Consume(res[4].Result[0]).Get("test").Data(), ShouldEqual, 3)
})
}

View file

@ -1,62 +0,0 @@
# Endpoints
This document describes RESTful http endpoints which can be used to query and manipulate the database.
---
### SQL
The SQL endpoint allows you to query the database using any SQL query supported by Surreal. The content body must contain the properly formatted SQL queries which should be executed by the database. It may include a batch of SQL statements separated by a semicolon.
- `POST` `https://api.surreal.io/sql`
---
### Key Value
The key value endpoints allow you to manipulate the database records without needing to use SQL. It only includes a small portion of the functionality available with SQL queries. The endpoints enable you to use the database as if it were an API, using multi-tenancy separation, and multi-level authentication for preventing access to specific data within a database.
- `GET` `https://api.surreal.io/key/{table}`
```sql
SELECT * FROM {table}
```
- `POST` `https://api.surreal.io/key/{table}`
```sql
CREATE {table}
```
- `DELETE` `https://api.surreal.io/key/{table}`
```sql
DELETE {table}
```
- `GET` `https://api.surreal.io/key/{table}/{key}`
```sql
SELECT * FROM @{table}:{key}
```
- `PUT` `https://api.surreal.io/key/{table}/{key}`
```sql
UPDATE @{table}:{key} CONTENT {} RETURN AFTER
```
- `POST` `https://api.surreal.io/key/{table}/{key}`
```sql
CREATE @{table}:{key} CONTENT {} RETURN AFTER
```
- `PATCH` `https://api.surreal.io/key/{table}/{key}`
```sql
MODIFY @{table}:{key} DIFF {} RETURN AFTER
```
- `TRACE` `https://api.surreal.io/key/{table}/{key}`
```sql
SELECT HISTORY FROM @{table}:{key}
```
- `DELETE` `https://api.surreal.io/key/{table}/{key}`
```sql
DELETE @{table}:{key}
```

View file

@ -1,409 +0,0 @@
# Queries
This document describes example SQL queries which can be used to query the database.
#### USE
```sql
-- Specify a namespace to use for future sql commands
USE NAMESPACE abcum;
-- Specify a database to use for future sql commands
USE DATABASE acreon;
-- Specify a namespace and database to use in one sql query
USE NAMESPACE abcum DATABASE acreon;
```
#### INFO
```sql
-- Retrive info for the namespace
INFO FOR NAMESPACE;
-- Retrive info for the database
INFO FOR DATABASE;
-- Retrive info for a specific table
INFO FOR TABLE person;
```
#### DEFINE NAMESPACE
```sql
-- Define a namespace
DEFINE NAMESPACE abcum;
-- Remove a namespace and all data
REMOVE NAMESPACE abcum;
```
#### DEFINE DATABASE
```sql
-- Define a database
DEFINE DATABASE acreon;
-- Remove a database and all data
REMOVE DATABASE acreon;
```
#### DEFINE LOGIN
```sql
-- Define a user account on the namespace
DEFINE LOGIN `tobie@abcum.com` ON NAMESPACE PASSWORD '192837192837192837';
-- Remove a user account from the namespace
REMOVE LOGIN `tobie@abcum.com` ON NAMESPACE;
-- Define a user account on the database
DEFINE LOGIN `tobie@abcum.com` ON DATABASE PASSWORD '192837192837192837';
-- Remove a user account from the database
REMOVE LOGIN `tobie@abcum.com` ON DATABASE;
```
#### DEFINE TOKEN
```sql
-- Define a signing token on the namespace
DEFINE TOKEN `default` ON NAMESPACE TYPE HS256 VALUE "secretkey";
-- Define a signing token public key on the namespace
DEFINE TOKEN `default` ON NAMESPACE TYPE RS256 VALUE "-----BEGIN PUBLIC KEY----- MIGfMA0G...";
-- Remove a signing token from the namespace
REMOVE TOKEN `default` ON NAMESPACE;
-- Define a signing token on the database
DEFINE TOKEN `default` ON DATABASE TYPE HS256 VALUE "secretkey";
-- Define a signing token public key on the database
DEFINE TOKEN `default` ON DATABASE TYPE HS256 VALUE "-----BEGIN PUBLIC KEY----- MIGfMA0G...";
-- Remove a signing token from the database
REMOVE TOKEN `default` ON DATABASE;
```
#### DEFINE SCOPE
```sql
-- Define an authentication scope named 'account'
DEFINE SCOPE account SESSION 1h SIGNUP AS (CREATE admin SET email=$user, pass=bcrypt.generate($pass), account=(UPDATE AND UPSERT @account:$account SET name=$accountname)) SIGNIN AS (SELECT * FROM admin WHERE email=$user AND bcrypt.compare(pass, $pass));
-- Remove the authentication scope named 'account'
REMOVE SCOPE account;
-- Define an authentication scope named 'profile'
DEFINE SCOPE profile SESSION 24h SIGNUP AS (CREATE person SET email=$user, pass=bcrypt.generate($pass)) SIGNIN AS (SELECT * FROM person WHERE email=$user AND bcrypt.compare(pass, $pass));
-- Remove the authentication scope named 'profile'
REMOVE SCOPE profile;
```
#### DEFINE TABLE
```sql
-- Define a new table on the database
DEFINE TABLE person;
-- Remove a table from the database
REMOVE TABLE person;
-- Define a new table as schemaless
DEFINE TABLE items SCHEMALESS;
-- Define a new table as schemafull
DEFINE TABLE items SCHEMAFULL;
DEFINE TABLE shot SHARD ON course, hole;
SELECT * FROM shot::{course=$course, hole=$hole};
SELECT * FROM shot WHERE course = $course AND hole = $hole;
-- Define a new table as with no scope permissions
DEFINE TABLE items PERMISSIONS NONE;
-- Define a new table as with full scope permissions
DEFINE TABLE items PERMISSIONS FULL;
-- Define a new table as with advanced scope permissions
DEFINE TABLE items PERMISSIONS FOR select FULL FOR delete NONE FOR create, update WHERE $auth.type = "admin";
```
#### DEFINE FIELD
```sql
-- Define a new field on a database table
DEFINE FIELD age ON person;
-- Remove a field from a database table
REMOVE FIELD name ON person;
-- Define a new field with a type
DEFINE FIELD age ON person TYPE number;
-- Define a new embedded field with type
DEFINE FIELD name.first ON person TYPE string;
-- Define a new field on an array of objects
DEFINE FIELD emails.*.value ON person TYPE email;
-- Define a new field with min and max allowed values
DEFINE FIELD age ON person TYPE number MIN 0 MAX 100;
-- Define a new field which can not be specified as NULL
DEFINE FIELD age ON person TYPE number MIN 0 MAX 100 NOTNULL;
-- Define a new field which will fail if not the correct type
DEFINE FIELD age ON person TYPE number MIN 0 MAX 100 VALIDATE;
-- Define a new field which is not able to be changed once defined
DEFINE FIELD age ON person TYPE number MIN 0 MAX 100 NOTNULL VALIDATE READONLY;
-- Define a new field which defaults to a specified value if not defined
DEFINE FIELD country ON address TYPE string DEFAULT "GBR";
-- Define a new field into which any data put must match a regular expression
DEFINE FIELD iso ON output TYPE string MATCH /^[A-Z]{3}$/;
-- Define a new field into which any data put must match a specific set of values
DEFINE FIELD kind ON address TYPE custom ENUM ["home","work"];
-- Define a new computed field which will autoupdate when any dependent fields change
DEFINE FIELD fullname ON person TYPE string CODE "return [doc.data.firstname, doc.data.lastname].join(' ');";
-- Define a new field which can not be viewed or edited by any user authenticated by scope
DEFINE FIELD password ON person TYPE string PERMISSIONS NONE;
-- Define a new field which has specific access methods for any user authenticated by scope
DEFINE FIELD notes ON person TYPE string PERMISSIONS FOR select WHERE $auth.accountid = accountid FOR create, update, delete WHERE $auth.accountid = accountid AND $auth.type = "admin";
```
#### DEFINE INDEX
```sql
-- Define an index for a table
DEFINE INDEX sortable ON person COLUMNS name;
-- Remove an index from a table
REMOVE INDEX sortable ON person;
-- Define a unique index on a table
DEFINE INDEX sortable ON person COLUMNS uuid UNIQUE;
-- Define a compound index with multiple columns
DEFINE INDEX sortable ON person COLUMNS firstname, lastname;
-- Define an index for all values in an array set
DEFINE INDEX tags ON person COLUMNS tags.*;
-- Define an index for all values in each object in an array set
DEFINE INDEX tags ON person COLUMNS tags.*.value;
```
#### DEFINE VIEW
```sql
-- Define an aggregated view on a database
DEFINE VIEW ages AS SELECT count(*), min(age), max(age) FROM person;
-- Remove an aggregated view from a database
REMOVE VIEW ages;
-- Define an aggregated view with a where clause
DEFINE VIEW ages AS SELECT count(*), min(age), max(age) FROM person WHERE age > 18;
-- Define an aggregated view with a where clause, and a group-by clause
DEFINE VIEW ages AS SELECT count(*), min(age), max(age) FROM person WHERE age > 18 GROUP BY nationality;
-- Define an aggregated view with a where clause, and multiple group-by clauses
DEFINE VIEW ages AS SELECT count(*), min(age), max(age) FROM person WHERE age > 18 GROUP BY nationality, gender;
```
#### LIVE
```sql
-- Define a live query for a table
LIVE SELECT * FROM person;
-- Remove a live query from a table
KILL "183047103847103847";
-- Define a live query for a table, only for records which match a condition
LIVE SELECT name, age, country FROM person WHERE age > 18 AND age < 60;
```
#### CREATE
```sql
-- Create a new record
CREATE person;
-- Create a new record and set some fields
CREATE person SET age=28, name='Tobie';
-- Create a new record and merge the record content
CREATE person MERGE {"firstname":"Tobie", "lastname":"Morgan Hitchcock"};
-- Create a new record and specify the full record content
CREATE person CONTENT {"firstname":"Tobie", "lastname":"Morgan Hitchcock"};
-- Create a new specific record
CREATE @person:id;
-- Create a new specific record and set some fields
CREATE @person:id SET age = 28, name = 'Tobie';
-- Create a new specific record and set some fields, along with an empty set
CREATE @person:id SET age = 28, name = 'Tobie', tags = [];
-- Create a new specific record and set some fields, along with a set with 1 element
CREATE @person:id SET age = 28, name = 'Tobie', tags = ['old'];
-- Create multiple records in one query
CREATE person, person, person;
-- Create multiple specific records in
CREATE @person:one, @person:two;
```
#### UPDATE
```sql
-- Update a table, ensuring all defined fields are up-to-date
UPDATE person;
-- Update a table, setting a field to null on all records
UPDATE person SET age=NULL;
-- Update a table, removing a field completely from all records
UPDATE person SET age=VOID;
-- Update a table, removing a field completely from all records that match a condition
UPDATE person SET age=VOID WHERE age < 18;
-- Update a specific record, ensuring it exists
UPDATE @person:id
-- Update a specific record, and erase all record data
UPDATE @person:id CONTENT {};
-- Update a specific record, and set some fields
UPDATE @person:id SET age = 28, name = 'Tobie';
-- Update a specific record, and set a field as NULL
UPDATE @person:id SET age = 28, name = 'Tobie', tags = NULL;
-- Update a specific record, and set a field to an empty set
UPDATE @person:id SET age = 28, name = 'Tobie', tags = [];
-- Update a specific record, and set a field to a set with 1 element
UPDATE @person:id SET age = 28, name = 'Tobie', tags = ['old'];
-- Update a specific record, and add 'new' to the `tags` set and removes 'old' from the `tags` set
UPDATE @person:id SET age = 28, name = 'Tobie', tags += ['new'], tags -= ['old'];
-- Update multiple records in one query, ensuring both exist
UPDATE @person:one, @person:two;
-- Update a specific record and ensure the `emails` field is a set
UPDATE @person:id SET emails = [];
-- Update a specific record and add an object to the `emails` set
UPDATE @person:id SET emails += {type: "work", value: "tobie@abcum.co.uk"};
-- Update a specific record and set the vaue of the first object in the `emails` set
UPDATE @person:id SET emails[0].value = "tobie@abcum.com";
-- Update a specific record and remove the object from the `emails` set
UPDATE @person:id SET emails -= {type: "work", value: "tobie@abcum.com"};
```
#### DELETE
```sql
-- Delete all records in a table
DELETE person;
-- Delete all records in a table that match a condition
DELETE person WHERE age < 18;
-- Delete a specific record from a table
DELETE @person:id;
-- Delete a specific record, if the condition matches
DELETE @person:id WHERE age < 18;
-- Delete multiple records in one statement
DELETE @person:one, @person:two;
```
#### RELATE
```sql
-- Define an edge connection between two records
RELATE friend FROM @person:one TO @person:two;
-- Define an edge connection between two records, ensuring only one edge of this type exists
RELATE friend FROM @person:one TO @person:two UNIQUE;
-- Define an edge connection between two records, created in subqueries
RELATE friend FROM (CREATE person) TO (CREATE person);
```
#### BEGIN, CANCEL, COMMIT
```sql
-- Begin a new transaction
BEGIN;
-- Cancel a transaction
CANCEL;
-- Commit a transaction
COMMIT;
-- Define a unique index
DEFINE INDEX languages ON country COLUMNS languages.* UNIQUE;
CREATE @country:GBR SET name="Great Britain" languages=["english", "welsh", "scottish"];
CREATE @country:FRA SET name="France" languages=["french"];
-- Define a transaction that will fail, without any changes to the database
BEGIN;
CREATE @country:BRA SET name="Brazil" languages=["portugese"];
CREATE @country:USA SET name="United States of America" languages=["english"];
CREATE @country:DEU SET name="Germany" languages="german";
COMMIT;
```
#### LET, RETURN
```sql
-- Define a new variable as a new person record
LET person1 = (CREATE person);
-- Define a 2nd variable as a new person record
LET person2 = (CREATE person);
-- Define a 3rd variable as a graph connection between the 1st and 2nd variables
LET edge = (RELATE friend FROM $person TO $person2);
-- Return only the first two people, ignoring the graph edge
RETURN $person1, $person2;
```
#### SELECT
```sql
-- Select all records from a table
SELECT * FROM person;
-- Select all records where the condition matches
SELECT * FROM person WHERE age > 18;
-- Select all records and specify a dynamically calculated field
SELECT ((celsius*2)+30) AS fahrenheit FROM temperatues;
-- Select all records where the age is greater than the age of another specific record
SELECT * FROM person WHERE age >= person:tobie;
SELECT * FROM shot::{course=$course, hole=$hole}
-- Select all records where the `tags` set contains "tag"
SELECT * FROM person WHERE tags ∋ "tag";
SELECT * FROM person WHERE tags ~ "tag";
SELECT * FROM person WHERE tags CONTAINS "tag";
SELECT * FROM person WHERE "tag" ∈ tags;
SELECT * FROM person WHERE "tag" IS IN tags;
-- Select all records where the `tags` set does not contain "tag"
SELECT * FROM person WHERE tags ∌ "tag";
SELECT * FROM person WHERE tags !~ "tag";
SELECT * FROM person WHERE tags CONTAINS NOT "tag";
SELECT * FROM person WHERE "tag" ∉ tags;
SELECT * FROM person WHERE "tag" IS NOT IN tags;
-- Select all records where the `tags` set contains "tag1" AND "tag2"
SELECT * FROM person WHERE tags ⊇ ["tag1", "tag2"];
SELECT * FROM person WHERE tags CONTAINSALL ["tag1", "tag2"];
-- Select all records where the `tags` set contains "tag1" OR "tag2"
SELECT * FROM person WHERE tags ⊃ ["tag1", "tag2"];
SELECT * FROM person WHERE tags CONTAINSSOME ["tag1", "tag2"];
-- Select all records where the `tags` does not contain "tag1" OR "tag2"
SELECT * FROM person WHERE tags ⊅ ["tag1", "tag2"];
SELECT * FROM person WHERE tags CONTAINSNONE ["tag1", "tag2"];
-- Select all records where all email address values end with 'gmail.com'
SELECT * FROM person WHERE emails.*.value = /gmail.com$/;
-- Select all records where no email address values end with 'gmail.com'
SELECT * FROM person WHERE emails.*.value != /gmail.com$/;
-- Select all records where any email address value ends with 'gmail.com'
SELECT * FROM person WHERE emails.*.value ?= /gmail.com$/;
-- Select all person records, and all of their likes
SELECT ->likes->? FROM person;
-- Select all person records, and all of their friends
SELECT ->friend->person FROM person;
-- Select all person records, and all of the friends and followers
SELECT <->(friend, follow)->person FROM person;
-- Select all person records, and the ids of people who like each person
SELECT *, <-likes<-person.id FROM person;
-- Select all person records, and the people who like this person, who are older than 18
SELECT *, <-friend<-person[age>=18] AS friends FROM person;
-- Select only person records where a friend likes chocolate
SELECT * FROM person WHERE ->friend->person->likes->food:chocolate;
-- Select the products purchased by friends of a specific person record
SELECT ->friend->person{1..3}->purchased->product FROM @person:tobie;
-- Select all 1st, 2nd, or 3rd level people who this specific person record knows
SELECT ->knows->?{1..3} FROM @person:tobie;
-- Select all 1st, 2nd, and 3rd level people who this specific person record knows, or likes, as separet paths
SELECT ->knows->(? AS f1)->knows->(? AS f2)->(knows, likes AS e3 WHERE hot=true)->(? AS f3) FROM @person:tobie;
-- Select all person records (and their recipients), who have sent more than 5 emails
SELECT *, ->sent->email->to->person FROM person WHERE count(->sent->email->to->person) > 5;
-- Select all people who know jaime
SELECT * FROM person WHERE ->knows->@person:jaime;
-- Select all person records, and all of the adult friends
SELECT ->knows->(person WHERE age >= 18) FROM person;
-- Select other products purchased by people who purchased this laptop
SELECT <-purchased<-person->purchased->product FOLLOW DISTINCT FROM @product:laptop;
-- Select products purchased by people who have purchased the same products that we have purchased
SELECT ->purchased->product<-purchased<-person->purchased->product FOLLOW DISTINCT FROM @person:tobie;
-- Select products purchased by people in the last 3 weeks who have purchased the same products that we have purchased
SELECT ->purchased->product<-purchased<-person->(purchased WHERE created_at > now() - 3w)->product FOLLOW DISTINCT FROM @person:tobie;
-- Select products purchased by people who have purchased the same products that we have purchased
SELECT ->purchased->product<-purchased<-person->purchased->product FOLLOW DISTINCT FROM @person:tobie;
-- Select all people who have sent an email to tobie@abcum.com
SELECT * FROM person WHERE @email:{tobie@abcum.com}->from->email.address IN emails.?.value;
```

View file

@ -1,140 +0,0 @@
# Security
This document describes how authentication and user access works for accessing the database.
#### Authentication levels
** ROOT authentication **
- Signin with root email and password, set at server initialisation
- Can create, select, delete all namespaces
- Can create, select, delete all databases
- Can create, select, delete all tables
- Can create, select, delete all data
- Not restricted by any Table or Field permissions
** NS authentcation **
- Signin with NS, email, and password, which must exist as a NAMESPACE USER
- Can create, select, delete any database under the NS
- Can create, select, delete any tables under the NS
- Can create, select, delete any data under the NS
- Not restricted by any Table or Field permissions
** DB authentication **
- Signup
- Signin with NS, DB, email, and password, which must exist as a DATABASE USER
- Can create, select, delete any tables under the DB
- Can create, select, delete any data under the DB
- Not restricted by any Table or Field permissions
** SC authentcation **
- Signup with NS, DB, SC, email, and password, which must successfully pass an scope SIGNUP clause
- Signin with NS, DB, SC, email, and password, which must successfully pass an scope SIGNIN clause
- Can create, select, delete any data under the DB, as long as permission match
- Restricted by any Table or Field permissions
#### Database signup
** SC signup **
- POST an HTTP FORM or JSON to /signin
```json
{
"NS": "abcum",
"DB": "acreon",
"SC": "account",
"user": "user@example.com",
"pass": "123456"
}
```
- Receive a HTTP 200 code from server
#### Database signin
** ROOT signin **
- Use HTTP Basic Auth specifying username:password with each request
```HTTP
POST /sql HTTP/1.1
Host: localhost:8000
Content-Type: application/json
Authorization: Basic cm9vdDpyb290
```
** NS signin **
- POST an HTTP FORM or JSON to /signin
```json
{
"NS": "abcum",
"user": "user@example.com",
"pass": "123456"
}
```
- Receive a JSON Web Token from the server
- Use the JSON Web Token to authenticate requests
```HTTP
POST /sql HTTP/1.1
Host: localhost:8000
Content-Type: application/json
Authorization: Bearer eyJhbGciOiIkpXVCJ9.eyJEQiI6ImFiY30Nzk3Mzc2NDh9.RMVkex6OpHPZY1BQIQKlQ
```
** DB signin **
- POST an HTTP FORM or JSON to /signin
```json
{
"NS": "abcum",
"DB": "acreon",
"user": "user@example.com",
"pass": "123456"
}
```
- Receive a JSON Web Token from the server
- Use the JSON Web Token to authenticate requests
```HTTP
POST /sql HTTP/1.1
Host: localhost:8000
Content-Type: application/json
Authorization: Bearer eyJhbGciOiIkpXVCJ9.eyJEQiI6ImFiY30Nzk3Mzc2NDh9.RMVkex6OpHPZY1BQIQKlQ
```
** SC signin **
- POST an HTTP FORM or JSON to /signup
```json
{
"NS": "abcum",
"DB": "acreon",
"SC": "account",
"user": "user@example.com",
"pass": "123456"
}
```
- Receive a JSON Web Token from the server
- Use the JSON Web Token to authenticate requests
```HTTP
POST /sql HTTP/1.1
Host: localhost:8000
Content-Type: application/json
Authorization: Bearer eyJhbGciOiIkpXVCJ9.eyJEQiI6ImFiY30Nzk3Mzc2NDh9.RMVkex6OpHPZY1BQIQKlQ
```

View file

@ -1,93 +0,0 @@
# Storage
This document describes how the database data is stored in the key-value storage layer;
**Base keys**
```bash
{$kv} = "surreal" # This is the base key
```
The base key is used to separate the data used in SurrealDB from data used by other databases using the same key:value store.
```bash
{$ns} = "acme" # This is the name of the namespace
```
The namespace key is used to enable separation of data and multi-tenancy of databases on SurrealDB.
```bash
{$db} = "test" # This is the name of the database
```
The database key is used to separate data into multiple different databases under each multi-tenant installation.
#### Chars
Each data type is stored using a different symbol in the key-value pair.
```bash
! # Used to store config data
* # Used to store item data
~ # Used to store item diffs
• # Used to store item trail
« # Used to store item edges
» # Used to store item edges
¤ # Used to store index data
```
#### Keys
The keys for the data in the key-value store use the template below.
```bash
KV /{$kv}
NS /{$kv}/{$ns}
NT /{$kv}/{$ns}/!/t/{$tk}
NU /{$kv}/{$ns}/!/u/{$us}
DB /{$kv}/{$ns}/*/{$db}
LV /{$kv}/{$ns}/*/{$db}/!/l/{$lv}
SC /{$kv}/{$ns}/*/{$db}/!/s/{$sc}
ST /{$kv}/{$ns}/*/{$db}/!/s/{$sc}/!/t/{$tk}
DT /{$kv}/{$ns}/*/{$db}/!/t/{$tk}
DU /{$kv}/{$ns}/*/{$db}/!/u/{$us}
VW /{$kv}/{$ns}/*/{$db}/!/v/{$vw}
TB /{$kv}/{$ns}/*/{$db}/*/{$tb}
EV /{$kv}/{$ns}/*/{$db}/*/{$tb}/!/e/{$ev}
FD /{$kv}/{$ns}/*/{$db}/*/{$tb}/!/f/{$fd}
IX /{$kv}/{$ns}/*/{$db}/*/{$tb}/!/i/{$ix}
Table /{$kv}/{$ns}/*/{$db}/*/{$tb}/*
Thing /{$kv}/{$ns}/*/{$db}/*/{$tb}/*/{$id}
Field /{$kv}/{$ns}/*/{$db}/*/{$tb}/*/{$id}/*/{$fd}
Edge /{$kv}/{$ns}/*/{$db}/*/{$tb}/*/{$id}/»/{$tp}/{$ft}/{$fk}
Patch /{$kv}/{$ns}/*/{$db}/*/{$tb}/~/{$id}/{$at}
Index /{$kv}/{$ns}/*/{$db}/*/{$tb}/¤/{$ix}/{$fd}
Point /{$kv}/{$ns}/*/{$db}/*/{$tb}/¤/{$ix}/{$fd}/{$id}
```
The specific keys listed above are displayed with example data below.
```bash
KV /surreal
NS /surreal/abcum
NT /surreal/abcum/!/t/default
NU /surreal/abcum/!/u/tobie@abcum.com
DB /surreal/abcum/*/acreon
LV /surreal/abcum/*/acreon/!/l/name
SC /surreal/abcum/*/acreon/!/s/admin
ST /surreal/abcum/*/acreon/!/s/admin/!/t/default
DT /surreal/abcum/*/acreon/!/t/default
DU /surreal/abcum/*/acreon/!/u/tobie@abcum.com
VW /surreal/abcum/*/acreon/!/v/ages
TB /surreal/abcum/*/acreon/*/person
EV /surreal/abcum/*/acreon/*/person/!/e/activity
FD /surreal/abcum/*/acreon/*/person/!/f/name.first
IX /surreal/abcum/*/acreon/*/person/!/i/names
Table /surreal/abcum/*/acreon/*/person/*
Thing /surreal/abcum/*/acreon/*/person/*/tobie
Field /surreal/abcum/*/acreon/*/person/*/tobie/*/name.first
Edge /surreal/abcum/*/acreon/*/person/*/tobie/»/like/entity/apple
Patch /surreal/abcum/*/acreon/*/person/~/tobie/2016-01-29T22:42:56.478173947Z
Index /surreal/abcum/*/acreon/*/person/¤/names/[col1,col2,col3]
Point /surreal/abcum/*/acreon/*/person/¤/names/[col1,col2,col3]/tobie
```

68
go.mod
View file

@ -1,68 +0,0 @@
module github.com/surrealdb/surrealdb
go 1.17
require (
cloud.google.com/go v0.99.0
cloud.google.com/go/errorreporting v0.1.0
cloud.google.com/go/logging v1.4.2
github.com/dgraph-io/ristretto v0.1.0
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/elithrar/simple-scrypt v1.3.0
github.com/gorilla/websocket v1.4.2
github.com/hjson/hjson-go v3.1.0+incompatible
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d
github.com/pkg/profile v1.6.0
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be
github.com/rs/xid v1.3.0
github.com/satori/go.uuid v1.2.0
github.com/sergi/go-diff v1.2.0
github.com/sirupsen/logrus v1.8.1
github.com/smartystreets/goconvey v1.7.2
github.com/spf13/cobra v1.2.1
github.com/surrealdb/bump v0.0.0-20211208111645-b89bf4013dc9
github.com/surrealdb/cork v0.0.0-20211208110447-54ccd49930c9
github.com/surrealdb/fibre v0.0.0-20211208172721-cc4f2770111e
github.com/surrealdb/rixxdb v0.0.0-20211019142450-5b9e2a203f4c
github.com/ugorji/go/codec v1.2.6
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b
golang.org/x/net v0.0.0-20211209124913-491a49abca63
golang.org/x/text v0.3.7
)
require (
github.com/census-instrumentation/opencensus-proto v0.2.1 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/cespare/xxhash/v2 v2.1.1 // indirect
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 // indirect
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0 // indirect
github.com/envoyproxy/protoc-gen-validate v0.1.0 // indirect
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.6 // indirect
github.com/googleapis/gax-go/v2 v2.1.1 // indirect
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jtolds/gls v4.20.0+incompatible // indirect
github.com/mattn/go-colorable v0.0.9 // indirect
github.com/mattn/go-isatty v0.0.3 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/ory/graceful v0.1.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/segmentio/ksuid v1.0.4 // indirect
github.com/smartystreets/assertions v1.2.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
go.opencensus.io v0.23.0 // indirect
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 // indirect
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect
google.golang.org/api v0.61.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0 // indirect
google.golang.org/grpc v1.40.0 // indirect
google.golang.org/protobuf v1.27.1 // indirect
)

756
go.sum
View file

@ -1,756 +0,0 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
cloud.google.com/go v0.92.2/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
cloud.google.com/go v0.99.0 h1:y/cM2iqGgGi5D5DQZl6D9STN/3dR/Vx5Mp8s752oJTY=
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/errorreporting v0.1.0 h1:z40EhrjRspplwbpO+9DSnC4kgDokBi94T/gYwtdKL5Q=
cloud.google.com/go/errorreporting v0.1.0/go.mod h1:cZSiBMvrnl0X13pD9DwKf9sQ8Eqy3EzHqkyKBZxiIrM=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/logging v1.4.2 h1:Mu2Q75VBDQlW1HlBMjTX4X84UFR73G1TiLlRYc/b7tA=
cloud.google.com/go/logging v1.4.2/go.mod h1:jco9QZSx8HiVVqLJReq7z7bVdj0P1Jb9PDFs63T+axo=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed h1:OZmjad4L3H8ncOIR8rnb5MREYqG8ixi5+WbeUsquF0c=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI=
github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/elithrar/simple-scrypt v1.3.0 h1:KIlOlxdoQf9JWKl5lMAJ28SY2URB0XTRDn2TckyzAZg=
github.com/elithrar/simple-scrypt v1.3.0/go.mod h1:U2XQRI95XHY0St410VE3UjT7vuKb1qPwrl/EJwEqnZo=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0 h1:dulLQAYQFYtG5MTplgNGHWuV2D+OBD+Z8lmDBmbLg+s=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
github.com/googleapis/gax-go/v2 v2.1.1 h1:dp3bWCh+PPO1zjRRiCSczJav13sBvG4UhNyVTa1KqdU=
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0=
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hjson/hjson-go v3.1.0+incompatible h1:DY/9yE8ey8Zv22bY+mHV1uk2yRy0h8tKhZ77hEdi0Aw=
github.com/hjson/hjson-go v3.1.0+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/ory/graceful v0.1.1 h1:zx+8tDObLPrG+7Tc8jKYlXsqWnLtOQA1IZ/FAAKHMXU=
github.com/ory/graceful v0.1.1/go.mod h1:zqu70l95WrKHF4AZ6tXHvAqAvpY6M7g6ttaAVcMm7KU=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.6.0 h1:hUDfIISABYI59DyeB3OTay/HxSRwTQ8rB/H83k6r5dM=
github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be h1:ta7tUOvsPHVHGom5hKW5VXNc2xZIkfCKP8iaqOyYtUQ=
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/xid v1.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4=
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c=
github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=
github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw=
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/surrealdb/bump v0.0.0-20211208102305-88d509230106/go.mod h1:7j+Wcc4B4QM++XXvz8wCpFdSRWHvWLWMr7iC6495KoM=
github.com/surrealdb/bump v0.0.0-20211208111645-b89bf4013dc9 h1:6yfxCWaPVNeA32FOgZRIor6nT8FhGDbeDVaFNm/3ago=
github.com/surrealdb/bump v0.0.0-20211208111645-b89bf4013dc9/go.mod h1:7j+Wcc4B4QM++XXvz8wCpFdSRWHvWLWMr7iC6495KoM=
github.com/surrealdb/cork v0.0.0-20211208110447-54ccd49930c9 h1:DeURFsQG41ZtWL76+wvSeohgLY6vyvoeRLauhNhy/A4=
github.com/surrealdb/cork v0.0.0-20211208110447-54ccd49930c9/go.mod h1:q+YzNz7MWmUNiYkZNDXuOSrp2pdIXGLdmpVgf2pcoG4=
github.com/surrealdb/fibre v0.0.0-20211208172721-cc4f2770111e h1:HGhm81GyA+dSwhF6RY+mATAqB/vcMIC39UUw5DBcxCo=
github.com/surrealdb/fibre v0.0.0-20211208172721-cc4f2770111e/go.mod h1:p7ih2w2iYiJauQciUUW8YpdMXdba08FtNbW9Zi7KltA=
github.com/surrealdb/rixxdb v0.0.0-20211019142450-5b9e2a203f4c h1:btj+hGxS7X4qHqceTIkCCWhdIgkSuNe96ZLB2lPLEs0=
github.com/surrealdb/rixxdb v0.0.0-20211019142450-5b9e2a203f4c/go.mod h1:S4h2aMp3Lk62FXmhCOdS0OeWuiaNeP8Vk4OOXnGa0Cw=
github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E=
github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0=
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b h1:QAqMVf3pSa6eeTsuklijukjXBlj7Es2QQplab+/RbQ4=
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY=
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210427180440-81ed05c6b58c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210503080704-8803ae5d1324/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 h1:TyHqChC80pFkXWraUUf6RuB5IqFdQieMLwwCJokV2pc=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
google.golang.org/api v0.46.0/go.mod h1:ceL4oozhkAiTID8XMmJBsIxID/9wMXJVVFXPg4ylg3I=
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
google.golang.org/api v0.61.0 h1:TXXKS1slM3b2bZNJwD5DV/Tp6/M2cLzLOLh9PjDhrw8=
google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210429181445-86c259c2b4ab/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
google.golang.org/genproto v0.0.0-20210517163617-5e0236093d7a/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0 h1:c7yRRmuQiVMo+YppNj5MUREXUyc2lPo3DrtYMwaWQ28=
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

View file

@ -1,28 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package kvs
import (
"context"
"io"
)
// DB represents a database implementation
type DB interface {
Begin(context.Context, bool) (TX, error)
Import(io.Reader) error
Export(io.Writer) error
Close() error
}

View file

@ -1,80 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package kvs
import (
"context"
"io"
"strings"
"github.com/surrealdb/surrealdb/cnf"
)
// DB represents a backing datastore.
type DS struct {
db DB
}
// New sets up the underlying key-value store
func New(opts *cnf.Options) (ds *DS, err error) {
var db DB
switch {
case opts.DB.Path == "memory":
db, err = stores["rixxdb"](opts)
case strings.HasPrefix(opts.DB.Path, "file://"):
db, err = stores["rixxdb"](opts)
case strings.HasPrefix(opts.DB.Path, "dendrodb://"):
db, err = stores["dendro"](opts)
}
if err != nil {
return
}
ds = &DS{db: db}
return
}
// Begin begins a new read / write transaction
// with the underlying database, and returns
// the transaction, or any error which occured.
func (ds *DS) Begin(ctx context.Context, writable bool) (txn TX, err error) {
return ds.db.Begin(ctx, writable)
}
// Import loads database operations from a reader.
// This can be used to playback a database snapshot
// into an already running database.
func (ds *DS) Import(r io.Reader) (err error) {
return ds.db.Import(r)
}
// Export saves all database operations to a writer.
// This can be used to save a database snapshot
// to a secondary file or stream.
func (ds *DS) Export(w io.Writer) (err error) {
return ds.db.Export(w)
}
// Close closes the underlying rixxdb / dendrodb
// database connection, enabling the underlying
// database to clean up remainging transactions.
func (ds *DS) Close() (err error) {
return ds.db.Close()
}

View file

@ -1,28 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package kvs
import "fmt"
// DBError is an error which occurs when there is a
// problem with writing keys/values to the database.
type DBError struct {
Err error
}
// Error returns the string representation of the error.
func (e *DBError) Error() string {
return fmt.Sprintf("Unable to write to the database: %s", e.Err)
}

View file

@ -1,23 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package kvs
// KV represents a database item
type KV interface {
Exi() bool
Key() []byte
Val() []byte
Ver() uint64
}

View file

@ -1,77 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package kvs
import (
"context"
"io"
"github.com/surrealdb/surrealdb/cnf"
"github.com/surrealdb/surrealdb/log"
)
var ds *DS
// Stores the different backend implementations
var stores = make(map[string]func(*cnf.Options) (DB, error))
// Setup sets up the connection with the data layer
func Setup(opts *cnf.Options) (err error) {
log.WithPrefix("kvs").Infof("Starting kvs storage at %s", opts.DB.Path)
ds, err = New(opts)
log.WithPrefix("kvs").Infof("Started kvs storage at %s", opts.DB.Path)
return
}
// Exit shuts down the connection with the data layer
func Exit(opts *cnf.Options) (err error) {
log.WithPrefix("kvs").Infof("Shutting down kvs storage at %s", opts.DB.Path)
return ds.Close()
}
// Begin begins a new read / write transaction
// with the underlying database, and returns
// the transaction, or any error which occured.
func Begin(ctx context.Context, writable bool) (txn TX, err error) {
return ds.db.Begin(ctx, writable)
}
// Import loads database operations from a reader.
// This can be used to playback a database snapshot
// into an already running database.
func Import(r io.Reader) (err error) {
return ds.db.Import(r)
}
// Export saves all database operations to a writer.
// This can be used to save a database snapshot
// to a secondary file or stream.
func Export(w io.Writer) (err error) {
return ds.db.Export(w)
}
// Close closes the underlying rixxdb / dendrodb
// database connection, enabling the underlying
// database to clean up remainging transactions.
func Close() (err error) {
return ds.db.Close()
}
// Register registers a new database type with
// the kvs package, enabling it's use as a
// backing datastore within SurrealDB.
func Register(name string, constructor func(*cnf.Options) (DB, error)) {
stores[name] = constructor
}

View file

@ -1,51 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rixxdb
import (
"io"
"context"
"github.com/surrealdb/rixxdb"
"github.com/surrealdb/surrealdb/kvs"
"github.com/surrealdb/surrealdb/log"
)
type DB struct {
pntr *rixxdb.DB
}
func (db *DB) Begin(ctx context.Context, writable bool) (txn kvs.TX, err error) {
var pntr *rixxdb.TX
if pntr, err = db.pntr.Begin(writable); err != nil {
log.WithPrefix("kvs").Errorln(err)
err = &kvs.DBError{Err: err}
return
}
return &TX{pntr: pntr}, err
}
func (db *DB) Import(r io.Reader) (err error) {
return db.pntr.Load(r)
}
func (db *DB) Export(w io.Writer) (err error) {
return db.pntr.Save(w)
}
func (db *DB) Close() (err error) {
return db.pntr.Close()
}

View file

@ -1,50 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rixxdb
import (
"github.com/surrealdb/rixxdb"
"github.com/surrealdb/surrealdb/cnf"
"github.com/surrealdb/surrealdb/kvs"
"github.com/surrealdb/surrealdb/log"
)
func init() {
kvs.Register("rixxdb", func(opts *cnf.Options) (db kvs.DB, err error) {
var pntr *rixxdb.DB
pntr, err = rixxdb.Open(opts.DB.Path, &rixxdb.Config{
// Explicitly sync writes
SyncWrites: true,
// Set the encryption key
EncryptionKey: opts.DB.Key,
// Set the sync offset duration
FlushPolicy: opts.DB.Proc.Flush,
// Set the shrink offset duration
ShrinkPolicy: opts.DB.Proc.Shrink,
})
if err != nil {
log.WithPrefix("kvs").Errorln(err)
return
}
return &DB{pntr: pntr}, err
})
}

View file

@ -1,139 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rixxdb
import (
"context"
"github.com/surrealdb/rixxdb"
"github.com/surrealdb/surrealdb/kvs"
)
type TX struct {
pntr *rixxdb.TX
}
func one(res *rixxdb.KV, err error) (kvs.KV, error) {
if err != nil {
return nil, &kvs.DBError{Err: err}
}
return res, err
}
func many(res []*rixxdb.KV, err error) ([]kvs.KV, error) {
if err != nil {
return nil, &kvs.DBError{Err: err}
}
var out = make([]kvs.KV, len(res))
for i, v := range res {
out[i] = v
}
return out, err
}
func (tx *TX) Closed() bool {
return tx.pntr.Closed()
}
func (tx *TX) Cancel() error {
return tx.pntr.Cancel()
}
func (tx *TX) Commit() error {
return tx.pntr.Commit()
}
func (tx *TX) All(ctx context.Context, key []byte) ([]kvs.KV, error) {
res, err := tx.pntr.All(key)
return many(res, err)
}
func (tx *TX) AllP(ctx context.Context, key []byte, max uint64) ([]kvs.KV, error) {
res, err := tx.pntr.AllP(key, max)
return many(res, err)
}
func (tx *TX) AllR(ctx context.Context, beg []byte, end []byte, max uint64) ([]kvs.KV, error) {
res, err := tx.pntr.AllR(beg, end, max)
return many(res, err)
}
func (tx *TX) Clr(ctx context.Context, key []byte) (kvs.KV, error) {
res, err := tx.pntr.Clr(key)
return one(res, err)
}
func (tx *TX) ClrP(ctx context.Context, key []byte, max uint64) ([]kvs.KV, error) {
res, err := tx.pntr.ClrP(key, max)
return many(res, err)
}
func (tx *TX) ClrR(ctx context.Context, beg []byte, end []byte, max uint64) ([]kvs.KV, error) {
res, err := tx.pntr.ClrR(beg, end, max)
return many(res, err)
}
func (tx *TX) Get(ctx context.Context, ver int64, key []byte) (kvs.KV, error) {
res, err := tx.pntr.Get(uint64(ver), key)
return one(res, err)
}
func (tx *TX) GetP(ctx context.Context, ver int64, key []byte, max uint64) ([]kvs.KV, error) {
res, err := tx.pntr.GetP(uint64(ver), key, max)
return many(res, err)
}
func (tx *TX) GetR(ctx context.Context, ver int64, beg []byte, end []byte, max uint64) ([]kvs.KV, error) {
res, err := tx.pntr.GetR(uint64(ver), beg, end, max)
return many(res, err)
}
func (tx *TX) Del(ctx context.Context, ver int64, key []byte) (kvs.KV, error) {
res, err := tx.pntr.Del(uint64(ver), key)
return one(res, err)
}
func (tx *TX) DelC(ctx context.Context, ver int64, key []byte, exp []byte) (kvs.KV, error) {
res, err := tx.pntr.DelC(uint64(ver), key, exp)
return one(res, err)
}
func (tx *TX) DelP(ctx context.Context, ver int64, key []byte, max uint64) ([]kvs.KV, error) {
res, err := tx.pntr.DelP(uint64(ver), key, max)
return many(res, err)
}
func (tx *TX) DelR(ctx context.Context, ver int64, beg []byte, end []byte, max uint64) ([]kvs.KV, error) {
res, err := tx.pntr.DelR(uint64(ver), beg, end, max)
return many(res, err)
}
func (tx *TX) Put(ctx context.Context, ver int64, key []byte, val []byte) (kvs.KV, error) {
res, err := tx.pntr.Put(uint64(ver), key, val)
return one(res, err)
}
func (tx *TX) PutC(ctx context.Context, ver int64, key []byte, val []byte, exp []byte) (kvs.KV, error) {
res, err := tx.pntr.PutC(uint64(ver), key, val, exp)
return one(res, err)
}

View file

@ -1,44 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package kvs
import "context"
// TX represents a database transaction
type TX interface {
Closed() bool
Cancel() error
Commit() error
All(context.Context, []byte) ([]KV, error)
AllP(context.Context, []byte, uint64) ([]KV, error)
AllR(context.Context, []byte, []byte, uint64) ([]KV, error)
Clr(context.Context, []byte) (KV, error)
ClrP(context.Context, []byte, uint64) ([]KV, error)
ClrR(context.Context, []byte, []byte, uint64) ([]KV, error)
Put(context.Context, int64, []byte, []byte) (KV, error)
PutC(context.Context, int64, []byte, []byte, []byte) (KV, error)
Get(context.Context, int64, []byte) (KV, error)
GetP(context.Context, int64, []byte, uint64) ([]KV, error)
GetR(context.Context, int64, []byte, []byte, uint64) ([]KV, error)
Del(context.Context, int64, []byte) (KV, error)
DelC(context.Context, int64, []byte, []byte) (KV, error)
DelP(context.Context, int64, []byte, uint64) ([]KV, error)
DelR(context.Context, int64, []byte, []byte, uint64) ([]KV, error)
}

View file

@ -1,104 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package log
import (
"io"
"io/ioutil"
"os"
"time"
"github.com/sirupsen/logrus"
)
type DefaultHook struct {
w io.Writer
l []logrus.Level
f logrus.Formatter
}
func (h *DefaultHook) Levels() []logrus.Level {
return h.l
}
func (h *DefaultHook) Fire(entry *logrus.Entry) error {
bit, err := h.f.Format(entry)
if err != nil {
return err
}
_, err = h.w.Write(bit)
return err
}
// SetLevel sets the logging level of the logger instance.
func (h *DefaultHook) SetLevel(v string) {
switch v {
case "trace":
h.l = TraceLevels
case "debug":
h.l = DebugLevels
case "info":
h.l = InfoLevels
case "warn":
h.l = WarnLevels
case "error":
h.l = ErrorLevels
case "fatal":
h.l = FatalLevels
case "panic":
h.l = PanicLevels
}
}
// SetOutput sets the logging output of the logger instance.
func (h *DefaultHook) SetOutput(v string) {
switch v {
case "none":
h.w = ioutil.Discard
case "stdout":
h.w = os.Stdout
case "stderr":
h.w = os.Stderr
}
}
// SetFormat sets the logging format of the logger instance.
func (h *DefaultHook) SetFormat(v string) {
switch v {
case "json":
h.f = &JSONFormatter{
IgnoreFields: []string{
"ctx",
"span",
"trace",
"fibre",
"prefix",
},
TimestampFormat: time.RFC3339,
}
case "text":
h.f = &TextFormatter{
IgnoreFields: []string{
"ctx",
"vars",
"span",
"trace",
"fibre",
"prefix",
},
TimestampFormat: time.RFC3339,
}
}
}

View file

@ -1,78 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package log
import (
"time"
"encoding/json"
"github.com/sirupsen/logrus"
)
type JSONFormatter struct {
IgnoreFields []string
TimestampFormat string
}
func (f *JSONFormatter) ignore(field string) bool {
for _, ignore := range f.IgnoreFields {
if field == ignore {
return true
}
}
return false
}
func (f *JSONFormatter) include(field string) bool {
for _, ignore := range f.IgnoreFields {
if field == ignore {
return false
}
}
return true
}
func (f *JSONFormatter) Format(entry *logrus.Entry) (data []byte, err error) {
if f.TimestampFormat == "" {
f.TimestampFormat = time.RFC3339Nano
}
obj := make(map[string]interface{})
obj["msg"] = entry.Message
obj["time"] = entry.Time.Format(f.TimestampFormat)
obj["level"] = entry.Level.String()
for k, v := range entry.Data {
if f.include(k) {
switch x := v.(type) {
case error:
obj[k] = x.Error()
default:
obj[k] = x
}
}
}
data, err = json.Marshal(obj)
if err != nil {
return nil, err
}
return append(data, '\n'), nil
}

View file

@ -1,327 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package log
import (
"fmt"
"io/ioutil"
"os"
"time"
"github.com/sirupsen/logrus"
)
const (
PanicLevel = logrus.PanicLevel
FatalLevel = logrus.FatalLevel
ErrorLevel = logrus.ErrorLevel
WarnLevel = logrus.WarnLevel
InfoLevel = logrus.InfoLevel
DebugLevel = logrus.DebugLevel
TraceLevel = logrus.TraceLevel
)
var (
PanicLevels = []logrus.Level{
PanicLevel,
}
FatalLevels = []logrus.Level{
PanicLevel,
FatalLevel,
}
ErrorLevels = []logrus.Level{
PanicLevel,
FatalLevel,
ErrorLevel,
}
WarnLevels = []logrus.Level{
PanicLevel,
FatalLevel,
ErrorLevel,
WarnLevel,
}
InfoLevels = []logrus.Level{
PanicLevel,
FatalLevel,
ErrorLevel,
WarnLevel,
InfoLevel,
}
DebugLevels = []logrus.Level{
PanicLevel,
FatalLevel,
ErrorLevel,
WarnLevel,
InfoLevel,
DebugLevel,
}
TraceLevels = []logrus.Level{
PanicLevel,
FatalLevel,
ErrorLevel,
WarnLevel,
InfoLevel,
DebugLevel,
TraceLevel,
}
)
var log *Logger
// Logger ...
type Logger struct {
*logrus.Logger
}
func init() {
log = &Logger{
logrus.New(),
}
}
// Instance returns the underlying logger instance
func Instance() *logrus.Logger {
return log.Logger
}
// Hook adds a logging hook to the logger instance
func Hook(hook logrus.Hook) {
log.AddHook(hook)
}
// IsPanic returns whether panic level logs are enabled
func IsPanic() bool {
return log.IsLevelEnabled(PanicLevel)
}
// IsFatal returns whether fatal level logs are enabled
func IsFatal() bool {
return log.IsLevelEnabled(FatalLevel)
}
// IsError returns whether error level logs are enabled
func IsError() bool {
return log.IsLevelEnabled(ErrorLevel)
}
// IsWarn returns whether warning level logs are enabled
func IsWarn() bool {
return log.IsLevelEnabled(WarnLevel)
}
// IsInfo returns whether info level logs are enabled
func IsInfo() bool {
return log.IsLevelEnabled(InfoLevel)
}
// IsDebug returns whether debug level logs are enabled
func IsDebug() bool {
return log.IsLevelEnabled(DebugLevel)
}
// IsTrace returns whether trace level logs are enabled
func IsTrace() bool {
return log.IsLevelEnabled(TraceLevel)
}
// SetLevel sets the logging level of the logger instance.
func SetLevel(v string) {
switch v {
case "trace":
log.Logger.SetLevel(TraceLevel)
case "debug":
log.Logger.SetLevel(DebugLevel)
case "info":
log.Logger.SetLevel(InfoLevel)
case "warn":
log.Logger.SetLevel(WarnLevel)
case "error":
log.Logger.SetLevel(ErrorLevel)
case "fatal":
log.Logger.SetLevel(FatalLevel)
case "panic":
log.Logger.SetLevel(PanicLevel)
}
}
// SetOutput sets the logging output of the logger instance.
func SetOutput(v string) {
switch v {
case "none":
log.Logger.SetOutput(ioutil.Discard)
case "stdout":
log.Logger.SetOutput(os.Stdout)
case "stderr":
log.Logger.SetOutput(os.Stderr)
case "stackdriver":
log.Logger.SetOutput(ioutil.Discard)
log.Logger.AddHook(NewStackDriver())
}
}
// SetFormat sets the logging format of the logger instance.
func SetFormat(v string) {
switch v {
case "json":
log.Logger.SetFormatter(&JSONFormatter{
IgnoreFields: []string{
"ctx",
"span",
"trace",
"fibre",
"prefix",
},
TimestampFormat: time.RFC3339,
})
case "text":
log.Logger.SetFormatter(&TextFormatter{
IgnoreFields: []string{
"ctx",
"vars",
"span",
"trace",
"fibre",
"prefix",
},
TimestampFormat: time.RFC3339,
})
}
}
func Display(v ...interface{}) {
if isTerminal {
fmt.Print(v...)
}
}
// Debug logs a message at level Debug on the standard logger.
func Debug(v ...interface{}) {
log.Debug(v...)
}
// Debugf logs a message at level Debug on the standard logger.
func Debugf(format string, v ...interface{}) {
log.Debugf(format, v...)
}
// Debugln logs a message at level Debug on the standard logger.
func Debugln(v ...interface{}) {
log.Debugln(v...)
}
// Error loggs a message at level Error on the standard logger.
func Error(v ...interface{}) {
log.Error(v...)
}
// Errorf loggs a message at level Error on the standard logger.
func Errorf(format string, v ...interface{}) {
log.Errorf(format, v...)
}
// Errorln loggs a message at level Error on the standard logger.
func Errorln(v ...interface{}) {
log.Errorln(v...)
}
// Fatal loggs a message at level Fatal on the standard logger.
func Fatal(v ...interface{}) {
log.Fatal(v...)
}
// Fatalf loggs a message at level Fatal on the standard logger.
func Fatalf(format string, v ...interface{}) {
log.Fatalf(format, v...)
}
// Fatalln loggs a message at level Fatal on the standard logger.
func Fatalln(v ...interface{}) {
log.Fatalln(v...)
}
// Info loggs a message at level Info on the standard logger.
func Info(v ...interface{}) {
log.Info(v...)
}
// Infof loggs a message at level Info on the standard logger.
func Infof(format string, v ...interface{}) {
log.Infof(format, v...)
}
// Infoln loggs a message at level Info on the standard logger.
func Infoln(v ...interface{}) {
log.Infoln(v...)
}
// Panic loggs a message at level Panic on the standard logger.
func Panic(v ...interface{}) {
log.Panic(v...)
}
// Panicf loggs a message at level Panic on the standard logger.
func Panicf(format string, v ...interface{}) {
log.Panicf(format, v...)
}
// Panicln loggs a message at level Panic on the standard logger.
func Panicln(v ...interface{}) {
log.Panicln(v...)
}
// Print loggs a message at level Print on the standard logger.
func Print(v ...interface{}) {
log.Print(v...)
}
// Printf loggs a message at level Print on the standard logger.
func Printf(format string, v ...interface{}) {
log.Printf(format, v...)
}
// Println loggs a message at level Print on the standard logger.
func Println(v ...interface{}) {
log.Println(v...)
}
// Warn loggs a message at level Warn on the standard logger.
func Warn(v ...interface{}) {
log.Warn(v...)
}
// Warnf loggs a message at level Warn on the standard logger.
func Warnf(format string, v ...interface{}) {
log.Warnf(format, v...)
}
// Warnln loggs a message at level Warn on the standard logger.
func Warnln(v ...interface{}) {
log.Warnln(v...)
}
// WithPrefix prepares a log entry with a prefix.
func WithPrefix(value interface{}) *logrus.Entry {
return log.WithField("prefix", value)
}
// WithField prepares a log entry with a single data field.
func WithField(key string, value interface{}) *logrus.Entry {
return log.WithField(key, value)
}
// WithFields prepares a log entry with multiple data fields.
func WithFields(fields map[string]interface{}) *logrus.Entry {
return log.WithFields(fields)
}

View file

@ -1,197 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package log
import (
"context"
"fmt"
"net/http"
"os"
"time"
"github.com/surrealdb/fibre"
"github.com/sirupsen/logrus"
"github.com/surrealdb/surrealdb/util/build"
"cloud.google.com/go/compute/metadata"
"cloud.google.com/go/errorreporting"
"cloud.google.com/go/logging"
)
var proj = os.Getenv("PROJECT")
type StackdriverLogger struct {
name string
client *logging.Client
logger *logging.Logger
errors *errorreporting.Client
}
func NewStackDriver() *StackdriverLogger {
var err error
ctx := context.Background()
hook := new(StackdriverLogger)
conf := errorreporting.Config{
ServiceName: "surreal",
ServiceVersion: build.GetInfo().Ver,
}
// If no project id has been set
// then attempt to pull this from
// machine metadata if on GCE.
if len(proj) == 0 {
if proj, err = metadata.ProjectID(); err != nil {
log.Fatalf("Failed to connect to Stackdriver: %v", err)
}
}
// Connect to Stackdriver logging
// using the project name retrieved
// from the machine metadata.
hook.client, err = logging.NewClient(ctx, proj)
if err != nil {
log.Fatalf("Failed to connect to Stackdriver: %v", err)
}
// Connect to Stackdriver errors
// using the project name retrieved
// from the machine metadata.
hook.errors, err = errorreporting.NewClient(ctx, proj, conf)
if err != nil {
log.Fatalf("Failed to connect to Stackdriver: %v", err)
}
// Attempt to ping the Stackdriver
// endpoint to ensure the settings
// and authentication are correct.
err = hook.client.Ping(ctx)
if err != nil {
log.Fatalf("Failed to connect to Stackdriver: %v", err)
}
hook.logger = hook.client.Logger("surreal")
return hook
}
func (h *StackdriverLogger) Levels() []logrus.Level {
switch log.GetLevel() {
case TraceLevel:
return TraceLevels
case DebugLevel:
return DebugLevels
case InfoLevel:
return InfoLevels
case WarnLevel:
return WarnLevels
case ErrorLevel:
return ErrorLevels
case FatalLevel:
return FatalLevels
case PanicLevel:
return PanicLevels
default:
return DebugLevels
}
}
func (h *StackdriverLogger) Fire(entry *logrus.Entry) error {
// If we receive an error, fatal, or
// panic - then log the error to GCE
// with a full stack trace.
if entry.Level <= logrus.ErrorLevel {
e := errorreporting.Entry{
Error: fmt.Errorf("%s", entry.Message),
}
for _, v := range entry.Data {
switch i := v.(type) {
case *http.Request:
e.Req = i
case *fibre.Context:
e.Req = i.Request().Request
}
}
h.errors.Report(e)
}
// Otherwise just log the entry to
// Stackdriver, and attach any http
// request data to it if available.
e := logging.Entry{
Timestamp: entry.Time,
Labels: make(map[string]string, len(entry.Data)),
Payload: entry.Message,
Severity: logging.ParseSeverity(entry.Level.String()),
}
if v, ok := entry.Data["trace"].(string); ok {
e.Trace = fmt.Sprintf("projects/%s/traces/%s", proj, v)
}
if v, ok := entry.Data["span"].(string); ok {
e.SpanID = v
}
if p, ok := entry.Data["prefix"]; ok && p == "sql" {
e.Payload = map[string]interface{}{
"sql": entry.Message,
"vars": entry.Data["vars"],
}
}
for k, v := range entry.Data {
switch i := v.(type) {
default:
e.Labels[k] = fmt.Sprintf("%v", i)
case *http.Request:
e.HTTPRequest = &logging.HTTPRequest{
Request: i,
RemoteIP: i.RemoteAddr,
}
case *fibre.Context:
e.HTTPRequest = &logging.HTTPRequest{
RemoteIP: i.IP().String(),
Request: i.Request().Request,
Status: i.Response().Status(),
RequestSize: i.Request().Size(),
ResponseSize: i.Response().Size(),
Latency: time.Since(i.Request().Start()),
}
}
}
h.logger.Log(e)
return nil
}

View file

@ -1,169 +0,0 @@
// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package log
import (
"bytes"
"fmt"
"sort"
"strings"
"time"
"github.com/mgutz/ansi"
"github.com/sirupsen/logrus"
)
const clear = ansi.Reset
type TextFormatter struct {
IgnoreFields []string
TimestampFormat string
}
func (f *TextFormatter) ignore(field string) bool {
for _, ignore := range f.IgnoreFields {
if field == ignore {
return true
}
}
return false
}
func (f *TextFormatter) include(field string) bool {
for _, ignore := range f.IgnoreFields {
if field == ignore {
return false
}
}
return true
}
func (f *TextFormatter) Format(entry *logrus.Entry) ([]byte, error) {
if f.TimestampFormat == "" {
f.TimestampFormat = time.RFC3339Nano
}
var keys []string = make([]string, 0)
for k := range entry.Data {
if f.include(k) {
keys = append(keys, k)
}
}
sort.Strings(keys)
b := &bytes.Buffer{}
switch isColoured {
case false:
f.printBasic(b, entry, keys)
case true:
f.printColored(b, entry, keys)
}
b.WriteByte('\n')
return b.Bytes(), nil
}
func (f *TextFormatter) printField(b *bytes.Buffer, key string, value interface{}) {
b.WriteString(key)
b.WriteByte('=')
switch value := value.(type) {
case string:
if needsQuoting(value) {
b.WriteString(value)
} else {
fmt.Fprintf(b, "%q", value)
}
case error:
errmsg := value.Error()
if needsQuoting(errmsg) {
b.WriteString(errmsg)
} else {
fmt.Fprintf(b, "%q", value)
}
default:
fmt.Fprint(b, value)
}
b.WriteByte(' ')
}
func (f *TextFormatter) printBasic(b *bytes.Buffer, entry *logrus.Entry, keys []string) {
f.printField(b, "time", entry.Time.Format(f.TimestampFormat))
f.printField(b, "level", entry.Level.String())
f.printField(b, "msg", entry.Message)
for _, key := range keys {
f.printField(b, key, entry.Data[key])
}
}
func (f *TextFormatter) printColored(b *bytes.Buffer, entry *logrus.Entry, keys []string) {
var color string
var prefix string
switch entry.Level {
case logrus.TraceLevel:
color = ansi.White
case logrus.DebugLevel:
color = ansi.Magenta
case logrus.InfoLevel:
color = ansi.Blue
case logrus.WarnLevel:
color = ansi.Yellow
case logrus.ErrorLevel:
color = ansi.Red
case logrus.FatalLevel:
color = ansi.Red
case logrus.PanicLevel:
color = ansi.Red
default:
color = ansi.Blue
}
level := strings.ToUpper(entry.Level.String())[0:4]
if value, ok := entry.Data["prefix"]; ok {
prefix = fmt.Sprint(" ", ansi.Cyan, value, ":", clear)
}
fmt.Fprintf(b, "%s[%s]%s %s%+5s%s%s %s", ansi.LightBlack, entry.Time.Format(f.TimestampFormat), clear, color, level, clear, prefix, entry.Message)
for _, k := range keys {
fmt.Fprintf(b, " %s%s%s=%+v", color, k, clear, entry.Data[k])
}
}
func needsQuoting(text string) bool {
for _, ch := range text {
if !((ch >= 'a' && ch <= 'z') ||
(ch >= 'A' && ch <= 'Z') ||
(ch >= '0' && ch <= '9') ||
ch == '-' || ch == '.') {
return false
}
}
return true
}

Some files were not shown because too many files have changed in this diff Show more