Prepare conversion to version 1.0
This commit is contained in:
parent
b92219b04b
commit
24e58ba4d1
349 changed files with 0 additions and 74930 deletions
|
@ -1,3 +0,0 @@
|
|||
*
|
||||
!app
|
||||
!surreal
|
|
@ -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
43
.gitignore
vendored
|
@ -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
|
|
@ -1,7 +0,0 @@
|
|||
FROM alpine:latest
|
||||
|
||||
RUN apk add --update --no-cache ca-certificates
|
||||
|
||||
ADD surreal /usr/bin/
|
||||
|
||||
ENTRYPOINT ["surreal"]
|
201
LICENSE
201
LICENSE
|
@ -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.
|
61
Makefile
61
Makefile
|
@ -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
|
77
README.md
77
README.md
|
@ -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
|
||||
```
|
|
@ -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')'"'
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
56
cli/cli.go
56
cli/cli.go
|
@ -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)
|
||||
}
|
||||
}
|
151
cli/export.go
151
cli/export.go
|
@ -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.")
|
||||
|
||||
}
|
141
cli/import.go
141
cli/import.go
|
@ -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.")
|
||||
|
||||
}
|
27
cli/logo.go
27
cli/logo.go
|
@ -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"
|
||||
|
||||
`
|
250
cli/setup.go
250
cli/setup.go
|
@ -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
|
||||
|
||||
}
|
99
cli/start.go
99
cli/start.go
|
@ -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")
|
||||
|
||||
}
|
|
@ -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()
|
||||
|
||||
},
|
||||
}
|
39
cnf/auth.go
39
cnf/auth.go
|
@ -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
|
||||
|
||||
}
|
71
cnf/cnf.go
71
cnf/cnf.go
|
@ -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
|
||||
}
|
||||
}
|
53
cnf/kind.go
53
cnf/kind.go
|
@ -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
|
||||
}
|
39
db/access.go
39
db/access.go
|
@ -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
|
||||
|
||||
}
|
58
db/cache.go
58
db/cache.go
|
@ -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
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
}
|
195
db/check.go
195
db/check.go
|
@ -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
|
||||
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
178
db/create.go
178
db/create.go
|
@ -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)
|
||||
|
||||
}
|
|
@ -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)
|
||||
|
||||
})
|
||||
|
||||
}
|
493
db/db.gen.go
493
db/db.gen.go
|
@ -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
273
db/db.go
|
@ -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
|
||||
|
||||
}
|
|
@ -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
|
||||
|
||||
}
|
291
db/define.go
291
db/define.go
|
@ -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
|
||||
|
||||
}
|
1383
db/define_test.go
1383
db/define_test.go
File diff suppressed because it is too large
Load diff
183
db/delete.go
183
db/delete.go
|
@ -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)
|
||||
|
||||
}
|
|
@ -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")
|
||||
|
||||
})
|
||||
|
||||
}
|
444
db/document.go
444
db/document.go
|
@ -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
|
||||
|
||||
}
|
141
db/error.go
141
db/error.go
|
@ -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)
|
||||
}
|
105
db/event.go
105
db/event.go
|
@ -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
|
||||
|
||||
}
|
543
db/executor.go
543
db/executor.go
|
@ -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()
|
||||
}
|
||||
}
|
315
db/export.go
315
db/export.go
|
@ -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
|
||||
|
||||
}
|
1461
db/fetch.go
1461
db/fetch.go
File diff suppressed because it is too large
Load diff
1037
db/fetch_test.go
1037
db/fetch_test.go
File diff suppressed because it is too large
Load diff
18
db/gen.go
18
db/gen.go
|
@ -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
|
56
db/ifelse.go
56
db/ifelse.go
|
@ -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)
|
||||
|
||||
}
|
|
@ -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)
|
||||
|
||||
})
|
||||
|
||||
}
|
261
db/info.go
261
db/info.go
|
@ -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
|
||||
|
||||
}
|
180
db/info_test.go
180
db/info_test.go
|
@ -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)
|
||||
|
||||
})
|
||||
|
||||
}
|
146
db/insert.go
146
db/insert.go
|
@ -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)
|
||||
|
||||
}
|
|
@ -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)
|
||||
|
||||
})
|
||||
|
||||
}
|
1057
db/iterator.go
1057
db/iterator.go
File diff suppressed because it is too large
Load diff
43
db/let.go
43
db/let.go
|
@ -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
|
||||
|
||||
}
|
|
@ -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})
|
||||
|
||||
})
|
||||
|
||||
}
|
78
db/live.go
78
db/live.go
|
@ -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{}
|
||||
|
||||
}
|
139
db/lives.go
139
db/lives.go
|
@ -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
|
||||
|
||||
}
|
444
db/merge.go
444
db/merge.go
|
@ -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
|
||||
|
||||
}
|
88
db/mutex.go
88
db/mutex.go
|
@ -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)
|
||||
}
|
386
db/mutex_test.go
386
db/mutex_test.go
|
@ -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)
|
||||
|
||||
})
|
||||
|
||||
}
|
67
db/opt.go
67
db/opt.go
|
@ -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
|
||||
|
||||
}
|
99
db/perms.go
99
db/perms.go
|
@ -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
|
||||
|
||||
}
|
35
db/pool.go
35
db/pool.go
|
@ -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)
|
||||
},
|
||||
}
|
176
db/relate.go
176
db/relate.go
|
@ -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)
|
||||
|
||||
}
|
270
db/remove.go
270
db/remove.go
|
@ -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
|
||||
|
||||
}
|
|
@ -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)
|
||||
|
||||
})
|
||||
|
||||
}
|
44
db/return.go
44
db/return.go
|
@ -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
|
||||
|
||||
}
|
|
@ -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{}{})
|
||||
|
||||
})
|
||||
|
||||
}
|
39
db/run.go
39
db/run.go
|
@ -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
|
||||
|
||||
}
|
106
db/run_test.go
106
db/run_test.go
|
@ -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()
|
||||
|
||||
}()
|
||||
|
||||
})
|
||||
|
||||
}
|
250
db/scope_test.go
250
db/scope_test.go
|
@ -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",
|
||||
},
|
||||
})
|
||||
|
||||
}()
|
||||
|
||||
})
|
||||
|
||||
}
|
185
db/select.go
185
db/select.go
|
@ -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)
|
||||
|
||||
}
|
2167
db/select_test.go
2167
db/select_test.go
File diff suppressed because it is too large
Load diff
|
@ -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
|
||||
|
||||
}
|
421
db/socket.go
421
db/socket.go
|
@ -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
|
||||
|
||||
}
|
668
db/table.go
668
db/table.go
|
@ -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),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
189
db/update.go
189
db/update.go
|
@ -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)
|
||||
|
||||
}
|
|
@ -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"))
|
||||
|
||||
})
|
||||
|
||||
}
|
152
db/upsert.go
152
db/upsert.go
|
@ -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)
|
||||
|
||||
}
|
|
@ -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)
|
||||
|
||||
})
|
||||
|
||||
}
|
98
db/use.go
98
db/use.go
|
@ -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
|
||||
|
||||
}
|
123
db/vars.go
123
db/vars.go
|
@ -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}
|
||||
)
|
239
db/yield.go
239
db/yield.go
|
@ -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
|
||||
|
||||
}
|
|
@ -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)
|
||||
|
||||
})
|
||||
|
||||
}
|
|
@ -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}
|
||||
```
|
409
doc/QUERIES.md
409
doc/QUERIES.md
|
@ -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;
|
||||
```
|
140
doc/SECURITY.md
140
doc/SECURITY.md
|
@ -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
|
||||
```
|
|
@ -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
68
go.mod
|
@ -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
756
go.sum
|
@ -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=
|
28
kvs/db.go
28
kvs/db.go
|
@ -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
|
||||
}
|
80
kvs/ds.go
80
kvs/ds.go
|
@ -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()
|
||||
}
|
28
kvs/err.go
28
kvs/err.go
|
@ -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)
|
||||
}
|
23
kvs/kv.go
23
kvs/kv.go
|
@ -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
|
||||
}
|
77
kvs/main.go
77
kvs/main.go
|
@ -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
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
|
||||
})
|
||||
|
||||
}
|
139
kvs/rixxdb/tx.go
139
kvs/rixxdb/tx.go
|
@ -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)
|
||||
}
|
44
kvs/tx.go
44
kvs/tx.go
|
@ -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)
|
||||
}
|
104
log/hook.go
104
log/hook.go
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
78
log/json.go
78
log/json.go
|
@ -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
|
||||
|
||||
}
|
327
log/log.go
327
log/log.go
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
|
||||
}
|
169
log/text.go
169
log/text.go
|
@ -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
Loading…
Reference in a new issue