mirror of
https://github.com/pikami/cosmium.git
synced 2026-04-20 13:28:46 +01:00
Compare commits
6 Commits
v0.1.9
...
84c33e3c8e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84c33e3c8e | ||
|
|
5e677431a3 | ||
|
|
a4659d90a9 | ||
|
|
503e6bb8ad | ||
|
|
e5ddc143f0 | ||
|
|
66ea859f34 |
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.21.6
|
||||
go-version: 1.22.0
|
||||
- name: Docker Login
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
builds:
|
||||
- binary: cosmium
|
||||
main: ./cmd/server
|
||||
goos:
|
||||
- darwin
|
||||
- linux
|
||||
@@ -27,11 +28,14 @@ brews:
|
||||
email: git@pikami.org
|
||||
|
||||
dockers:
|
||||
- image_templates:
|
||||
- "ghcr.io/pikami/{{ .ProjectName }}:{{ .Version }}"
|
||||
- "ghcr.io/pikami/{{ .ProjectName }}:latest"
|
||||
- id: docker-linux-amd64
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
image_templates:
|
||||
- "ghcr.io/pikami/{{ .ProjectName }}:{{ .Version }}-amd64"
|
||||
- "ghcr.io/pikami/{{ .ProjectName }}:latest-amd64"
|
||||
dockerfile: Dockerfile
|
||||
use: docker
|
||||
use: buildx
|
||||
build_flag_templates:
|
||||
- "--platform=linux/amd64"
|
||||
- "--pull"
|
||||
@@ -42,6 +46,38 @@ dockers:
|
||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||
- id: docker-linux-arm64
|
||||
goos: linux
|
||||
goarch: arm64
|
||||
image_templates:
|
||||
- "ghcr.io/pikami/{{ .ProjectName }}:{{ .Version }}-arm64"
|
||||
- "ghcr.io/pikami/{{ .ProjectName }}:latest-arm64"
|
||||
- "ghcr.io/pikami/{{ .ProjectName }}:{{ .Version }}-arm64v8"
|
||||
- "ghcr.io/pikami/{{ .ProjectName }}:latest-arm64v8"
|
||||
dockerfile: Dockerfile
|
||||
use: buildx
|
||||
build_flag_templates:
|
||||
- "--platform=linux/arm64"
|
||||
- "--pull"
|
||||
- "--label=org.opencontainers.image.title={{.ProjectName}}"
|
||||
- "--label=org.opencontainers.image.description=Lightweight Cosmos DB emulator"
|
||||
- "--label=org.opencontainers.image.url=https://github.com/pikami/cosmium"
|
||||
- "--label=org.opencontainers.image.source=https://github.com/pikami/cosmium"
|
||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||
|
||||
docker_manifests:
|
||||
- name_template: 'ghcr.io/pikami/{{ .ProjectName }}:latest'
|
||||
image_templates:
|
||||
- "ghcr.io/pikami/{{ .ProjectName }}:latest-amd64"
|
||||
- "ghcr.io/pikami/{{ .ProjectName }}:latest-arm64"
|
||||
- "ghcr.io/pikami/{{ .ProjectName }}:latest-arm64v8"
|
||||
- name_template: 'ghcr.io/pikami/{{ .ProjectName }}:{{ .Version }}'
|
||||
image_templates:
|
||||
- "ghcr.io/pikami/{{ .ProjectName }}:{{ .Version }}-amd64"
|
||||
- "ghcr.io/pikami/{{ .ProjectName }}:{{ .Version }}-arm64"
|
||||
- "ghcr.io/pikami/{{ .ProjectName }}:{{ .Version }}-arm64v8"
|
||||
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
|
||||
26
Makefile
26
Makefile
@@ -4,28 +4,44 @@ GOTEST=$(GOCMD) test
|
||||
GOCLEAN=$(GOCMD) clean
|
||||
|
||||
BINARY_NAME=cosmium
|
||||
SERVER_LOCATION=./cmd/server
|
||||
|
||||
SHARED_LIB_LOCATION=./sharedlibrary
|
||||
SHARED_LIB_OPT=-buildmode=c-shared
|
||||
|
||||
DIST_DIR=dist
|
||||
|
||||
all: test build-all
|
||||
|
||||
build-all: build-darwin-arm64 build-darwin-amd64 build-linux-amd64 build-windows-amd64
|
||||
build-all: build-darwin-arm64 build-darwin-amd64 build-linux-amd64 build-linux-arm64 build-windows-amd64 build-windows-arm64
|
||||
|
||||
build-darwin-arm64:
|
||||
@echo "Building macOS ARM binary..."
|
||||
@GOOS=darwin GOARCH=arm64 $(GOBUILD) -o $(DIST_DIR)/$(BINARY_NAME)-darwin-arm64 .
|
||||
@GOOS=darwin GOARCH=arm64 $(GOBUILD) -o $(DIST_DIR)/$(BINARY_NAME)-darwin-arm64 $(SERVER_LOCATION)
|
||||
|
||||
build-darwin-amd64:
|
||||
@echo "Building macOS x64 binary..."
|
||||
@GOOS=darwin GOARCH=amd64 $(GOBUILD) -o $(DIST_DIR)/$(BINARY_NAME)-darwin-amd64 .
|
||||
@GOOS=darwin GOARCH=amd64 $(GOBUILD) -o $(DIST_DIR)/$(BINARY_NAME)-darwin-amd64 $(SERVER_LOCATION)
|
||||
|
||||
build-linux-amd64:
|
||||
@echo "Building Linux x64 binary..."
|
||||
@GOOS=linux GOARCH=amd64 $(GOBUILD) -o $(DIST_DIR)/$(BINARY_NAME)-linux-amd64 .
|
||||
@GOOS=linux GOARCH=amd64 $(GOBUILD) -o $(DIST_DIR)/$(BINARY_NAME)-linux-amd64 $(SERVER_LOCATION)
|
||||
|
||||
build-linux-arm64:
|
||||
@echo "Building Linux ARM binary..."
|
||||
@GOOS=linux GOARCH=arm64 $(GOBUILD) -o $(DIST_DIR)/$(BINARY_NAME)-linux-arm64 $(SERVER_LOCATION)
|
||||
|
||||
build-windows-amd64:
|
||||
@echo "Building Windows x64 binary..."
|
||||
@GOOS=windows GOARCH=amd64 $(GOBUILD) -o $(DIST_DIR)/$(BINARY_NAME)-windows-amd64.exe .
|
||||
@GOOS=windows GOARCH=amd64 $(GOBUILD) -o $(DIST_DIR)/$(BINARY_NAME)-windows-amd64.exe $(SERVER_LOCATION)
|
||||
|
||||
build-windows-arm64:
|
||||
@echo "Building Windows ARM binary..."
|
||||
@GOOS=windows GOARCH=arm64 $(GOBUILD) -o $(DIST_DIR)/$(BINARY_NAME)-windows-arm64.exe $(SERVER_LOCATION)
|
||||
|
||||
build-sharedlib-linux-amd64:
|
||||
@echo "Building shared library for Linux x64..."
|
||||
@GOOS=linux GOARCH=amd64 $(GOBUILD) $(SHARED_LIB_OPT) -o $(DIST_DIR)/$(BINARY_NAME)-linux-amd64.so $(SHARED_LIB_LOCATION)
|
||||
|
||||
generate-parser-nosql:
|
||||
pigeon -o ./parsers/nosql/nosql.go ./parsers/nosql/nosql.peg
|
||||
|
||||
@@ -26,9 +26,11 @@ You can download the latest version of Cosmium from the [GitHub Releases page](h
|
||||
Cosmium is available for the following platforms:
|
||||
|
||||
- **Linux**: cosmium-linux-amd64
|
||||
- **Linux on ARM**: cosmium-linux-arm64
|
||||
- **macOS**: cosmium-darwin-amd64
|
||||
- **macOS on Apple Silicon**: cosmium-darwin-arm64
|
||||
- **Windows**: cosmium-windows-amd64.exe
|
||||
- **Windows on ARM**: cosmium-windows-arm64.exe
|
||||
|
||||
### Running Cosmium
|
||||
|
||||
|
||||
@@ -8,5 +8,11 @@ import (
|
||||
)
|
||||
|
||||
func CosmiumExport(c *gin.Context) {
|
||||
c.IndentedJSON(http.StatusOK, repositories.GetState())
|
||||
repositoryState, err := repositories.GetState()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.Data(http.StatusOK, "application/json", []byte(repositoryState))
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
@@ -12,6 +13,10 @@ import (
|
||||
tlsprovider "github.com/pikami/cosmium/internal/tls_provider"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
StopServer chan interface{}
|
||||
}
|
||||
|
||||
func CreateRouter() *gin.Engine {
|
||||
router := gin.Default(func(e *gin.Engine) {
|
||||
e.RedirectTrailingSlash = false
|
||||
@@ -57,42 +62,60 @@ func CreateRouter() *gin.Engine {
|
||||
return router
|
||||
}
|
||||
|
||||
func StartAPI() {
|
||||
func StartAPI() *Server {
|
||||
if !config.Config.Debug {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
}
|
||||
|
||||
router := CreateRouter()
|
||||
listenAddress := fmt.Sprintf(":%d", config.Config.Port)
|
||||
stopChan := make(chan interface{})
|
||||
|
||||
server := &http.Server{
|
||||
Addr: listenAddress,
|
||||
Handler: router.Handler(),
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-stopChan
|
||||
logger.Info("Shutting down server...")
|
||||
err := server.Shutdown(context.TODO())
|
||||
if err != nil {
|
||||
logger.Error("Failed to shutdown server:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
if config.Config.DisableTls {
|
||||
logger.Infof("Listening and serving HTTP on %s\n", server.Addr)
|
||||
err := server.ListenAndServe()
|
||||
if err != nil {
|
||||
logger.Error("Failed to start HTTP server:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if config.Config.TLS_CertificatePath != "" && config.Config.TLS_CertificateKey != "" {
|
||||
err := router.RunTLS(
|
||||
listenAddress,
|
||||
logger.Infof("Listening and serving HTTPS on %s\n", server.Addr)
|
||||
err := server.ListenAndServeTLS(
|
||||
config.Config.TLS_CertificatePath,
|
||||
config.Config.TLS_CertificateKey)
|
||||
if err != nil {
|
||||
logger.Error("Failed to start HTTPS server:", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if config.Config.DisableTls {
|
||||
router.Run(listenAddress)
|
||||
}
|
||||
|
||||
} else {
|
||||
tlsConfig := tlsprovider.GetDefaultTlsConfig()
|
||||
server := &http.Server{
|
||||
Addr: listenAddress,
|
||||
Handler: router.Handler(),
|
||||
TLSConfig: tlsConfig,
|
||||
}
|
||||
server.TLSConfig = tlsConfig
|
||||
|
||||
logger.Infof("Listening and serving HTTPS on %s\n", server.Addr)
|
||||
err := server.ListenAndServeTLS("", "")
|
||||
if err != nil {
|
||||
logger.Error("Failed to start HTTPS server:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
router.Run()
|
||||
return &Server{StopServer: stopChan}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos"
|
||||
@@ -162,6 +164,56 @@ func Test_Documents(t *testing.T) {
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("Should handle parallel writes", func(t *testing.T) {
|
||||
var wg sync.WaitGroup
|
||||
rutineCount := 100
|
||||
results := make(chan error, rutineCount)
|
||||
|
||||
createCall := func(i int) {
|
||||
defer wg.Done()
|
||||
item := map[string]interface{}{
|
||||
"id": fmt.Sprintf("id-%d", i),
|
||||
"pk": fmt.Sprintf("pk-%d", i),
|
||||
"val": i,
|
||||
}
|
||||
bytes, err := json.Marshal(item)
|
||||
if err != nil {
|
||||
results <- err
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
|
||||
_, err = collectionClient.CreateItem(
|
||||
ctx,
|
||||
azcosmos.PartitionKey{},
|
||||
bytes,
|
||||
&azcosmos.ItemOptions{
|
||||
EnableContentResponseOnWrite: false,
|
||||
},
|
||||
)
|
||||
results <- err
|
||||
|
||||
collectionClient.ReadItem(ctx, azcosmos.PartitionKey{}, fmt.Sprintf("id-%d", i), nil)
|
||||
collectionClient.DeleteItem(ctx, azcosmos.PartitionKey{}, fmt.Sprintf("id-%d", i), nil)
|
||||
}
|
||||
|
||||
for i := 0; i < rutineCount; i++ {
|
||||
wg.Add(1)
|
||||
go createCall(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
close(results)
|
||||
|
||||
for err := range results {
|
||||
if err != nil {
|
||||
t.Errorf("Error creating item: %v", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Documents_Patch(t *testing.T) {
|
||||
|
||||
@@ -15,18 +15,21 @@ func main() {
|
||||
|
||||
repositories.InitializeRepository()
|
||||
|
||||
go api.StartAPI()
|
||||
server := api.StartAPI()
|
||||
|
||||
waitForExit()
|
||||
waitForExit(server)
|
||||
}
|
||||
|
||||
func waitForExit() {
|
||||
func waitForExit(server *api.Server) {
|
||||
sigs := make(chan os.Signal, 1)
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
// Block until a exit signal is received
|
||||
<-sigs
|
||||
|
||||
// Stop the server
|
||||
server.StopServer <- true
|
||||
|
||||
if config.Config.PersistDataFilePath != "" {
|
||||
repositories.SaveStateFS(config.Config.PersistDataFilePath)
|
||||
}
|
||||
@@ -18,8 +18,8 @@ Cosmium strives to support the core features of Cosmos DB, including:
|
||||
|
||||
| Feature | Implemented |
|
||||
| ----------------------------- | ----------- |
|
||||
| Subqueries | No |
|
||||
| Joins | No |
|
||||
| Subqueries | Yes |
|
||||
| Joins | Yes |
|
||||
| Computed properties | No |
|
||||
| Coalesce operators | No |
|
||||
| Bitwise operators | No |
|
||||
@@ -63,9 +63,11 @@ Cosmium strives to support the core features of Cosmos DB, including:
|
||||
### Array Functions
|
||||
|
||||
| Function | Implemented |
|
||||
| -------------- | ----------- |
|
||||
| ------------------ | ----------- |
|
||||
| ARRAY_CONCAT | Yes |
|
||||
| ARRAY_CONTAINS | No |
|
||||
| ARRAY_CONTAINS_ANY | No |
|
||||
| ARRAY_CONTAINS_ALL | No |
|
||||
| ARRAY_LENGTH | Yes |
|
||||
| ARRAY_SLICE | Yes |
|
||||
| CHOOSE | No |
|
||||
|
||||
30
go.mod
30
go.mod
@@ -1,6 +1,6 @@
|
||||
module github.com/pikami/cosmium
|
||||
|
||||
go 1.21.6
|
||||
go 1.22.0
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.2
|
||||
@@ -9,39 +9,39 @@ require (
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc
|
||||
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 // indirect
|
||||
github.com/bytedance/sonic v1.11.8 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/bytedance/sonic v1.12.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.1 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.4 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.7 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.21.0 // indirect
|
||||
github.com/goccy/go-json v0.10.3 // indirect
|
||||
github.com/go-playground/validator/v10 v10.23.0 // indirect
|
||||
github.com/goccy/go-json v0.10.4 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/crypto v0.23.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/text v0.15.0 // indirect
|
||||
google.golang.org/protobuf v1.34.1 // indirect
|
||||
golang.org/x/arch v0.12.0 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/net v0.32.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
google.golang.org/protobuf v1.36.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
60
go.sum
60
go.sum
@@ -10,10 +10,11 @@ github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 h1:LqbJ/WzJUwBf8UiaSzgX7aM
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2/go.mod h1:yInRyqWXAuaPrgI7p70+lDDgh3mlBohis29jGMISnmc=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0 h1:WVsrXCnHlDDX8ls+tootqRE87/hL9S/g4ewig9RsD/c=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4=
|
||||
github.com/bytedance/sonic v1.11.8 h1:Zw/j1KfiS+OYTi9lyB3bb0CFxPJVkM17k1wyDG32LRA=
|
||||
github.com/bytedance/sonic v1.11.8/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||
github.com/bytedance/sonic v1.12.6 h1:/isNmCUF2x3Sh8RAp/4mh4ZGkcFAX/hLrzrK3AvpRzk=
|
||||
github.com/bytedance/sonic v1.12.6/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/bytedance/sonic/loader v0.2.1 h1:1GgorWTqf12TA8mma4DDSbaQigE2wOgQo7iCjjJv3+E=
|
||||
github.com/bytedance/sonic/loader v0.2.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||
@@ -23,8 +24,8 @@ 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/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=
|
||||
github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
|
||||
github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I=
|
||||
github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s=
|
||||
github.com/gabriel-vasile/mimetype v1.4.7 h1:SKFKl7kD0RiPdbht0s7hFtjl489WcQ1VyPW8ZzUMYCA=
|
||||
github.com/gabriel-vasile/mimetype v1.4.7/go.mod h1:GDlAgAyIRT27BhFl53XNAFtfjzOkLaF35JdEG0P7LtU=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
@@ -35,10 +36,10 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.21.0 h1:4fZA11ovvtkdgaeev9RGWPgc1uj3H8W+rNYyH/ySBb0=
|
||||
github.com/go-playground/validator/v10 v10.21.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o=
|
||||
github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
|
||||
github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c=
|
||||
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
@@ -49,8 +50,8 @@ github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
@@ -63,8 +64,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 h1:Qj1ukM4GlMWXNdMBuXcXfz/Kw9s1qm0CLY32QxuSImI=
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@@ -74,40 +75,35 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc h1:O9NuF4s+E/PvMIy+9IUZB9znFwUIXEWSstNjek6VpVg=
|
||||
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/arch v0.12.0 h1:UsYJhbzPYGsT0HbEdmYcqtCv8UNGvnaL561NnIUvaKg=
|
||||
golang.org/x/arch v0.12.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo=
|
||||
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
||||
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
|
||||
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ=
|
||||
google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
||||
@@ -12,6 +12,9 @@ import (
|
||||
)
|
||||
|
||||
func GetAllCollections(databaseId string) ([]repositorymodels.Collection, repositorymodels.RepositoryStatus) {
|
||||
storeState.RLock()
|
||||
defer storeState.RUnlock()
|
||||
|
||||
if _, ok := storeState.Databases[databaseId]; !ok {
|
||||
return make([]repositorymodels.Collection, 0), repositorymodels.StatusNotFound
|
||||
}
|
||||
@@ -20,6 +23,9 @@ func GetAllCollections(databaseId string) ([]repositorymodels.Collection, reposi
|
||||
}
|
||||
|
||||
func GetCollection(databaseId string, collectionId string) (repositorymodels.Collection, repositorymodels.RepositoryStatus) {
|
||||
storeState.RLock()
|
||||
defer storeState.RUnlock()
|
||||
|
||||
if _, ok := storeState.Databases[databaseId]; !ok {
|
||||
return repositorymodels.Collection{}, repositorymodels.StatusNotFound
|
||||
}
|
||||
@@ -32,6 +38,9 @@ func GetCollection(databaseId string, collectionId string) (repositorymodels.Col
|
||||
}
|
||||
|
||||
func DeleteCollection(databaseId string, collectionId string) repositorymodels.RepositoryStatus {
|
||||
storeState.Lock()
|
||||
defer storeState.Unlock()
|
||||
|
||||
if _, ok := storeState.Databases[databaseId]; !ok {
|
||||
return repositorymodels.StatusNotFound
|
||||
}
|
||||
@@ -46,6 +55,9 @@ func DeleteCollection(databaseId string, collectionId string) repositorymodels.R
|
||||
}
|
||||
|
||||
func CreateCollection(databaseId string, newCollection repositorymodels.Collection) (repositorymodels.Collection, repositorymodels.RepositoryStatus) {
|
||||
storeState.Lock()
|
||||
defer storeState.Unlock()
|
||||
|
||||
var ok bool
|
||||
var database repositorymodels.Database
|
||||
if database, ok = storeState.Databases[databaseId]; !ok {
|
||||
|
||||
@@ -11,10 +11,16 @@ import (
|
||||
)
|
||||
|
||||
func GetAllDatabases() ([]repositorymodels.Database, repositorymodels.RepositoryStatus) {
|
||||
storeState.RLock()
|
||||
defer storeState.RUnlock()
|
||||
|
||||
return maps.Values(storeState.Databases), repositorymodels.StatusOk
|
||||
}
|
||||
|
||||
func GetDatabase(id string) (repositorymodels.Database, repositorymodels.RepositoryStatus) {
|
||||
storeState.RLock()
|
||||
defer storeState.RUnlock()
|
||||
|
||||
if database, ok := storeState.Databases[id]; ok {
|
||||
return database, repositorymodels.StatusOk
|
||||
}
|
||||
@@ -23,6 +29,9 @@ func GetDatabase(id string) (repositorymodels.Database, repositorymodels.Reposit
|
||||
}
|
||||
|
||||
func DeleteDatabase(id string) repositorymodels.RepositoryStatus {
|
||||
storeState.Lock()
|
||||
defer storeState.Unlock()
|
||||
|
||||
if _, ok := storeState.Databases[id]; !ok {
|
||||
return repositorymodels.StatusNotFound
|
||||
}
|
||||
@@ -33,6 +42,9 @@ func DeleteDatabase(id string) repositorymodels.RepositoryStatus {
|
||||
}
|
||||
|
||||
func CreateDatabase(newDatabase repositorymodels.Database) (repositorymodels.Database, repositorymodels.RepositoryStatus) {
|
||||
storeState.Lock()
|
||||
defer storeState.Unlock()
|
||||
|
||||
if _, ok := storeState.Databases[newDatabase.ID]; ok {
|
||||
return repositorymodels.Database{}, repositorymodels.Conflict
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@ import (
|
||||
)
|
||||
|
||||
func GetAllDocuments(databaseId string, collectionId string) ([]repositorymodels.Document, repositorymodels.RepositoryStatus) {
|
||||
storeState.RLock()
|
||||
defer storeState.RUnlock()
|
||||
|
||||
if _, ok := storeState.Databases[databaseId]; !ok {
|
||||
return make([]repositorymodels.Document, 0), repositorymodels.StatusNotFound
|
||||
}
|
||||
@@ -27,6 +30,9 @@ func GetAllDocuments(databaseId string, collectionId string) ([]repositorymodels
|
||||
}
|
||||
|
||||
func GetDocument(databaseId string, collectionId string, documentId string) (repositorymodels.Document, repositorymodels.RepositoryStatus) {
|
||||
storeState.RLock()
|
||||
defer storeState.RUnlock()
|
||||
|
||||
if _, ok := storeState.Databases[databaseId]; !ok {
|
||||
return repositorymodels.Document{}, repositorymodels.StatusNotFound
|
||||
}
|
||||
@@ -43,6 +49,9 @@ func GetDocument(databaseId string, collectionId string, documentId string) (rep
|
||||
}
|
||||
|
||||
func DeleteDocument(databaseId string, collectionId string, documentId string) repositorymodels.RepositoryStatus {
|
||||
storeState.Lock()
|
||||
defer storeState.Unlock()
|
||||
|
||||
if _, ok := storeState.Databases[databaseId]; !ok {
|
||||
return repositorymodels.StatusNotFound
|
||||
}
|
||||
@@ -61,6 +70,9 @@ func DeleteDocument(databaseId string, collectionId string, documentId string) r
|
||||
}
|
||||
|
||||
func CreateDocument(databaseId string, collectionId string, document map[string]interface{}) (repositorymodels.Document, repositorymodels.RepositoryStatus) {
|
||||
storeState.Lock()
|
||||
defer storeState.Unlock()
|
||||
|
||||
var ok bool
|
||||
var documentId string
|
||||
var database repositorymodels.Database
|
||||
@@ -111,7 +123,7 @@ func ExecuteQueryDocuments(databaseId string, collectionId string, query string,
|
||||
|
||||
if typedQuery, ok := parsedQuery.(parsers.SelectStmt); ok {
|
||||
typedQuery.Parameters = queryParameters
|
||||
return memoryexecutor.Execute(typedQuery, covDocs), repositorymodels.StatusOk
|
||||
return memoryexecutor.ExecuteQuery(typedQuery, covDocs), repositorymodels.StatusOk
|
||||
}
|
||||
|
||||
return nil, repositorymodels.BadRequest
|
||||
|
||||
@@ -10,6 +10,9 @@ import (
|
||||
|
||||
// I have no idea what this is tbh
|
||||
func GetPartitionKeyRanges(databaseId string, collectionId string) ([]repositorymodels.PartitionKeyRange, repositorymodels.RepositoryStatus) {
|
||||
storeState.RLock()
|
||||
defer storeState.RUnlock()
|
||||
|
||||
databaseRid := databaseId
|
||||
collectionRid := collectionId
|
||||
var timestamp int64 = 0
|
||||
|
||||
@@ -66,6 +66,9 @@ func LoadStateFS(filePath string) {
|
||||
}
|
||||
|
||||
func SaveStateFS(filePath string) {
|
||||
storeState.RLock()
|
||||
defer storeState.RUnlock()
|
||||
|
||||
data, err := json.MarshalIndent(storeState, "", "\t")
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to save state: %v\n", err)
|
||||
@@ -80,8 +83,17 @@ func SaveStateFS(filePath string) {
|
||||
logger.Infof("Documents: %d\n", getLength(storeState.Documents))
|
||||
}
|
||||
|
||||
func GetState() repositorymodels.State {
|
||||
return storeState
|
||||
func GetState() (string, error) {
|
||||
storeState.RLock()
|
||||
defer storeState.RUnlock()
|
||||
|
||||
data, err := json.MarshalIndent(storeState, "", "\t")
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to serialize state: %v\n", err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
func getLength(v interface{}) int {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package repositorymodels
|
||||
|
||||
import "sync"
|
||||
|
||||
type Database struct {
|
||||
ID string `json:"id"`
|
||||
TimeStamp int64 `json:"_ts"`
|
||||
@@ -101,6 +103,8 @@ type PartitionKeyRange struct {
|
||||
}
|
||||
|
||||
type State struct {
|
||||
sync.RWMutex
|
||||
|
||||
// Map databaseId -> Database
|
||||
Databases map[string]Database `json:"databases"`
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ type SelectStmt struct {
|
||||
Table Table
|
||||
JoinItems []JoinItem
|
||||
Filters interface{}
|
||||
Exists bool
|
||||
Distinct bool
|
||||
Count int
|
||||
Offset int
|
||||
@@ -15,6 +16,7 @@ type SelectStmt struct {
|
||||
|
||||
type Table struct {
|
||||
Value string
|
||||
SelectItem SelectItem
|
||||
}
|
||||
|
||||
type JoinItem struct {
|
||||
@@ -30,6 +32,7 @@ const (
|
||||
SelectItemTypeArray
|
||||
SelectItemTypeConstant
|
||||
SelectItemTypeFunctionCall
|
||||
SelectItemTypeSubQuery
|
||||
)
|
||||
|
||||
type SelectItem struct {
|
||||
|
||||
@@ -122,4 +122,25 @@ func Test_Parse(t *testing.T) {
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("Should parse IN selector", func(t *testing.T) {
|
||||
testQueryParse(
|
||||
t,
|
||||
`SELECT c.id FROM c IN c.tags`,
|
||||
parsers.SelectStmt{
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{
|
||||
Path: []string{"c", "id"},
|
||||
Type: parsers.SelectItemTypeField,
|
||||
},
|
||||
},
|
||||
Table: parsers.Table{
|
||||
Value: "c",
|
||||
SelectItem: parsers.SelectItem{
|
||||
Path: []string{"c", "tags"},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,14 +4,17 @@ package nosql
|
||||
import "github.com/pikami/cosmium/parsers"
|
||||
|
||||
func makeSelectStmt(
|
||||
columns, table, joinItems,
|
||||
columns, fromClause, joinItems,
|
||||
whereClause interface{}, distinctClause interface{},
|
||||
count interface{}, groupByClause interface{}, orderList interface{},
|
||||
offsetClause interface{},
|
||||
) (parsers.SelectStmt, error) {
|
||||
selectStmt := parsers.SelectStmt{
|
||||
SelectItems: columns.([]parsers.SelectItem),
|
||||
Table: table.(parsers.Table),
|
||||
}
|
||||
|
||||
if fromTable, ok := fromClause.(parsers.Table); ok {
|
||||
selectStmt.Table = fromTable
|
||||
}
|
||||
|
||||
if joinItemsArray, ok := joinItems.([]interface{}); ok && len(joinItemsArray) > 0 {
|
||||
@@ -56,10 +59,18 @@ func makeSelectStmt(
|
||||
}
|
||||
|
||||
func makeJoin(table interface{}, column interface{}) (parsers.JoinItem, error) {
|
||||
return parsers.JoinItem{
|
||||
Table: table.(parsers.Table),
|
||||
SelectItem: column.(parsers.SelectItem),
|
||||
}, nil
|
||||
joinItem := parsers.JoinItem{}
|
||||
|
||||
if selectItem, isSelectItem := column.(parsers.SelectItem); isSelectItem {
|
||||
joinItem.SelectItem = selectItem
|
||||
joinItem.Table.Value = selectItem.Alias
|
||||
}
|
||||
|
||||
if tableTyped, isTable := table.(parsers.Table); isTable {
|
||||
joinItem.Table = tableTyped
|
||||
}
|
||||
|
||||
return joinItem, nil
|
||||
}
|
||||
|
||||
func makeSelectItem(name interface{}, path interface{}, selectItemType parsers.SelectItemType) (parsers.SelectItem, error) {
|
||||
@@ -177,13 +188,13 @@ SelectStmt <- Select ws
|
||||
distinctClause:DistinctClause? ws
|
||||
topClause:TopClause? ws
|
||||
columns:Selection ws
|
||||
From ws table:TableName ws
|
||||
joinClauses:JoinClause* ws
|
||||
fromClause:FromClause? ws
|
||||
joinClauses:(ws join:JoinClause { return join, nil })* ws
|
||||
whereClause:(ws Where ws condition:Condition { return condition, nil })?
|
||||
groupByClause:(ws GroupBy ws columns:ColumnList { return columns, nil })?
|
||||
orderByClause:OrderByClause?
|
||||
offsetClause:OffsetClause? {
|
||||
return makeSelectStmt(columns, table, joinClauses, whereClause,
|
||||
orderByClause:(ws order:OrderByClause { return order, nil })?
|
||||
offsetClause:(ws offset:OffsetClause { return offset, nil })? {
|
||||
return makeSelectStmt(columns, fromClause, joinClauses, whereClause,
|
||||
distinctClause, topClause, groupByClause, orderByClause, offsetClause)
|
||||
}
|
||||
|
||||
@@ -193,8 +204,49 @@ TopClause <- Top ws count:Integer {
|
||||
return count, nil
|
||||
}
|
||||
|
||||
FromClause <- From ws table:TableName selectItem:(ws "IN"i ws column:SelectItem { return column, nil })? {
|
||||
tableTyped := table.(parsers.Table)
|
||||
|
||||
if selectItem != nil {
|
||||
tableTyped.SelectItem = selectItem.(parsers.SelectItem)
|
||||
}
|
||||
|
||||
return tableTyped, nil
|
||||
} / From ws subQuery:SubQuerySelectItem {
|
||||
subQueryTyped := subQuery.(parsers.SelectItem)
|
||||
table := parsers.Table{
|
||||
Value: subQueryTyped.Alias,
|
||||
SelectItem: subQueryTyped,
|
||||
}
|
||||
return table, nil
|
||||
}
|
||||
|
||||
SubQuery <- exists:(exists:Exists ws { return exists, nil })? "(" ws selectStmt:SelectStmt ws ")" {
|
||||
if selectStatement, isGoodValue := selectStmt.(parsers.SelectStmt); isGoodValue {
|
||||
selectStatement.Exists = exists != nil
|
||||
return selectStatement, nil
|
||||
}
|
||||
|
||||
return selectStmt, nil
|
||||
}
|
||||
|
||||
SubQuerySelectItem <- subQuery:SubQuery asClause:(ws alias:AsClause { return alias, nil })? {
|
||||
selectItem := parsers.SelectItem{
|
||||
Type: parsers.SelectItemTypeSubQuery,
|
||||
Value: subQuery,
|
||||
}
|
||||
|
||||
if tableName, isString := asClause.(string); isString {
|
||||
selectItem.Alias = tableName
|
||||
}
|
||||
|
||||
return selectItem, nil
|
||||
}
|
||||
|
||||
JoinClause <- Join ws table:TableName ws "IN"i ws column:SelectItem {
|
||||
return makeJoin(table, column)
|
||||
} / Join ws subQuery:SubQuerySelectItem {
|
||||
return makeJoin(nil, subQuery)
|
||||
}
|
||||
|
||||
OffsetClause <- "OFFSET"i ws offset:IntegerLiteral ws "LIMIT"i ws limit:IntegerLiteral {
|
||||
@@ -241,7 +293,7 @@ SelectProperty <- name:Identifier path:(DotFieldAccess / ArrayFieldAccess)* {
|
||||
return makeSelectItem(name, path, parsers.SelectItemTypeField)
|
||||
}
|
||||
|
||||
SelectItem <- selectItem:(Literal / FunctionCall / SelectArray / SelectObject / SelectProperty) asClause:AsClause? {
|
||||
SelectItem <- selectItem:(SubQuerySelectItem / Literal / FunctionCall / SelectArray / SelectObject / SelectProperty) asClause:AsClause? {
|
||||
var itemResult parsers.SelectItem
|
||||
switch typedValue := selectItem.(type) {
|
||||
case parsers.SelectItem:
|
||||
@@ -322,11 +374,13 @@ From <- "FROM"i
|
||||
|
||||
Join <- "JOIN"i
|
||||
|
||||
Exists <- "EXISTS"i
|
||||
|
||||
Where <- "WHERE"i
|
||||
|
||||
And <- "AND"i
|
||||
|
||||
Or <- "OR"i
|
||||
Or <- "OR"i wss
|
||||
|
||||
GroupBy <- "GROUP"i ws "BY"i
|
||||
|
||||
@@ -677,4 +731,6 @@ non_escape_character <- !(escape_character) char:.
|
||||
|
||||
ws <- [ \t\n\r]*
|
||||
|
||||
wss <- [ \t\n\r]+
|
||||
|
||||
EOF <- !.
|
||||
|
||||
125
parsers/nosql/subquery_test.go
Normal file
125
parsers/nosql/subquery_test.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package nosql_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/pikami/cosmium/parsers"
|
||||
)
|
||||
|
||||
func Test_Parse_SubQuery(t *testing.T) {
|
||||
|
||||
t.Run("Should parse FROM subquery", func(t *testing.T) {
|
||||
testQueryParse(
|
||||
t,
|
||||
`SELECT c.id FROM (SELECT VALUE cc["info"] FROM cc) AS c`,
|
||||
parsers.SelectStmt{
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{Path: []string{"c", "id"}},
|
||||
},
|
||||
Table: parsers.Table{
|
||||
Value: "c",
|
||||
SelectItem: parsers.SelectItem{
|
||||
Alias: "c",
|
||||
Type: parsers.SelectItemTypeSubQuery,
|
||||
Value: parsers.SelectStmt{
|
||||
Table: parsers.Table{Value: "cc"},
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{Path: []string{"cc", "info"}, IsTopLevel: true},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("Should parse JOIN subquery", func(t *testing.T) {
|
||||
testQueryParse(
|
||||
t,
|
||||
`SELECT c.id, cc.name FROM c JOIN (SELECT tag.name FROM tag IN c.tags) AS cc`,
|
||||
parsers.SelectStmt{
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{Path: []string{"c", "id"}},
|
||||
{Path: []string{"cc", "name"}},
|
||||
},
|
||||
Table: parsers.Table{
|
||||
Value: "c",
|
||||
},
|
||||
JoinItems: []parsers.JoinItem{
|
||||
{
|
||||
Table: parsers.Table{
|
||||
Value: "cc",
|
||||
},
|
||||
SelectItem: parsers.SelectItem{
|
||||
Alias: "cc",
|
||||
Type: parsers.SelectItemTypeSubQuery,
|
||||
Value: parsers.SelectStmt{
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{Path: []string{"tag", "name"}},
|
||||
},
|
||||
Table: parsers.Table{
|
||||
Value: "tag",
|
||||
SelectItem: parsers.SelectItem{
|
||||
Path: []string{"c", "tags"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("Should parse JOIN EXISTS subquery", func(t *testing.T) {
|
||||
testQueryParse(
|
||||
t,
|
||||
`SELECT c.id
|
||||
FROM c
|
||||
JOIN (
|
||||
SELECT VALUE EXISTS(SELECT tag.name FROM tag IN c.tags)
|
||||
) AS hasTags
|
||||
WHERE hasTags`,
|
||||
parsers.SelectStmt{
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{Path: []string{"c", "id"}},
|
||||
},
|
||||
Table: parsers.Table{
|
||||
Value: "c",
|
||||
},
|
||||
JoinItems: []parsers.JoinItem{
|
||||
{
|
||||
Table: parsers.Table{Value: "hasTags"},
|
||||
SelectItem: parsers.SelectItem{
|
||||
Alias: "hasTags",
|
||||
Type: parsers.SelectItemTypeSubQuery,
|
||||
Value: parsers.SelectStmt{
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{
|
||||
IsTopLevel: true,
|
||||
Type: parsers.SelectItemTypeSubQuery,
|
||||
Value: parsers.SelectStmt{
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{Path: []string{"tag", "name"}},
|
||||
},
|
||||
Table: parsers.Table{
|
||||
Value: "tag",
|
||||
SelectItem: parsers.SelectItem{
|
||||
Path: []string{"c", "tags"},
|
||||
},
|
||||
},
|
||||
Exists: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Filters: parsers.SelectItem{
|
||||
Path: []string{"hasTags"},
|
||||
},
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -6,14 +6,13 @@ import (
|
||||
"github.com/pikami/cosmium/parsers"
|
||||
)
|
||||
|
||||
func (c memoryExecutorContext) aggregate_Avg(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) aggregate_Avg(arguments []interface{}) interface{} {
|
||||
selectExpression := arguments[0].(parsers.SelectItem)
|
||||
sum := 0.0
|
||||
count := 0
|
||||
|
||||
if array, isArray := row.([]RowWithJoins); isArray {
|
||||
for _, item := range array {
|
||||
value := c.getFieldValue(selectExpression, item)
|
||||
for _, item := range r.grouppedRows {
|
||||
value := item.resolveSelectItem(selectExpression)
|
||||
if numericValue, ok := value.(float64); ok {
|
||||
sum += numericValue
|
||||
count++
|
||||
@@ -22,7 +21,6 @@ func (c memoryExecutorContext) aggregate_Avg(arguments []interface{}, row RowTyp
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if count > 0 {
|
||||
return sum / float64(count)
|
||||
@@ -31,30 +29,27 @@ func (c memoryExecutorContext) aggregate_Avg(arguments []interface{}, row RowTyp
|
||||
}
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) aggregate_Count(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) aggregate_Count(arguments []interface{}) interface{} {
|
||||
selectExpression := arguments[0].(parsers.SelectItem)
|
||||
count := 0
|
||||
|
||||
if array, isArray := row.([]RowWithJoins); isArray {
|
||||
for _, item := range array {
|
||||
value := c.getFieldValue(selectExpression, item)
|
||||
for _, item := range r.grouppedRows {
|
||||
value := item.resolveSelectItem(selectExpression)
|
||||
if value != nil {
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) aggregate_Max(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) aggregate_Max(arguments []interface{}) interface{} {
|
||||
selectExpression := arguments[0].(parsers.SelectItem)
|
||||
max := 0.0
|
||||
count := 0
|
||||
|
||||
if array, isArray := row.([]RowWithJoins); isArray {
|
||||
for _, item := range array {
|
||||
value := c.getFieldValue(selectExpression, item)
|
||||
for _, item := range r.grouppedRows {
|
||||
value := item.resolveSelectItem(selectExpression)
|
||||
if numericValue, ok := value.(float64); ok {
|
||||
if numericValue > max {
|
||||
max = numericValue
|
||||
@@ -67,7 +62,6 @@ func (c memoryExecutorContext) aggregate_Max(arguments []interface{}, row RowTyp
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if count > 0 {
|
||||
return max
|
||||
@@ -76,14 +70,13 @@ func (c memoryExecutorContext) aggregate_Max(arguments []interface{}, row RowTyp
|
||||
}
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) aggregate_Min(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) aggregate_Min(arguments []interface{}) interface{} {
|
||||
selectExpression := arguments[0].(parsers.SelectItem)
|
||||
min := math.MaxFloat64
|
||||
count := 0
|
||||
|
||||
if array, isArray := row.([]RowWithJoins); isArray {
|
||||
for _, item := range array {
|
||||
value := c.getFieldValue(selectExpression, item)
|
||||
for _, item := range r.grouppedRows {
|
||||
value := item.resolveSelectItem(selectExpression)
|
||||
if numericValue, ok := value.(float64); ok {
|
||||
if numericValue < min {
|
||||
min = numericValue
|
||||
@@ -96,7 +89,6 @@ func (c memoryExecutorContext) aggregate_Min(arguments []interface{}, row RowTyp
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if count > 0 {
|
||||
return min
|
||||
@@ -105,14 +97,13 @@ func (c memoryExecutorContext) aggregate_Min(arguments []interface{}, row RowTyp
|
||||
}
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) aggregate_Sum(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) aggregate_Sum(arguments []interface{}) interface{} {
|
||||
selectExpression := arguments[0].(parsers.SelectItem)
|
||||
sum := 0.0
|
||||
count := 0
|
||||
|
||||
if array, isArray := row.([]RowWithJoins); isArray {
|
||||
for _, item := range array {
|
||||
value := c.getFieldValue(selectExpression, item)
|
||||
for _, item := range r.grouppedRows {
|
||||
value := item.resolveSelectItem(selectExpression)
|
||||
if numericValue, ok := value.(float64); ok {
|
||||
sum += numericValue
|
||||
count++
|
||||
@@ -121,7 +112,6 @@ func (c memoryExecutorContext) aggregate_Sum(arguments []interface{}, row RowTyp
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if count > 0 {
|
||||
return sum
|
||||
|
||||
@@ -7,17 +7,17 @@ import (
|
||||
"github.com/pikami/cosmium/parsers"
|
||||
)
|
||||
|
||||
func (c memoryExecutorContext) array_Concat(arguments []interface{}, row RowType) []interface{} {
|
||||
func (r rowContext) array_Concat(arguments []interface{}) []interface{} {
|
||||
var result []interface{}
|
||||
for _, arg := range arguments {
|
||||
array := c.parseArray(arg, row)
|
||||
array := r.parseArray(arg)
|
||||
result = append(result, array...)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) array_Length(arguments []interface{}, row RowType) int {
|
||||
array := c.parseArray(arguments[0], row)
|
||||
func (r rowContext) array_Length(arguments []interface{}) int {
|
||||
array := r.parseArray(arguments[0])
|
||||
if array == nil {
|
||||
return 0
|
||||
}
|
||||
@@ -25,15 +25,15 @@ func (c memoryExecutorContext) array_Length(arguments []interface{}, row RowType
|
||||
return len(array)
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) array_Slice(arguments []interface{}, row RowType) []interface{} {
|
||||
func (r rowContext) array_Slice(arguments []interface{}) []interface{} {
|
||||
var ok bool
|
||||
var start int
|
||||
var length int
|
||||
array := c.parseArray(arguments[0], row)
|
||||
startEx := c.getFieldValue(arguments[1].(parsers.SelectItem), row)
|
||||
array := r.parseArray(arguments[0])
|
||||
startEx := r.resolveSelectItem(arguments[1].(parsers.SelectItem))
|
||||
|
||||
if arguments[2] != nil {
|
||||
lengthEx := c.getFieldValue(arguments[2].(parsers.SelectItem), row)
|
||||
lengthEx := r.resolveSelectItem(arguments[2].(parsers.SelectItem))
|
||||
|
||||
if length, ok = lengthEx.(int); !ok {
|
||||
logger.Error("array_Slice - got length parameters of wrong type")
|
||||
@@ -65,9 +65,9 @@ func (c memoryExecutorContext) array_Slice(arguments []interface{}, row RowType)
|
||||
return array[start:end]
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) set_Intersect(arguments []interface{}, row RowType) []interface{} {
|
||||
set1 := c.parseArray(arguments[0], row)
|
||||
set2 := c.parseArray(arguments[1], row)
|
||||
func (r rowContext) set_Intersect(arguments []interface{}) []interface{} {
|
||||
set1 := r.parseArray(arguments[0])
|
||||
set2 := r.parseArray(arguments[1])
|
||||
|
||||
intersection := make(map[interface{}]struct{})
|
||||
if set1 == nil || set2 == nil {
|
||||
@@ -88,9 +88,9 @@ func (c memoryExecutorContext) set_Intersect(arguments []interface{}, row RowTyp
|
||||
return result
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) set_Union(arguments []interface{}, row RowType) []interface{} {
|
||||
set1 := c.parseArray(arguments[0], row)
|
||||
set2 := c.parseArray(arguments[1], row)
|
||||
func (r rowContext) set_Union(arguments []interface{}) []interface{} {
|
||||
set1 := r.parseArray(arguments[0])
|
||||
set2 := r.parseArray(arguments[1])
|
||||
|
||||
var result []interface{}
|
||||
union := make(map[interface{}]struct{})
|
||||
@@ -111,9 +111,9 @@ func (c memoryExecutorContext) set_Union(arguments []interface{}, row RowType) [
|
||||
return result
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) parseArray(argument interface{}, row RowType) []interface{} {
|
||||
func (r rowContext) parseArray(argument interface{}) []interface{} {
|
||||
exItem := argument.(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
arrValue := reflect.ValueOf(ex)
|
||||
if arrValue.Kind() != reflect.Slice {
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
"github.com/pikami/cosmium/parsers"
|
||||
)
|
||||
|
||||
func (c memoryExecutorContext) math_Abs(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_Abs(arguments []interface{}) interface{} {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
switch val := ex.(type) {
|
||||
case float64:
|
||||
@@ -26,9 +26,9 @@ func (c memoryExecutorContext) math_Abs(arguments []interface{}, row RowType) in
|
||||
}
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_Acos(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_Acos(arguments []interface{}) interface{} {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
val, valIsNumber := numToFloat64(ex)
|
||||
if !valIsNumber {
|
||||
@@ -44,9 +44,9 @@ func (c memoryExecutorContext) math_Acos(arguments []interface{}, row RowType) i
|
||||
return math.Acos(val) * 180 / math.Pi
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_Asin(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_Asin(arguments []interface{}) interface{} {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
val, valIsNumber := numToFloat64(ex)
|
||||
if !valIsNumber {
|
||||
@@ -62,9 +62,9 @@ func (c memoryExecutorContext) math_Asin(arguments []interface{}, row RowType) i
|
||||
return math.Asin(val) * 180 / math.Pi
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_Atan(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_Atan(arguments []interface{}) interface{} {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
val, valIsNumber := numToFloat64(ex)
|
||||
if !valIsNumber {
|
||||
@@ -75,9 +75,9 @@ func (c memoryExecutorContext) math_Atan(arguments []interface{}, row RowType) i
|
||||
return math.Atan(val) * 180 / math.Pi
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_Ceiling(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_Ceiling(arguments []interface{}) interface{} {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
switch val := ex.(type) {
|
||||
case float64:
|
||||
@@ -90,9 +90,9 @@ func (c memoryExecutorContext) math_Ceiling(arguments []interface{}, row RowType
|
||||
}
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_Cos(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_Cos(arguments []interface{}) interface{} {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
val, valIsNumber := numToFloat64(ex)
|
||||
if !valIsNumber {
|
||||
@@ -103,9 +103,9 @@ func (c memoryExecutorContext) math_Cos(arguments []interface{}, row RowType) in
|
||||
return math.Cos(val)
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_Cot(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_Cot(arguments []interface{}) interface{} {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
val, valIsNumber := numToFloat64(ex)
|
||||
if !valIsNumber {
|
||||
@@ -121,9 +121,9 @@ func (c memoryExecutorContext) math_Cot(arguments []interface{}, row RowType) in
|
||||
return 1 / math.Tan(val)
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_Degrees(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_Degrees(arguments []interface{}) interface{} {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
val, valIsNumber := numToFloat64(ex)
|
||||
if !valIsNumber {
|
||||
@@ -134,9 +134,9 @@ func (c memoryExecutorContext) math_Degrees(arguments []interface{}, row RowType
|
||||
return val * (180 / math.Pi)
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_Exp(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_Exp(arguments []interface{}) interface{} {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
val, valIsNumber := numToFloat64(ex)
|
||||
if !valIsNumber {
|
||||
@@ -147,9 +147,9 @@ func (c memoryExecutorContext) math_Exp(arguments []interface{}, row RowType) in
|
||||
return math.Exp(val)
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_Floor(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_Floor(arguments []interface{}) interface{} {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
switch val := ex.(type) {
|
||||
case float64:
|
||||
@@ -162,9 +162,9 @@ func (c memoryExecutorContext) math_Floor(arguments []interface{}, row RowType)
|
||||
}
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_IntBitNot(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_IntBitNot(arguments []interface{}) interface{} {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
switch val := ex.(type) {
|
||||
case int:
|
||||
@@ -175,9 +175,9 @@ func (c memoryExecutorContext) math_IntBitNot(arguments []interface{}, row RowTy
|
||||
}
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_Log10(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_Log10(arguments []interface{}) interface{} {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
val, valIsNumber := numToFloat64(ex)
|
||||
if !valIsNumber {
|
||||
@@ -193,9 +193,9 @@ func (c memoryExecutorContext) math_Log10(arguments []interface{}, row RowType)
|
||||
return math.Log10(val)
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_Radians(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_Radians(arguments []interface{}) interface{} {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
val, valIsNumber := numToFloat64(ex)
|
||||
if !valIsNumber {
|
||||
@@ -206,9 +206,9 @@ func (c memoryExecutorContext) math_Radians(arguments []interface{}, row RowType
|
||||
return val * (math.Pi / 180.0)
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_Round(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_Round(arguments []interface{}) interface{} {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
switch val := ex.(type) {
|
||||
case float64:
|
||||
@@ -221,9 +221,9 @@ func (c memoryExecutorContext) math_Round(arguments []interface{}, row RowType)
|
||||
}
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_Sign(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_Sign(arguments []interface{}) interface{} {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
switch val := ex.(type) {
|
||||
case float64:
|
||||
@@ -248,9 +248,9 @@ func (c memoryExecutorContext) math_Sign(arguments []interface{}, row RowType) i
|
||||
}
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_Sin(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_Sin(arguments []interface{}) interface{} {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
val, valIsNumber := numToFloat64(ex)
|
||||
if !valIsNumber {
|
||||
@@ -261,9 +261,9 @@ func (c memoryExecutorContext) math_Sin(arguments []interface{}, row RowType) in
|
||||
return math.Sin(val)
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_Sqrt(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_Sqrt(arguments []interface{}) interface{} {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
val, valIsNumber := numToFloat64(ex)
|
||||
if !valIsNumber {
|
||||
@@ -274,9 +274,9 @@ func (c memoryExecutorContext) math_Sqrt(arguments []interface{}, row RowType) i
|
||||
return math.Sqrt(val)
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_Square(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_Square(arguments []interface{}) interface{} {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
val, valIsNumber := numToFloat64(ex)
|
||||
if !valIsNumber {
|
||||
@@ -287,9 +287,9 @@ func (c memoryExecutorContext) math_Square(arguments []interface{}, row RowType)
|
||||
return math.Pow(val, 2)
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_Tan(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_Tan(arguments []interface{}) interface{} {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
val, valIsNumber := numToFloat64(ex)
|
||||
if !valIsNumber {
|
||||
@@ -300,9 +300,9 @@ func (c memoryExecutorContext) math_Tan(arguments []interface{}, row RowType) in
|
||||
return math.Tan(val)
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_Trunc(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_Trunc(arguments []interface{}) interface{} {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
switch val := ex.(type) {
|
||||
case float64:
|
||||
@@ -315,11 +315,11 @@ func (c memoryExecutorContext) math_Trunc(arguments []interface{}, row RowType)
|
||||
}
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_Atn2(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_Atn2(arguments []interface{}) interface{} {
|
||||
exItem1 := arguments[0].(parsers.SelectItem)
|
||||
exItem2 := arguments[1].(parsers.SelectItem)
|
||||
ex1 := c.getFieldValue(exItem1, row)
|
||||
ex2 := c.getFieldValue(exItem2, row)
|
||||
ex1 := r.resolveSelectItem(exItem1)
|
||||
ex2 := r.resolveSelectItem(exItem2)
|
||||
|
||||
y, yIsNumber := numToFloat64(ex1)
|
||||
x, xIsNumber := numToFloat64(ex2)
|
||||
@@ -332,11 +332,11 @@ func (c memoryExecutorContext) math_Atn2(arguments []interface{}, row RowType) i
|
||||
return math.Atan2(y, x)
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_IntAdd(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_IntAdd(arguments []interface{}) interface{} {
|
||||
exItem1 := arguments[0].(parsers.SelectItem)
|
||||
exItem2 := arguments[1].(parsers.SelectItem)
|
||||
ex1 := c.getFieldValue(exItem1, row)
|
||||
ex2 := c.getFieldValue(exItem2, row)
|
||||
ex1 := r.resolveSelectItem(exItem1)
|
||||
ex2 := r.resolveSelectItem(exItem2)
|
||||
|
||||
ex1Number, ex1IsNumber := numToInt(ex1)
|
||||
ex2Number, ex2IsNumber := numToInt(ex2)
|
||||
@@ -349,11 +349,11 @@ func (c memoryExecutorContext) math_IntAdd(arguments []interface{}, row RowType)
|
||||
return ex1Number + ex2Number
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_IntBitAnd(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_IntBitAnd(arguments []interface{}) interface{} {
|
||||
exItem1 := arguments[0].(parsers.SelectItem)
|
||||
exItem2 := arguments[1].(parsers.SelectItem)
|
||||
ex1 := c.getFieldValue(exItem1, row)
|
||||
ex2 := c.getFieldValue(exItem2, row)
|
||||
ex1 := r.resolveSelectItem(exItem1)
|
||||
ex2 := r.resolveSelectItem(exItem2)
|
||||
|
||||
ex1Int, ex1IsInt := numToInt(ex1)
|
||||
ex2Int, ex2IsInt := numToInt(ex2)
|
||||
@@ -366,11 +366,11 @@ func (c memoryExecutorContext) math_IntBitAnd(arguments []interface{}, row RowTy
|
||||
return ex1Int & ex2Int
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_IntBitLeftShift(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_IntBitLeftShift(arguments []interface{}) interface{} {
|
||||
exItem1 := arguments[0].(parsers.SelectItem)
|
||||
exItem2 := arguments[1].(parsers.SelectItem)
|
||||
ex1 := c.getFieldValue(exItem1, row)
|
||||
ex2 := c.getFieldValue(exItem2, row)
|
||||
ex1 := r.resolveSelectItem(exItem1)
|
||||
ex2 := r.resolveSelectItem(exItem2)
|
||||
|
||||
num1, num1IsInt := numToInt(ex1)
|
||||
num2, num2IsInt := numToInt(ex2)
|
||||
@@ -383,11 +383,11 @@ func (c memoryExecutorContext) math_IntBitLeftShift(arguments []interface{}, row
|
||||
return num1 << uint(num2)
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_IntBitOr(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_IntBitOr(arguments []interface{}) interface{} {
|
||||
exItem1 := arguments[0].(parsers.SelectItem)
|
||||
exItem2 := arguments[1].(parsers.SelectItem)
|
||||
ex1 := c.getFieldValue(exItem1, row)
|
||||
ex2 := c.getFieldValue(exItem2, row)
|
||||
ex1 := r.resolveSelectItem(exItem1)
|
||||
ex2 := r.resolveSelectItem(exItem2)
|
||||
|
||||
num1, num1IsInt := ex1.(int)
|
||||
num2, num2IsInt := ex2.(int)
|
||||
@@ -400,11 +400,11 @@ func (c memoryExecutorContext) math_IntBitOr(arguments []interface{}, row RowTyp
|
||||
return num1 | num2
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_IntBitRightShift(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_IntBitRightShift(arguments []interface{}) interface{} {
|
||||
exItem1 := arguments[0].(parsers.SelectItem)
|
||||
exItem2 := arguments[1].(parsers.SelectItem)
|
||||
ex1 := c.getFieldValue(exItem1, row)
|
||||
ex2 := c.getFieldValue(exItem2, row)
|
||||
ex1 := r.resolveSelectItem(exItem1)
|
||||
ex2 := r.resolveSelectItem(exItem2)
|
||||
|
||||
num1, num1IsInt := numToInt(ex1)
|
||||
num2, num2IsInt := numToInt(ex2)
|
||||
@@ -417,11 +417,11 @@ func (c memoryExecutorContext) math_IntBitRightShift(arguments []interface{}, ro
|
||||
return num1 >> uint(num2)
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_IntBitXor(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_IntBitXor(arguments []interface{}) interface{} {
|
||||
exItem1 := arguments[0].(parsers.SelectItem)
|
||||
exItem2 := arguments[1].(parsers.SelectItem)
|
||||
ex1 := c.getFieldValue(exItem1, row)
|
||||
ex2 := c.getFieldValue(exItem2, row)
|
||||
ex1 := r.resolveSelectItem(exItem1)
|
||||
ex2 := r.resolveSelectItem(exItem2)
|
||||
|
||||
num1, num1IsInt := ex1.(int)
|
||||
num2, num2IsInt := ex2.(int)
|
||||
@@ -434,11 +434,11 @@ func (c memoryExecutorContext) math_IntBitXor(arguments []interface{}, row RowTy
|
||||
return num1 ^ num2
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_IntDiv(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_IntDiv(arguments []interface{}) interface{} {
|
||||
exItem1 := arguments[0].(parsers.SelectItem)
|
||||
exItem2 := arguments[1].(parsers.SelectItem)
|
||||
ex1 := c.getFieldValue(exItem1, row)
|
||||
ex2 := c.getFieldValue(exItem2, row)
|
||||
ex1 := r.resolveSelectItem(exItem1)
|
||||
ex2 := r.resolveSelectItem(exItem2)
|
||||
|
||||
num1, num1IsInt := ex1.(int)
|
||||
num2, num2IsInt := ex2.(int)
|
||||
@@ -451,11 +451,11 @@ func (c memoryExecutorContext) math_IntDiv(arguments []interface{}, row RowType)
|
||||
return num1 / num2
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_IntMul(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_IntMul(arguments []interface{}) interface{} {
|
||||
exItem1 := arguments[0].(parsers.SelectItem)
|
||||
exItem2 := arguments[1].(parsers.SelectItem)
|
||||
ex1 := c.getFieldValue(exItem1, row)
|
||||
ex2 := c.getFieldValue(exItem2, row)
|
||||
ex1 := r.resolveSelectItem(exItem1)
|
||||
ex2 := r.resolveSelectItem(exItem2)
|
||||
|
||||
num1, num1IsInt := ex1.(int)
|
||||
num2, num2IsInt := ex2.(int)
|
||||
@@ -468,11 +468,11 @@ func (c memoryExecutorContext) math_IntMul(arguments []interface{}, row RowType)
|
||||
return num1 * num2
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_IntSub(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_IntSub(arguments []interface{}) interface{} {
|
||||
exItem1 := arguments[0].(parsers.SelectItem)
|
||||
exItem2 := arguments[1].(parsers.SelectItem)
|
||||
ex1 := c.getFieldValue(exItem1, row)
|
||||
ex2 := c.getFieldValue(exItem2, row)
|
||||
ex1 := r.resolveSelectItem(exItem1)
|
||||
ex2 := r.resolveSelectItem(exItem2)
|
||||
|
||||
num1, num1IsInt := ex1.(int)
|
||||
num2, num2IsInt := ex2.(int)
|
||||
@@ -485,11 +485,11 @@ func (c memoryExecutorContext) math_IntSub(arguments []interface{}, row RowType)
|
||||
return num1 - num2
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_IntMod(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_IntMod(arguments []interface{}) interface{} {
|
||||
exItem1 := arguments[0].(parsers.SelectItem)
|
||||
exItem2 := arguments[1].(parsers.SelectItem)
|
||||
ex1 := c.getFieldValue(exItem1, row)
|
||||
ex2 := c.getFieldValue(exItem2, row)
|
||||
ex1 := r.resolveSelectItem(exItem1)
|
||||
ex2 := r.resolveSelectItem(exItem2)
|
||||
|
||||
num1, num1IsInt := ex1.(int)
|
||||
num2, num2IsInt := ex2.(int)
|
||||
@@ -502,11 +502,11 @@ func (c memoryExecutorContext) math_IntMod(arguments []interface{}, row RowType)
|
||||
return num1 % num2
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_Power(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_Power(arguments []interface{}) interface{} {
|
||||
exItem1 := arguments[0].(parsers.SelectItem)
|
||||
exItem2 := arguments[1].(parsers.SelectItem)
|
||||
ex1 := c.getFieldValue(exItem1, row)
|
||||
ex2 := c.getFieldValue(exItem2, row)
|
||||
ex1 := r.resolveSelectItem(exItem1)
|
||||
ex2 := r.resolveSelectItem(exItem2)
|
||||
|
||||
base, baseIsNumber := numToFloat64(ex1)
|
||||
exponent, exponentIsNumber := numToFloat64(ex2)
|
||||
@@ -519,14 +519,14 @@ func (c memoryExecutorContext) math_Power(arguments []interface{}, row RowType)
|
||||
return math.Pow(base, exponent)
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_Log(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_Log(arguments []interface{}) interface{} {
|
||||
exItem1 := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem1, row)
|
||||
ex := r.resolveSelectItem(exItem1)
|
||||
|
||||
var base float64 = math.E
|
||||
if len(arguments) > 1 {
|
||||
exItem2 := arguments[1].(parsers.SelectItem)
|
||||
baseValueObject := c.getFieldValue(exItem2, row)
|
||||
baseValueObject := r.resolveSelectItem(exItem2)
|
||||
baseValue, baseValueIsNumber := numToFloat64(baseValueObject)
|
||||
|
||||
if !baseValueIsNumber {
|
||||
@@ -551,15 +551,15 @@ func (c memoryExecutorContext) math_Log(arguments []interface{}, row RowType) in
|
||||
return math.Log(num) / math.Log(base)
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_NumberBin(arguments []interface{}, row RowType) interface{} {
|
||||
func (r rowContext) math_NumberBin(arguments []interface{}) interface{} {
|
||||
exItem1 := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem1, row)
|
||||
ex := r.resolveSelectItem(exItem1)
|
||||
|
||||
binSize := 1.0
|
||||
|
||||
if len(arguments) > 1 {
|
||||
exItem2 := arguments[1].(parsers.SelectItem)
|
||||
binSizeValueObject := c.getFieldValue(exItem2, row)
|
||||
binSizeValueObject := r.resolveSelectItem(exItem2)
|
||||
binSizeValue, binSizeValueIsNumber := numToFloat64(binSizeValueObject)
|
||||
|
||||
if !binSizeValueIsNumber {
|
||||
@@ -584,11 +584,11 @@ func (c memoryExecutorContext) math_NumberBin(arguments []interface{}, row RowTy
|
||||
return math.Floor(num/binSize) * binSize
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_Pi() interface{} {
|
||||
func (r rowContext) math_Pi() interface{} {
|
||||
return math.Pi
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) math_Rand() interface{} {
|
||||
func (r rowContext) math_Rand() interface{} {
|
||||
return rand.Float64()
|
||||
}
|
||||
|
||||
|
||||
@@ -13,137 +13,174 @@ import (
|
||||
)
|
||||
|
||||
type RowType interface{}
|
||||
type RowWithJoins map[string]RowType
|
||||
type ExpressionType interface{}
|
||||
|
||||
type memoryExecutorContext struct {
|
||||
type rowContext struct {
|
||||
tables map[string]RowType
|
||||
parameters map[string]interface{}
|
||||
grouppedRows []rowContext
|
||||
}
|
||||
|
||||
func Execute(query parsers.SelectStmt, data []RowType) []RowType {
|
||||
ctx := memoryExecutorContext{
|
||||
parameters: query.Parameters,
|
||||
func ExecuteQuery(query parsers.SelectStmt, documents []RowType) []RowType {
|
||||
currentDocuments := make([]rowContext, 0)
|
||||
for _, doc := range documents {
|
||||
currentDocuments = append(currentDocuments, resolveFrom(query, doc)...)
|
||||
}
|
||||
|
||||
joinedRows := make([]RowWithJoins, 0)
|
||||
for _, row := range data {
|
||||
// Perform joins
|
||||
dataTables := map[string][]RowType{}
|
||||
|
||||
for _, join := range query.JoinItems {
|
||||
joinedData := ctx.getFieldValue(join.SelectItem, row)
|
||||
if joinedDataArray, isArray := joinedData.([]map[string]interface{}); isArray {
|
||||
var rows []RowType
|
||||
for _, m := range joinedDataArray {
|
||||
rows = append(rows, RowType(m))
|
||||
}
|
||||
dataTables[join.Table.Value] = rows
|
||||
}
|
||||
}
|
||||
|
||||
// Generate flat rows
|
||||
flatRows := []RowWithJoins{
|
||||
{query.Table.Value: row},
|
||||
}
|
||||
for joinedTableName, joinedTable := range dataTables {
|
||||
flatRows = zipRows(flatRows, joinedTableName, joinedTable)
|
||||
// Handle JOINS
|
||||
nextDocuments := make([]rowContext, 0)
|
||||
for _, currentDocument := range currentDocuments {
|
||||
rowContexts := currentDocument.handleJoin(query)
|
||||
nextDocuments = append(nextDocuments, rowContexts...)
|
||||
}
|
||||
currentDocuments = nextDocuments
|
||||
|
||||
// Apply filters
|
||||
filteredRows := []RowWithJoins{}
|
||||
for _, rowWithJoins := range flatRows {
|
||||
if ctx.evaluateFilters(query.Filters, rowWithJoins) {
|
||||
filteredRows = append(filteredRows, rowWithJoins)
|
||||
nextDocuments = make([]rowContext, 0)
|
||||
for _, currentDocument := range currentDocuments {
|
||||
if currentDocument.applyFilters(query.Filters) {
|
||||
nextDocuments = append(nextDocuments, currentDocument)
|
||||
}
|
||||
}
|
||||
|
||||
joinedRows = append(joinedRows, filteredRows...)
|
||||
}
|
||||
currentDocuments = nextDocuments
|
||||
|
||||
// Apply order
|
||||
if query.OrderExpressions != nil && len(query.OrderExpressions) > 0 {
|
||||
ctx.orderBy(query.OrderExpressions, joinedRows)
|
||||
if len(query.OrderExpressions) > 0 {
|
||||
applyOrder(currentDocuments, query.OrderExpressions)
|
||||
}
|
||||
|
||||
result := make([]RowType, 0)
|
||||
|
||||
// Apply group
|
||||
isGroupSelect := query.GroupBy != nil && len(query.GroupBy) > 0
|
||||
if isGroupSelect {
|
||||
result = ctx.groupBy(query, joinedRows)
|
||||
// Apply group by
|
||||
if len(query.GroupBy) > 0 {
|
||||
currentDocuments = applyGroupBy(currentDocuments, query.GroupBy)
|
||||
}
|
||||
|
||||
// Apply select
|
||||
if !isGroupSelect {
|
||||
selectedData := make([]RowType, 0)
|
||||
if hasAggregateFunctions(query.SelectItems) {
|
||||
// When can have aggregate functions without GROUP BY clause,
|
||||
// we should aggregate all rows in that case
|
||||
selectedData = append(selectedData, ctx.selectRow(query.SelectItems, joinedRows))
|
||||
} else {
|
||||
for _, row := range joinedRows {
|
||||
selectedData = append(selectedData, ctx.selectRow(query.SelectItems, row))
|
||||
}
|
||||
}
|
||||
|
||||
result = selectedData
|
||||
}
|
||||
projectedDocuments := applyProjection(currentDocuments, query.SelectItems, query.GroupBy)
|
||||
|
||||
// Apply distinct
|
||||
if query.Distinct {
|
||||
result = deduplicate(result)
|
||||
projectedDocuments = deduplicate(projectedDocuments)
|
||||
}
|
||||
|
||||
// Apply result limit
|
||||
if query.Count > 0 {
|
||||
count := func() int {
|
||||
if len(result) < query.Count {
|
||||
return len(result)
|
||||
}
|
||||
return query.Count
|
||||
}()
|
||||
result = result[:count]
|
||||
if query.Count > 0 && len(projectedDocuments) > query.Count {
|
||||
projectedDocuments = projectedDocuments[:query.Count]
|
||||
}
|
||||
|
||||
return result
|
||||
return projectedDocuments
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) selectRow(selectItems []parsers.SelectItem, row interface{}) interface{} {
|
||||
// When the first value is top level, select it instead
|
||||
if len(selectItems) > 0 && selectItems[0].IsTopLevel {
|
||||
return c.getFieldValue(selectItems[0], row)
|
||||
func resolveFrom(query parsers.SelectStmt, doc RowType) []rowContext {
|
||||
initialRow, gotParentContext := doc.(rowContext)
|
||||
if !gotParentContext {
|
||||
var initialTableName string
|
||||
if query.Table.SelectItem.Type == parsers.SelectItemTypeSubQuery {
|
||||
initialTableName = query.Table.SelectItem.Value.(parsers.SelectStmt).Table.Value
|
||||
}
|
||||
|
||||
// Construct a new row based on the selected columns
|
||||
newRow := make(map[string]interface{})
|
||||
for index, column := range selectItems {
|
||||
destinationName := column.Alias
|
||||
if destinationName == "" {
|
||||
if len(column.Path) > 0 {
|
||||
destinationName = column.Path[len(column.Path)-1]
|
||||
} else {
|
||||
destinationName = fmt.Sprintf("$%d", index+1)
|
||||
if initialTableName == "" {
|
||||
initialTableName = query.Table.Value
|
||||
}
|
||||
|
||||
initialRow = rowContext{
|
||||
parameters: query.Parameters,
|
||||
tables: map[string]RowType{
|
||||
initialTableName: doc,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
newRow[destinationName] = c.getFieldValue(column, row)
|
||||
if query.Table.SelectItem.Path != nil || query.Table.SelectItem.Type == parsers.SelectItemTypeSubQuery {
|
||||
destinationTableName := query.Table.SelectItem.Alias
|
||||
if destinationTableName == "" {
|
||||
destinationTableName = query.Table.Value
|
||||
}
|
||||
|
||||
return newRow
|
||||
selectValue := initialRow.parseArray(query.Table.SelectItem)
|
||||
rowContexts := make([]rowContext, len(selectValue))
|
||||
for i, newRowData := range selectValue {
|
||||
rowContexts[i].parameters = initialRow.parameters
|
||||
rowContexts[i].tables = copyMap(initialRow.tables)
|
||||
rowContexts[i].tables[destinationTableName] = newRowData
|
||||
}
|
||||
return rowContexts
|
||||
}
|
||||
|
||||
return []rowContext{initialRow}
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) evaluateFilters(expr ExpressionType, row RowWithJoins) bool {
|
||||
if expr == nil {
|
||||
func (r rowContext) handleJoin(query parsers.SelectStmt) []rowContext {
|
||||
currentDocuments := []rowContext{r}
|
||||
|
||||
for _, joinItem := range query.JoinItems {
|
||||
nextDocuments := make([]rowContext, 0)
|
||||
for _, currentDocument := range currentDocuments {
|
||||
joinedItems := currentDocument.resolveJoinItemSelect(joinItem.SelectItem)
|
||||
for _, joinedItem := range joinedItems {
|
||||
tablesCopy := copyMap(currentDocument.tables)
|
||||
tablesCopy[joinItem.Table.Value] = joinedItem
|
||||
nextDocuments = append(nextDocuments, rowContext{
|
||||
parameters: currentDocument.parameters,
|
||||
tables: tablesCopy,
|
||||
})
|
||||
}
|
||||
}
|
||||
currentDocuments = nextDocuments
|
||||
}
|
||||
|
||||
return currentDocuments
|
||||
}
|
||||
|
||||
func (r rowContext) resolveJoinItemSelect(selectItem parsers.SelectItem) []RowType {
|
||||
if selectItem.Path != nil || selectItem.Type == parsers.SelectItemTypeSubQuery {
|
||||
selectValue := r.parseArray(selectItem)
|
||||
documents := make([]RowType, len(selectValue))
|
||||
for i, newRowData := range selectValue {
|
||||
documents[i] = newRowData
|
||||
}
|
||||
return documents
|
||||
}
|
||||
|
||||
return []RowType{}
|
||||
}
|
||||
|
||||
func (r rowContext) applyFilters(filters interface{}) bool {
|
||||
if filters == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
switch typedValue := expr.(type) {
|
||||
switch typedFilters := filters.(type) {
|
||||
case parsers.ComparisonExpression:
|
||||
leftValue := c.getExpressionParameterValue(typedValue.Left, row)
|
||||
rightValue := c.getExpressionParameterValue(typedValue.Right, row)
|
||||
return r.filters_ComparisonExpression(typedFilters)
|
||||
case parsers.LogicalExpression:
|
||||
return r.filters_LogicalExpression(typedFilters)
|
||||
case parsers.Constant:
|
||||
if value, ok := typedFilters.Value.(bool); ok {
|
||||
return value
|
||||
}
|
||||
return false
|
||||
case parsers.SelectItem:
|
||||
resolvedValue := r.resolveSelectItem(typedFilters)
|
||||
if value, ok := resolvedValue.(bool); ok {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (r rowContext) filters_ComparisonExpression(expression parsers.ComparisonExpression) bool {
|
||||
leftExpression, leftExpressionOk := expression.Left.(parsers.SelectItem)
|
||||
rightExpression, rightExpressionOk := expression.Right.(parsers.SelectItem)
|
||||
|
||||
if !leftExpressionOk || !rightExpressionOk {
|
||||
logger.Error("ComparisonExpression has incorrect Left or Right type")
|
||||
return false
|
||||
}
|
||||
|
||||
leftValue := r.resolveSelectItem(leftExpression)
|
||||
rightValue := r.resolveSelectItem(rightExpression)
|
||||
|
||||
cmp := compareValues(leftValue, rightValue)
|
||||
switch typedValue.Operation {
|
||||
switch expression.Operation {
|
||||
case "=":
|
||||
return cmp == 0
|
||||
case "!=":
|
||||
@@ -157,15 +194,19 @@ func (c memoryExecutorContext) evaluateFilters(expr ExpressionType, row RowWithJ
|
||||
case ">=":
|
||||
return cmp >= 0
|
||||
}
|
||||
case parsers.LogicalExpression:
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (r rowContext) filters_LogicalExpression(expression parsers.LogicalExpression) bool {
|
||||
var result bool
|
||||
for i, expression := range typedValue.Expressions {
|
||||
expressionResult := c.evaluateFilters(expression, row)
|
||||
for i, subExpression := range expression.Expressions {
|
||||
expressionResult := r.applyFilters(subExpression)
|
||||
if i == 0 {
|
||||
result = expressionResult
|
||||
}
|
||||
|
||||
switch typedValue.Operation {
|
||||
switch expression.Operation {
|
||||
case parsers.LogicalExpressionTypeAnd:
|
||||
result = result && expressionResult
|
||||
if !result {
|
||||
@@ -179,242 +220,363 @@ func (c memoryExecutorContext) evaluateFilters(expr ExpressionType, row RowWithJ
|
||||
}
|
||||
}
|
||||
return result
|
||||
case parsers.Constant:
|
||||
if value, ok := typedValue.Value.(bool); ok {
|
||||
return value
|
||||
}
|
||||
return false
|
||||
case parsers.SelectItem:
|
||||
resolvedValue := c.getFieldValue(typedValue, row)
|
||||
if value, ok := resolvedValue.(bool); ok {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) getFieldValue(field parsers.SelectItem, row interface{}) interface{} {
|
||||
if field.Type == parsers.SelectItemTypeArray {
|
||||
func applyOrder(documents []rowContext, orderExpressions []parsers.OrderExpression) {
|
||||
less := func(i, j int) bool {
|
||||
for _, order := range orderExpressions {
|
||||
val1 := documents[i].resolveSelectItem(order.SelectItem)
|
||||
val2 := documents[j].resolveSelectItem(order.SelectItem)
|
||||
|
||||
cmp := compareValues(val1, val2)
|
||||
if cmp != 0 {
|
||||
if order.Direction == parsers.OrderDirectionDesc {
|
||||
return cmp > 0
|
||||
}
|
||||
return cmp < 0
|
||||
}
|
||||
}
|
||||
return i < j
|
||||
}
|
||||
|
||||
sort.SliceStable(documents, less)
|
||||
}
|
||||
|
||||
func applyGroupBy(documents []rowContext, groupBy []parsers.SelectItem) []rowContext {
|
||||
groupedRows := make(map[string][]rowContext)
|
||||
groupedKeys := make([]string, 0)
|
||||
|
||||
for _, row := range documents {
|
||||
key := row.generateGroupByKey(groupBy)
|
||||
if _, ok := groupedRows[key]; !ok {
|
||||
groupedKeys = append(groupedKeys, key)
|
||||
}
|
||||
groupedRows[key] = append(groupedRows[key], row)
|
||||
}
|
||||
|
||||
grouppedRows := make([]rowContext, 0)
|
||||
for _, key := range groupedKeys {
|
||||
grouppedRowContext := rowContext{
|
||||
tables: groupedRows[key][0].tables,
|
||||
parameters: groupedRows[key][0].parameters,
|
||||
grouppedRows: groupedRows[key],
|
||||
}
|
||||
grouppedRows = append(grouppedRows, grouppedRowContext)
|
||||
}
|
||||
|
||||
return grouppedRows
|
||||
}
|
||||
|
||||
func (r rowContext) generateGroupByKey(groupBy []parsers.SelectItem) string {
|
||||
var keyBuilder strings.Builder
|
||||
for _, selectItem := range groupBy {
|
||||
value := r.resolveSelectItem(selectItem)
|
||||
keyBuilder.WriteString(fmt.Sprintf("%v", value))
|
||||
keyBuilder.WriteString(":")
|
||||
}
|
||||
return keyBuilder.String()
|
||||
}
|
||||
|
||||
func applyProjection(documents []rowContext, selectItems []parsers.SelectItem, groupBy []parsers.SelectItem) []RowType {
|
||||
if len(documents) == 0 {
|
||||
return []RowType{}
|
||||
}
|
||||
|
||||
if hasAggregateFunctions(selectItems) && len(groupBy) == 0 {
|
||||
// When can have aggregate functions without GROUP BY clause,
|
||||
// we should aggregate all rows in that case
|
||||
rowContext := rowContext{
|
||||
tables: documents[0].tables,
|
||||
parameters: documents[0].parameters,
|
||||
grouppedRows: documents,
|
||||
}
|
||||
return []RowType{rowContext.applyProjection(selectItems)}
|
||||
}
|
||||
|
||||
projectedDocuments := make([]RowType, len(documents))
|
||||
for index, row := range documents {
|
||||
projectedDocuments[index] = row.applyProjection(selectItems)
|
||||
}
|
||||
|
||||
return projectedDocuments
|
||||
}
|
||||
|
||||
func (r rowContext) applyProjection(selectItems []parsers.SelectItem) RowType {
|
||||
// When the first value is top level, select it instead
|
||||
if len(selectItems) > 0 && selectItems[0].IsTopLevel {
|
||||
return r.resolveSelectItem(selectItems[0])
|
||||
}
|
||||
|
||||
// Construct a new row based on the selected columns
|
||||
row := make(map[string]interface{})
|
||||
for index, selectItem := range selectItems {
|
||||
destinationName := selectItem.Alias
|
||||
if destinationName == "" {
|
||||
if len(selectItem.Path) > 0 {
|
||||
destinationName = selectItem.Path[len(selectItem.Path)-1]
|
||||
} else {
|
||||
destinationName = fmt.Sprintf("$%d", index+1)
|
||||
}
|
||||
}
|
||||
|
||||
row[destinationName] = r.resolveSelectItem(selectItem)
|
||||
}
|
||||
|
||||
return row
|
||||
}
|
||||
|
||||
func (r rowContext) resolveSelectItem(selectItem parsers.SelectItem) interface{} {
|
||||
if selectItem.Type == parsers.SelectItemTypeArray {
|
||||
return r.selectItem_SelectItemTypeArray(selectItem)
|
||||
}
|
||||
|
||||
if selectItem.Type == parsers.SelectItemTypeObject {
|
||||
return r.selectItem_SelectItemTypeObject(selectItem)
|
||||
}
|
||||
|
||||
if selectItem.Type == parsers.SelectItemTypeConstant {
|
||||
return r.selectItem_SelectItemTypeConstant(selectItem)
|
||||
}
|
||||
|
||||
if selectItem.Type == parsers.SelectItemTypeSubQuery {
|
||||
return r.selectItem_SelectItemTypeSubQuery(selectItem)
|
||||
}
|
||||
|
||||
if selectItem.Type == parsers.SelectItemTypeFunctionCall {
|
||||
if typedFunctionCall, ok := selectItem.Value.(parsers.FunctionCall); ok {
|
||||
return r.selectItem_SelectItemTypeFunctionCall(typedFunctionCall)
|
||||
}
|
||||
|
||||
logger.Error("parsers.SelectItem has incorrect Value type (expected parsers.FunctionCall)")
|
||||
return nil
|
||||
}
|
||||
|
||||
return r.selectItem_SelectItemTypeField(selectItem)
|
||||
}
|
||||
|
||||
func (r rowContext) selectItem_SelectItemTypeArray(selectItem parsers.SelectItem) interface{} {
|
||||
arrayValue := make([]interface{}, 0)
|
||||
for _, selectItem := range field.SelectItems {
|
||||
arrayValue = append(arrayValue, c.getFieldValue(selectItem, row))
|
||||
for _, subSelectItem := range selectItem.SelectItems {
|
||||
arrayValue = append(arrayValue, r.resolveSelectItem(subSelectItem))
|
||||
}
|
||||
return arrayValue
|
||||
}
|
||||
}
|
||||
|
||||
if field.Type == parsers.SelectItemTypeObject {
|
||||
func (r rowContext) selectItem_SelectItemTypeObject(selectItem parsers.SelectItem) interface{} {
|
||||
objectValue := make(map[string]interface{})
|
||||
for _, selectItem := range field.SelectItems {
|
||||
objectValue[selectItem.Alias] = c.getFieldValue(selectItem, row)
|
||||
for _, subSelectItem := range selectItem.SelectItems {
|
||||
objectValue[subSelectItem.Alias] = r.resolveSelectItem(subSelectItem)
|
||||
}
|
||||
return objectValue
|
||||
}
|
||||
}
|
||||
|
||||
if field.Type == parsers.SelectItemTypeConstant {
|
||||
func (r rowContext) selectItem_SelectItemTypeConstant(selectItem parsers.SelectItem) interface{} {
|
||||
var typedValue parsers.Constant
|
||||
var ok bool
|
||||
if typedValue, ok = field.Value.(parsers.Constant); !ok {
|
||||
if typedValue, ok = selectItem.Value.(parsers.Constant); !ok {
|
||||
// TODO: Handle error
|
||||
logger.Error("parsers.Constant has incorrect Value type")
|
||||
}
|
||||
|
||||
if typedValue.Type == parsers.ConstantTypeParameterConstant &&
|
||||
c.parameters != nil {
|
||||
r.parameters != nil {
|
||||
if key, ok := typedValue.Value.(string); ok {
|
||||
return c.parameters[key]
|
||||
return r.parameters[key]
|
||||
}
|
||||
}
|
||||
|
||||
return typedValue.Value
|
||||
}
|
||||
|
||||
func (r rowContext) selectItem_SelectItemTypeSubQuery(selectItem parsers.SelectItem) interface{} {
|
||||
subQuery := selectItem.Value.(parsers.SelectStmt)
|
||||
subQueryResult := ExecuteQuery(
|
||||
subQuery,
|
||||
[]RowType{r},
|
||||
)
|
||||
|
||||
if subQuery.Exists {
|
||||
return len(subQueryResult) > 0
|
||||
}
|
||||
|
||||
rowValue := row
|
||||
// Used for aggregates
|
||||
if array, isArray := row.([]RowWithJoins); isArray {
|
||||
rowValue = array[0]
|
||||
}
|
||||
return subQueryResult
|
||||
}
|
||||
|
||||
if field.Type == parsers.SelectItemTypeFunctionCall {
|
||||
var typedValue parsers.FunctionCall
|
||||
var ok bool
|
||||
if typedValue, ok = field.Value.(parsers.FunctionCall); !ok {
|
||||
// TODO: Handle error
|
||||
logger.Error("parsers.Constant has incorrect Value type")
|
||||
}
|
||||
|
||||
switch typedValue.Type {
|
||||
func (r rowContext) selectItem_SelectItemTypeFunctionCall(functionCall parsers.FunctionCall) interface{} {
|
||||
switch functionCall.Type {
|
||||
case parsers.FunctionCallStringEquals:
|
||||
return c.strings_StringEquals(typedValue.Arguments, rowValue)
|
||||
return r.strings_StringEquals(functionCall.Arguments)
|
||||
case parsers.FunctionCallContains:
|
||||
return c.strings_Contains(typedValue.Arguments, rowValue)
|
||||
return r.strings_Contains(functionCall.Arguments)
|
||||
case parsers.FunctionCallEndsWith:
|
||||
return c.strings_EndsWith(typedValue.Arguments, rowValue)
|
||||
return r.strings_EndsWith(functionCall.Arguments)
|
||||
case parsers.FunctionCallStartsWith:
|
||||
return c.strings_StartsWith(typedValue.Arguments, rowValue)
|
||||
return r.strings_StartsWith(functionCall.Arguments)
|
||||
case parsers.FunctionCallConcat:
|
||||
return c.strings_Concat(typedValue.Arguments, rowValue)
|
||||
return r.strings_Concat(functionCall.Arguments)
|
||||
case parsers.FunctionCallIndexOf:
|
||||
return c.strings_IndexOf(typedValue.Arguments, rowValue)
|
||||
return r.strings_IndexOf(functionCall.Arguments)
|
||||
case parsers.FunctionCallToString:
|
||||
return c.strings_ToString(typedValue.Arguments, rowValue)
|
||||
return r.strings_ToString(functionCall.Arguments)
|
||||
case parsers.FunctionCallUpper:
|
||||
return c.strings_Upper(typedValue.Arguments, rowValue)
|
||||
return r.strings_Upper(functionCall.Arguments)
|
||||
case parsers.FunctionCallLower:
|
||||
return c.strings_Lower(typedValue.Arguments, rowValue)
|
||||
return r.strings_Lower(functionCall.Arguments)
|
||||
case parsers.FunctionCallLeft:
|
||||
return c.strings_Left(typedValue.Arguments, rowValue)
|
||||
return r.strings_Left(functionCall.Arguments)
|
||||
case parsers.FunctionCallLength:
|
||||
return c.strings_Length(typedValue.Arguments, rowValue)
|
||||
return r.strings_Length(functionCall.Arguments)
|
||||
case parsers.FunctionCallLTrim:
|
||||
return c.strings_LTrim(typedValue.Arguments, rowValue)
|
||||
return r.strings_LTrim(functionCall.Arguments)
|
||||
case parsers.FunctionCallReplace:
|
||||
return c.strings_Replace(typedValue.Arguments, rowValue)
|
||||
return r.strings_Replace(functionCall.Arguments)
|
||||
case parsers.FunctionCallReplicate:
|
||||
return c.strings_Replicate(typedValue.Arguments, rowValue)
|
||||
return r.strings_Replicate(functionCall.Arguments)
|
||||
case parsers.FunctionCallReverse:
|
||||
return c.strings_Reverse(typedValue.Arguments, rowValue)
|
||||
return r.strings_Reverse(functionCall.Arguments)
|
||||
case parsers.FunctionCallRight:
|
||||
return c.strings_Right(typedValue.Arguments, rowValue)
|
||||
return r.strings_Right(functionCall.Arguments)
|
||||
case parsers.FunctionCallRTrim:
|
||||
return c.strings_RTrim(typedValue.Arguments, rowValue)
|
||||
return r.strings_RTrim(functionCall.Arguments)
|
||||
case parsers.FunctionCallSubstring:
|
||||
return c.strings_Substring(typedValue.Arguments, rowValue)
|
||||
return r.strings_Substring(functionCall.Arguments)
|
||||
case parsers.FunctionCallTrim:
|
||||
return c.strings_Trim(typedValue.Arguments, rowValue)
|
||||
return r.strings_Trim(functionCall.Arguments)
|
||||
|
||||
case parsers.FunctionCallIsDefined:
|
||||
return c.typeChecking_IsDefined(typedValue.Arguments, rowValue)
|
||||
return r.typeChecking_IsDefined(functionCall.Arguments)
|
||||
case parsers.FunctionCallIsArray:
|
||||
return c.typeChecking_IsArray(typedValue.Arguments, rowValue)
|
||||
return r.typeChecking_IsArray(functionCall.Arguments)
|
||||
case parsers.FunctionCallIsBool:
|
||||
return c.typeChecking_IsBool(typedValue.Arguments, rowValue)
|
||||
return r.typeChecking_IsBool(functionCall.Arguments)
|
||||
case parsers.FunctionCallIsFiniteNumber:
|
||||
return c.typeChecking_IsFiniteNumber(typedValue.Arguments, rowValue)
|
||||
return r.typeChecking_IsFiniteNumber(functionCall.Arguments)
|
||||
case parsers.FunctionCallIsInteger:
|
||||
return c.typeChecking_IsInteger(typedValue.Arguments, rowValue)
|
||||
return r.typeChecking_IsInteger(functionCall.Arguments)
|
||||
case parsers.FunctionCallIsNull:
|
||||
return c.typeChecking_IsNull(typedValue.Arguments, rowValue)
|
||||
return r.typeChecking_IsNull(functionCall.Arguments)
|
||||
case parsers.FunctionCallIsNumber:
|
||||
return c.typeChecking_IsNumber(typedValue.Arguments, rowValue)
|
||||
return r.typeChecking_IsNumber(functionCall.Arguments)
|
||||
case parsers.FunctionCallIsObject:
|
||||
return c.typeChecking_IsObject(typedValue.Arguments, rowValue)
|
||||
return r.typeChecking_IsObject(functionCall.Arguments)
|
||||
case parsers.FunctionCallIsPrimitive:
|
||||
return c.typeChecking_IsPrimitive(typedValue.Arguments, rowValue)
|
||||
return r.typeChecking_IsPrimitive(functionCall.Arguments)
|
||||
case parsers.FunctionCallIsString:
|
||||
return c.typeChecking_IsString(typedValue.Arguments, rowValue)
|
||||
return r.typeChecking_IsString(functionCall.Arguments)
|
||||
|
||||
case parsers.FunctionCallArrayConcat:
|
||||
return c.array_Concat(typedValue.Arguments, rowValue)
|
||||
return r.array_Concat(functionCall.Arguments)
|
||||
case parsers.FunctionCallArrayLength:
|
||||
return c.array_Length(typedValue.Arguments, rowValue)
|
||||
return r.array_Length(functionCall.Arguments)
|
||||
case parsers.FunctionCallArraySlice:
|
||||
return c.array_Slice(typedValue.Arguments, rowValue)
|
||||
return r.array_Slice(functionCall.Arguments)
|
||||
case parsers.FunctionCallSetIntersect:
|
||||
return c.set_Intersect(typedValue.Arguments, rowValue)
|
||||
return r.set_Intersect(functionCall.Arguments)
|
||||
case parsers.FunctionCallSetUnion:
|
||||
return c.set_Union(typedValue.Arguments, rowValue)
|
||||
return r.set_Union(functionCall.Arguments)
|
||||
|
||||
case parsers.FunctionCallMathAbs:
|
||||
return c.math_Abs(typedValue.Arguments, rowValue)
|
||||
return r.math_Abs(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathAcos:
|
||||
return c.math_Acos(typedValue.Arguments, rowValue)
|
||||
return r.math_Acos(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathAsin:
|
||||
return c.math_Asin(typedValue.Arguments, rowValue)
|
||||
return r.math_Asin(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathAtan:
|
||||
return c.math_Atan(typedValue.Arguments, rowValue)
|
||||
return r.math_Atan(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathCeiling:
|
||||
return c.math_Ceiling(typedValue.Arguments, rowValue)
|
||||
return r.math_Ceiling(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathCos:
|
||||
return c.math_Cos(typedValue.Arguments, rowValue)
|
||||
return r.math_Cos(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathCot:
|
||||
return c.math_Cot(typedValue.Arguments, rowValue)
|
||||
return r.math_Cot(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathDegrees:
|
||||
return c.math_Degrees(typedValue.Arguments, rowValue)
|
||||
return r.math_Degrees(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathExp:
|
||||
return c.math_Exp(typedValue.Arguments, rowValue)
|
||||
return r.math_Exp(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathFloor:
|
||||
return c.math_Floor(typedValue.Arguments, rowValue)
|
||||
return r.math_Floor(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathIntBitNot:
|
||||
return c.math_IntBitNot(typedValue.Arguments, rowValue)
|
||||
return r.math_IntBitNot(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathLog10:
|
||||
return c.math_Log10(typedValue.Arguments, rowValue)
|
||||
return r.math_Log10(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathRadians:
|
||||
return c.math_Radians(typedValue.Arguments, rowValue)
|
||||
return r.math_Radians(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathRound:
|
||||
return c.math_Round(typedValue.Arguments, rowValue)
|
||||
return r.math_Round(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathSign:
|
||||
return c.math_Sign(typedValue.Arguments, rowValue)
|
||||
return r.math_Sign(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathSin:
|
||||
return c.math_Sin(typedValue.Arguments, rowValue)
|
||||
return r.math_Sin(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathSqrt:
|
||||
return c.math_Sqrt(typedValue.Arguments, rowValue)
|
||||
return r.math_Sqrt(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathSquare:
|
||||
return c.math_Square(typedValue.Arguments, rowValue)
|
||||
return r.math_Square(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathTan:
|
||||
return c.math_Tan(typedValue.Arguments, rowValue)
|
||||
return r.math_Tan(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathTrunc:
|
||||
return c.math_Trunc(typedValue.Arguments, rowValue)
|
||||
return r.math_Trunc(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathAtn2:
|
||||
return c.math_Atn2(typedValue.Arguments, rowValue)
|
||||
return r.math_Atn2(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathIntAdd:
|
||||
return c.math_IntAdd(typedValue.Arguments, rowValue)
|
||||
return r.math_IntAdd(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathIntBitAnd:
|
||||
return c.math_IntBitAnd(typedValue.Arguments, rowValue)
|
||||
return r.math_IntBitAnd(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathIntBitLeftShift:
|
||||
return c.math_IntBitLeftShift(typedValue.Arguments, rowValue)
|
||||
return r.math_IntBitLeftShift(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathIntBitOr:
|
||||
return c.math_IntBitOr(typedValue.Arguments, rowValue)
|
||||
return r.math_IntBitOr(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathIntBitRightShift:
|
||||
return c.math_IntBitRightShift(typedValue.Arguments, rowValue)
|
||||
return r.math_IntBitRightShift(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathIntBitXor:
|
||||
return c.math_IntBitXor(typedValue.Arguments, rowValue)
|
||||
return r.math_IntBitXor(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathIntDiv:
|
||||
return c.math_IntDiv(typedValue.Arguments, rowValue)
|
||||
return r.math_IntDiv(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathIntMod:
|
||||
return c.math_IntMod(typedValue.Arguments, rowValue)
|
||||
return r.math_IntMod(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathIntMul:
|
||||
return c.math_IntMul(typedValue.Arguments, rowValue)
|
||||
return r.math_IntMul(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathIntSub:
|
||||
return c.math_IntSub(typedValue.Arguments, rowValue)
|
||||
return r.math_IntSub(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathPower:
|
||||
return c.math_Power(typedValue.Arguments, rowValue)
|
||||
return r.math_Power(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathLog:
|
||||
return c.math_Log(typedValue.Arguments, rowValue)
|
||||
return r.math_Log(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathNumberBin:
|
||||
return c.math_NumberBin(typedValue.Arguments, rowValue)
|
||||
return r.math_NumberBin(functionCall.Arguments)
|
||||
case parsers.FunctionCallMathPi:
|
||||
return c.math_Pi()
|
||||
return r.math_Pi()
|
||||
case parsers.FunctionCallMathRand:
|
||||
return c.math_Rand()
|
||||
return r.math_Rand()
|
||||
|
||||
case parsers.FunctionCallAggregateAvg:
|
||||
return c.aggregate_Avg(typedValue.Arguments, row)
|
||||
return r.aggregate_Avg(functionCall.Arguments)
|
||||
case parsers.FunctionCallAggregateCount:
|
||||
return c.aggregate_Count(typedValue.Arguments, row)
|
||||
return r.aggregate_Count(functionCall.Arguments)
|
||||
case parsers.FunctionCallAggregateMax:
|
||||
return c.aggregate_Max(typedValue.Arguments, row)
|
||||
return r.aggregate_Max(functionCall.Arguments)
|
||||
case parsers.FunctionCallAggregateMin:
|
||||
return c.aggregate_Min(typedValue.Arguments, row)
|
||||
return r.aggregate_Min(functionCall.Arguments)
|
||||
case parsers.FunctionCallAggregateSum:
|
||||
return c.aggregate_Sum(typedValue.Arguments, row)
|
||||
return r.aggregate_Sum(functionCall.Arguments)
|
||||
|
||||
case parsers.FunctionCallIn:
|
||||
return c.misc_In(typedValue.Arguments, rowValue)
|
||||
}
|
||||
return r.misc_In(functionCall.Arguments)
|
||||
}
|
||||
|
||||
value := rowValue
|
||||
if joinedRow, isRowWithJoins := value.(RowWithJoins); isRowWithJoins {
|
||||
value = joinedRow[field.Path[0]]
|
||||
}
|
||||
logger.Errorf("Unknown function call type: %v", functionCall.Type)
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(field.Path) > 1 {
|
||||
for _, pathSegment := range field.Path[1:] {
|
||||
func (r rowContext) selectItem_SelectItemTypeField(selectItem parsers.SelectItem) interface{} {
|
||||
value := r.tables[selectItem.Path[0]]
|
||||
|
||||
if len(selectItem.Path) > 1 {
|
||||
for _, pathSegment := range selectItem.Path[1:] {
|
||||
|
||||
switch nestedValue := value.(type) {
|
||||
case map[string]interface{}:
|
||||
value = nestedValue[pathSegment]
|
||||
case RowWithJoins:
|
||||
case map[string]RowType:
|
||||
value = nestedValue[pathSegment]
|
||||
case []int, []string, []interface{}:
|
||||
slice := reflect.ValueOf(nestedValue)
|
||||
@@ -428,82 +590,28 @@ func (c memoryExecutorContext) getFieldValue(field parsers.SelectItem, row inter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) getExpressionParameterValue(
|
||||
parameter interface{},
|
||||
row RowWithJoins,
|
||||
) interface{} {
|
||||
switch typedParameter := parameter.(type) {
|
||||
case parsers.SelectItem:
|
||||
return c.getFieldValue(typedParameter, row)
|
||||
func hasAggregateFunctions(selectItems []parsers.SelectItem) bool {
|
||||
if selectItems == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
logger.Error("getExpressionParameterValue - got incorrect parameter type")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) orderBy(orderBy []parsers.OrderExpression, data []RowWithJoins) {
|
||||
less := func(i, j int) bool {
|
||||
for _, order := range orderBy {
|
||||
val1 := c.getFieldValue(order.SelectItem, data[i])
|
||||
val2 := c.getFieldValue(order.SelectItem, data[j])
|
||||
|
||||
cmp := compareValues(val1, val2)
|
||||
if cmp != 0 {
|
||||
if order.Direction == parsers.OrderDirectionDesc {
|
||||
return cmp > 0
|
||||
}
|
||||
return cmp < 0
|
||||
for _, selectItem := range selectItems {
|
||||
if selectItem.Type == parsers.SelectItemTypeFunctionCall {
|
||||
if typedValue, ok := selectItem.Value.(parsers.FunctionCall); ok && slices.Contains[[]parsers.FunctionCallType](parsers.AggregateFunctions, typedValue.Type) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return i < j
|
||||
|
||||
if hasAggregateFunctions(selectItem.SelectItems) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
sort.SliceStable(data, less)
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) groupBy(selectStmt parsers.SelectStmt, data []RowWithJoins) []RowType {
|
||||
groupedRows := make(map[string][]RowWithJoins)
|
||||
groupedKeys := make([]string, 0)
|
||||
|
||||
// Group rows by group by columns
|
||||
for _, row := range data {
|
||||
key := c.generateGroupKey(selectStmt.GroupBy, row)
|
||||
if _, ok := groupedRows[key]; !ok {
|
||||
groupedKeys = append(groupedKeys, key)
|
||||
}
|
||||
groupedRows[key] = append(groupedRows[key], row)
|
||||
}
|
||||
|
||||
// Aggregate each group
|
||||
aggregatedRows := make([]RowType, 0)
|
||||
for _, key := range groupedKeys {
|
||||
groupRows := groupedRows[key]
|
||||
aggregatedRow := c.aggregateGroup(selectStmt, groupRows)
|
||||
aggregatedRows = append(aggregatedRows, aggregatedRow)
|
||||
}
|
||||
|
||||
return aggregatedRows
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) generateGroupKey(groupByFields []parsers.SelectItem, row RowWithJoins) string {
|
||||
var keyBuilder strings.Builder
|
||||
for _, column := range groupByFields {
|
||||
fieldValue := c.getFieldValue(column, row)
|
||||
keyBuilder.WriteString(fmt.Sprintf("%v", fieldValue))
|
||||
keyBuilder.WriteString(":")
|
||||
}
|
||||
|
||||
return keyBuilder.String()
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) aggregateGroup(selectStmt parsers.SelectStmt, groupRows []RowWithJoins) RowType {
|
||||
aggregatedRow := c.selectRow(selectStmt.SelectItems, groupRows)
|
||||
|
||||
return aggregatedRow
|
||||
return false
|
||||
}
|
||||
|
||||
func compareValues(val1, val2 interface{}) int {
|
||||
@@ -549,8 +657,9 @@ func compareValues(val1, val2 interface{}) int {
|
||||
}
|
||||
}
|
||||
|
||||
func deduplicate(slice []RowType) []RowType {
|
||||
var result []RowType
|
||||
func deduplicate[T RowType | interface{}](slice []T) []T {
|
||||
var result []T
|
||||
result = make([]T, 0)
|
||||
|
||||
for i := 0; i < len(slice); i++ {
|
||||
unique := true
|
||||
@@ -569,42 +678,8 @@ func deduplicate(slice []RowType) []RowType {
|
||||
return result
|
||||
}
|
||||
|
||||
func hasAggregateFunctions(selectItems []parsers.SelectItem) bool {
|
||||
if selectItems == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, selectItem := range selectItems {
|
||||
if selectItem.Type == parsers.SelectItemTypeFunctionCall {
|
||||
if typedValue, ok := selectItem.Value.(parsers.FunctionCall); ok && slices.Contains[[]parsers.FunctionCallType](parsers.AggregateFunctions, typedValue.Type) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if hasAggregateFunctions(selectItem.SelectItems) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func zipRows(current []RowWithJoins, joinedTableName string, rowsToZip []RowType) []RowWithJoins {
|
||||
resultMap := make([]RowWithJoins, 0)
|
||||
|
||||
for _, currentRow := range current {
|
||||
for _, rowToZip := range rowsToZip {
|
||||
newRow := copyMap(currentRow)
|
||||
newRow[joinedTableName] = rowToZip
|
||||
resultMap = append(resultMap, newRow)
|
||||
}
|
||||
}
|
||||
|
||||
return resultMap
|
||||
}
|
||||
|
||||
func copyMap(originalMap map[string]RowType) map[string]RowType {
|
||||
targetMap := make(map[string]RowType)
|
||||
func copyMap[T RowType | []RowType](originalMap map[string]T) map[string]T {
|
||||
targetMap := make(map[string]T)
|
||||
|
||||
for k, v := range originalMap {
|
||||
targetMap[k] = v
|
||||
|
||||
@@ -4,11 +4,11 @@ import (
|
||||
"github.com/pikami/cosmium/parsers"
|
||||
)
|
||||
|
||||
func (c memoryExecutorContext) misc_In(arguments []interface{}, row RowType) bool {
|
||||
value := c.getFieldValue(arguments[0].(parsers.SelectItem), row)
|
||||
func (r rowContext) misc_In(arguments []interface{}) bool {
|
||||
value := r.resolveSelectItem(arguments[0].(parsers.SelectItem))
|
||||
|
||||
for i := 1; i < len(arguments); i++ {
|
||||
compareValue := c.getFieldValue(arguments[i].(parsers.SelectItem), row)
|
||||
compareValue := r.resolveSelectItem(arguments[i].(parsers.SelectItem))
|
||||
if compareValues(value, compareValue) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ func testQueryExecute(
|
||||
data []memoryexecutor.RowType,
|
||||
expectedData []memoryexecutor.RowType,
|
||||
) {
|
||||
result := memoryexecutor.Execute(query, data)
|
||||
result := memoryexecutor.ExecuteQuery(query, data)
|
||||
|
||||
if !reflect.DeepEqual(result, expectedData) {
|
||||
t.Errorf("execution result does not match expected data.\nExpected: %+v\nGot: %+v", expectedData, result)
|
||||
@@ -25,8 +25,20 @@ func Test_Execute(t *testing.T) {
|
||||
mockData := []memoryexecutor.RowType{
|
||||
map[string]interface{}{"id": "12345", "pk": 123, "_self": "self1", "_rid": "rid1", "_ts": 123456, "isCool": false},
|
||||
map[string]interface{}{"id": "67890", "pk": 456, "_self": "self2", "_rid": "rid2", "_ts": 789012, "isCool": true},
|
||||
map[string]interface{}{"id": "456", "pk": 456, "_self": "self2", "_rid": "rid2", "_ts": 789012, "isCool": true},
|
||||
map[string]interface{}{"id": "123", "pk": 456, "_self": "self2", "_rid": "rid2", "_ts": 789012, "isCool": true},
|
||||
map[string]interface{}{
|
||||
"id": "456", "pk": 456, "_self": "self2", "_rid": "rid2", "_ts": 789012, "isCool": true,
|
||||
"tags": []map[string]interface{}{
|
||||
{"name": "tag-a"},
|
||||
{"name": "tag-b"},
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"id": "123", "pk": 456, "_self": "self2", "_rid": "rid2", "_ts": 789012, "isCool": true,
|
||||
"tags": []map[string]interface{}{
|
||||
{"name": "tag-b"},
|
||||
{"name": "tag-c"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("Should execute SELECT with ORDER BY", func(t *testing.T) {
|
||||
@@ -124,4 +136,31 @@ func Test_Execute(t *testing.T) {
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("Should execute IN selector", func(t *testing.T) {
|
||||
testQueryExecute(
|
||||
t,
|
||||
parsers.SelectStmt{
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{
|
||||
Path: []string{"c", "name"},
|
||||
Type: parsers.SelectItemTypeField,
|
||||
},
|
||||
},
|
||||
Table: parsers.Table{
|
||||
Value: "c",
|
||||
SelectItem: parsers.SelectItem{
|
||||
Path: []string{"c", "tags"},
|
||||
},
|
||||
},
|
||||
},
|
||||
mockData,
|
||||
[]memoryexecutor.RowType{
|
||||
map[string]interface{}{"name": "tag-a"},
|
||||
map[string]interface{}{"name": "tag-b"},
|
||||
map[string]interface{}{"name": "tag-b"},
|
||||
map[string]interface{}{"name": "tag-c"},
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -8,10 +8,10 @@ import (
|
||||
"github.com/pikami/cosmium/parsers"
|
||||
)
|
||||
|
||||
func (c memoryExecutorContext) strings_StringEquals(arguments []interface{}, row RowType) bool {
|
||||
str1 := c.parseString(arguments[0], row)
|
||||
str2 := c.parseString(arguments[1], row)
|
||||
ignoreCase := c.getBoolFlag(arguments, row)
|
||||
func (r rowContext) strings_StringEquals(arguments []interface{}) bool {
|
||||
str1 := r.parseString(arguments[0])
|
||||
str2 := r.parseString(arguments[1])
|
||||
ignoreCase := r.getBoolFlag(arguments)
|
||||
|
||||
if ignoreCase {
|
||||
return strings.EqualFold(str1, str2)
|
||||
@@ -20,10 +20,10 @@ func (c memoryExecutorContext) strings_StringEquals(arguments []interface{}, row
|
||||
return str1 == str2
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) strings_Contains(arguments []interface{}, row RowType) bool {
|
||||
str1 := c.parseString(arguments[0], row)
|
||||
str2 := c.parseString(arguments[1], row)
|
||||
ignoreCase := c.getBoolFlag(arguments, row)
|
||||
func (r rowContext) strings_Contains(arguments []interface{}) bool {
|
||||
str1 := r.parseString(arguments[0])
|
||||
str2 := r.parseString(arguments[1])
|
||||
ignoreCase := r.getBoolFlag(arguments)
|
||||
|
||||
if ignoreCase {
|
||||
str1 = strings.ToLower(str1)
|
||||
@@ -33,10 +33,10 @@ func (c memoryExecutorContext) strings_Contains(arguments []interface{}, row Row
|
||||
return strings.Contains(str1, str2)
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) strings_EndsWith(arguments []interface{}, row RowType) bool {
|
||||
str1 := c.parseString(arguments[0], row)
|
||||
str2 := c.parseString(arguments[1], row)
|
||||
ignoreCase := c.getBoolFlag(arguments, row)
|
||||
func (r rowContext) strings_EndsWith(arguments []interface{}) bool {
|
||||
str1 := r.parseString(arguments[0])
|
||||
str2 := r.parseString(arguments[1])
|
||||
ignoreCase := r.getBoolFlag(arguments)
|
||||
|
||||
if ignoreCase {
|
||||
str1 = strings.ToLower(str1)
|
||||
@@ -46,10 +46,10 @@ func (c memoryExecutorContext) strings_EndsWith(arguments []interface{}, row Row
|
||||
return strings.HasSuffix(str1, str2)
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) strings_StartsWith(arguments []interface{}, row RowType) bool {
|
||||
str1 := c.parseString(arguments[0], row)
|
||||
str2 := c.parseString(arguments[1], row)
|
||||
ignoreCase := c.getBoolFlag(arguments, row)
|
||||
func (r rowContext) strings_StartsWith(arguments []interface{}) bool {
|
||||
str1 := r.parseString(arguments[0])
|
||||
str2 := r.parseString(arguments[1])
|
||||
ignoreCase := r.getBoolFlag(arguments)
|
||||
|
||||
if ignoreCase {
|
||||
str1 = strings.ToLower(str1)
|
||||
@@ -59,12 +59,12 @@ func (c memoryExecutorContext) strings_StartsWith(arguments []interface{}, row R
|
||||
return strings.HasPrefix(str1, str2)
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) strings_Concat(arguments []interface{}, row RowType) string {
|
||||
func (r rowContext) strings_Concat(arguments []interface{}) string {
|
||||
result := ""
|
||||
|
||||
for _, arg := range arguments {
|
||||
if selectItem, ok := arg.(parsers.SelectItem); ok {
|
||||
value := c.getFieldValue(selectItem, row)
|
||||
value := r.resolveSelectItem(selectItem)
|
||||
result += convertToString(value)
|
||||
}
|
||||
}
|
||||
@@ -72,13 +72,13 @@ func (c memoryExecutorContext) strings_Concat(arguments []interface{}, row RowTy
|
||||
return result
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) strings_IndexOf(arguments []interface{}, row RowType) int {
|
||||
str1 := c.parseString(arguments[0], row)
|
||||
str2 := c.parseString(arguments[1], row)
|
||||
func (r rowContext) strings_IndexOf(arguments []interface{}) int {
|
||||
str1 := r.parseString(arguments[0])
|
||||
str2 := r.parseString(arguments[1])
|
||||
|
||||
start := 0
|
||||
if len(arguments) > 2 && arguments[2] != nil {
|
||||
if startPos, ok := c.getFieldValue(arguments[2].(parsers.SelectItem), row).(int); ok {
|
||||
if startPos, ok := r.resolveSelectItem(arguments[2].(parsers.SelectItem)).(int); ok {
|
||||
start = startPos
|
||||
}
|
||||
}
|
||||
@@ -97,26 +97,26 @@ func (c memoryExecutorContext) strings_IndexOf(arguments []interface{}, row RowT
|
||||
}
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) strings_ToString(arguments []interface{}, row RowType) string {
|
||||
value := c.getFieldValue(arguments[0].(parsers.SelectItem), row)
|
||||
func (r rowContext) strings_ToString(arguments []interface{}) string {
|
||||
value := r.resolveSelectItem(arguments[0].(parsers.SelectItem))
|
||||
return convertToString(value)
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) strings_Upper(arguments []interface{}, row RowType) string {
|
||||
value := c.getFieldValue(arguments[0].(parsers.SelectItem), row)
|
||||
func (r rowContext) strings_Upper(arguments []interface{}) string {
|
||||
value := r.resolveSelectItem(arguments[0].(parsers.SelectItem))
|
||||
return strings.ToUpper(convertToString(value))
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) strings_Lower(arguments []interface{}, row RowType) string {
|
||||
value := c.getFieldValue(arguments[0].(parsers.SelectItem), row)
|
||||
func (r rowContext) strings_Lower(arguments []interface{}) string {
|
||||
value := r.resolveSelectItem(arguments[0].(parsers.SelectItem))
|
||||
return strings.ToLower(convertToString(value))
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) strings_Left(arguments []interface{}, row RowType) string {
|
||||
func (r rowContext) strings_Left(arguments []interface{}) string {
|
||||
var ok bool
|
||||
var length int
|
||||
str := c.parseString(arguments[0], row)
|
||||
lengthEx := c.getFieldValue(arguments[1].(parsers.SelectItem), row)
|
||||
str := r.parseString(arguments[0])
|
||||
lengthEx := r.resolveSelectItem(arguments[1].(parsers.SelectItem))
|
||||
|
||||
if length, ok = lengthEx.(int); !ok {
|
||||
logger.Error("strings_Left - got parameters of wrong type")
|
||||
@@ -134,28 +134,28 @@ func (c memoryExecutorContext) strings_Left(arguments []interface{}, row RowType
|
||||
return str[:length]
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) strings_Length(arguments []interface{}, row RowType) int {
|
||||
str := c.parseString(arguments[0], row)
|
||||
func (r rowContext) strings_Length(arguments []interface{}) int {
|
||||
str := r.parseString(arguments[0])
|
||||
return len(str)
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) strings_LTrim(arguments []interface{}, row RowType) string {
|
||||
str := c.parseString(arguments[0], row)
|
||||
func (r rowContext) strings_LTrim(arguments []interface{}) string {
|
||||
str := r.parseString(arguments[0])
|
||||
return strings.TrimLeft(str, " ")
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) strings_Replace(arguments []interface{}, row RowType) string {
|
||||
str := c.parseString(arguments[0], row)
|
||||
oldStr := c.parseString(arguments[1], row)
|
||||
newStr := c.parseString(arguments[2], row)
|
||||
func (r rowContext) strings_Replace(arguments []interface{}) string {
|
||||
str := r.parseString(arguments[0])
|
||||
oldStr := r.parseString(arguments[1])
|
||||
newStr := r.parseString(arguments[2])
|
||||
return strings.Replace(str, oldStr, newStr, -1)
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) strings_Replicate(arguments []interface{}, row RowType) string {
|
||||
func (r rowContext) strings_Replicate(arguments []interface{}) string {
|
||||
var ok bool
|
||||
var times int
|
||||
str := c.parseString(arguments[0], row)
|
||||
timesEx := c.getFieldValue(arguments[1].(parsers.SelectItem), row)
|
||||
str := r.parseString(arguments[0])
|
||||
timesEx := r.resolveSelectItem(arguments[1].(parsers.SelectItem))
|
||||
|
||||
if times, ok = timesEx.(int); !ok {
|
||||
logger.Error("strings_Replicate - got parameters of wrong type")
|
||||
@@ -173,8 +173,8 @@ func (c memoryExecutorContext) strings_Replicate(arguments []interface{}, row Ro
|
||||
return strings.Repeat(str, times)
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) strings_Reverse(arguments []interface{}, row RowType) string {
|
||||
str := c.parseString(arguments[0], row)
|
||||
func (r rowContext) strings_Reverse(arguments []interface{}) string {
|
||||
str := r.parseString(arguments[0])
|
||||
runes := []rune(str)
|
||||
|
||||
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
|
||||
@@ -184,11 +184,11 @@ func (c memoryExecutorContext) strings_Reverse(arguments []interface{}, row RowT
|
||||
return string(runes)
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) strings_Right(arguments []interface{}, row RowType) string {
|
||||
func (r rowContext) strings_Right(arguments []interface{}) string {
|
||||
var ok bool
|
||||
var length int
|
||||
str := c.parseString(arguments[0], row)
|
||||
lengthEx := c.getFieldValue(arguments[1].(parsers.SelectItem), row)
|
||||
str := r.parseString(arguments[0])
|
||||
lengthEx := r.resolveSelectItem(arguments[1].(parsers.SelectItem))
|
||||
|
||||
if length, ok = lengthEx.(int); !ok {
|
||||
logger.Error("strings_Right - got parameters of wrong type")
|
||||
@@ -206,18 +206,18 @@ func (c memoryExecutorContext) strings_Right(arguments []interface{}, row RowTyp
|
||||
return str[len(str)-length:]
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) strings_RTrim(arguments []interface{}, row RowType) string {
|
||||
str := c.parseString(arguments[0], row)
|
||||
func (r rowContext) strings_RTrim(arguments []interface{}) string {
|
||||
str := r.parseString(arguments[0])
|
||||
return strings.TrimRight(str, " ")
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) strings_Substring(arguments []interface{}, row RowType) string {
|
||||
func (r rowContext) strings_Substring(arguments []interface{}) string {
|
||||
var ok bool
|
||||
var startPos int
|
||||
var length int
|
||||
str := c.parseString(arguments[0], row)
|
||||
startPosEx := c.getFieldValue(arguments[1].(parsers.SelectItem), row)
|
||||
lengthEx := c.getFieldValue(arguments[2].(parsers.SelectItem), row)
|
||||
str := r.parseString(arguments[0])
|
||||
startPosEx := r.resolveSelectItem(arguments[1].(parsers.SelectItem))
|
||||
lengthEx := r.resolveSelectItem(arguments[2].(parsers.SelectItem))
|
||||
|
||||
if startPos, ok = startPosEx.(int); !ok {
|
||||
logger.Error("strings_Substring - got start parameters of wrong type")
|
||||
@@ -240,16 +240,16 @@ func (c memoryExecutorContext) strings_Substring(arguments []interface{}, row Ro
|
||||
return str[startPos:endPos]
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) strings_Trim(arguments []interface{}, row RowType) string {
|
||||
str := c.parseString(arguments[0], row)
|
||||
func (r rowContext) strings_Trim(arguments []interface{}) string {
|
||||
str := r.parseString(arguments[0])
|
||||
return strings.TrimSpace(str)
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) getBoolFlag(arguments []interface{}, row RowType) bool {
|
||||
func (r rowContext) getBoolFlag(arguments []interface{}) bool {
|
||||
ignoreCase := false
|
||||
if len(arguments) > 2 && arguments[2] != nil {
|
||||
ignoreCaseItem := arguments[2].(parsers.SelectItem)
|
||||
if value, ok := c.getFieldValue(ignoreCaseItem, row).(bool); ok {
|
||||
if value, ok := r.resolveSelectItem(ignoreCaseItem).(bool); ok {
|
||||
ignoreCase = value
|
||||
}
|
||||
}
|
||||
@@ -257,9 +257,9 @@ func (c memoryExecutorContext) getBoolFlag(arguments []interface{}, row RowType)
|
||||
return ignoreCase
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) parseString(argument interface{}, row RowType) string {
|
||||
func (r rowContext) parseString(argument interface{}) string {
|
||||
exItem := argument.(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
if str1, ok := ex.(string); ok {
|
||||
return str1
|
||||
}
|
||||
|
||||
155
query_executors/memory_executor/subquery_test.go
Normal file
155
query_executors/memory_executor/subquery_test.go
Normal file
@@ -0,0 +1,155 @@
|
||||
package memoryexecutor_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/pikami/cosmium/parsers"
|
||||
memoryexecutor "github.com/pikami/cosmium/query_executors/memory_executor"
|
||||
)
|
||||
|
||||
func Test_Execute_SubQuery(t *testing.T) {
|
||||
mockData := []memoryexecutor.RowType{
|
||||
map[string]interface{}{"id": "123", "info": map[string]interface{}{"name": "row-1"}},
|
||||
map[string]interface{}{
|
||||
"id": "456",
|
||||
"info": map[string]interface{}{"name": "row-2"},
|
||||
"tags": []map[string]interface{}{
|
||||
{"name": "tag-a"},
|
||||
{"name": "tag-b"},
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"id": "789",
|
||||
"info": map[string]interface{}{"name": "row-3"},
|
||||
"tags": []map[string]interface{}{
|
||||
{"name": "tag-b"},
|
||||
{"name": "tag-c"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("Should execute FROM subquery", func(t *testing.T) {
|
||||
testQueryExecute(
|
||||
t,
|
||||
parsers.SelectStmt{
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{Path: []string{"c", "name"}},
|
||||
},
|
||||
Table: parsers.Table{
|
||||
Value: "c",
|
||||
SelectItem: parsers.SelectItem{
|
||||
Alias: "c",
|
||||
Type: parsers.SelectItemTypeSubQuery,
|
||||
Value: parsers.SelectStmt{
|
||||
Table: parsers.Table{Value: "cc"},
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{Path: []string{"cc", "info"}, IsTopLevel: true},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
mockData,
|
||||
[]memoryexecutor.RowType{
|
||||
map[string]interface{}{"name": "row-1"},
|
||||
map[string]interface{}{"name": "row-2"},
|
||||
map[string]interface{}{"name": "row-3"},
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("Should execute JOIN subquery", func(t *testing.T) {
|
||||
testQueryExecute(
|
||||
t,
|
||||
parsers.SelectStmt{
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{Path: []string{"c", "id"}},
|
||||
{Path: []string{"cc", "name"}},
|
||||
},
|
||||
Table: parsers.Table{
|
||||
Value: "c",
|
||||
},
|
||||
JoinItems: []parsers.JoinItem{
|
||||
{
|
||||
Table: parsers.Table{
|
||||
Value: "cc",
|
||||
},
|
||||
SelectItem: parsers.SelectItem{
|
||||
Alias: "cc",
|
||||
Type: parsers.SelectItemTypeSubQuery,
|
||||
Value: parsers.SelectStmt{
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{Path: []string{"tag", "name"}},
|
||||
},
|
||||
Table: parsers.Table{
|
||||
Value: "tag",
|
||||
SelectItem: parsers.SelectItem{
|
||||
Path: []string{"c", "tags"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
mockData,
|
||||
[]memoryexecutor.RowType{
|
||||
map[string]interface{}{"id": "456", "name": "tag-a"},
|
||||
map[string]interface{}{"id": "456", "name": "tag-b"},
|
||||
map[string]interface{}{"id": "789", "name": "tag-b"},
|
||||
map[string]interface{}{"id": "789", "name": "tag-c"},
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("Should execute JOIN EXISTS subquery", func(t *testing.T) {
|
||||
testQueryExecute(
|
||||
t,
|
||||
parsers.SelectStmt{
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{Path: []string{"c", "id"}},
|
||||
},
|
||||
Table: parsers.Table{
|
||||
Value: "c",
|
||||
},
|
||||
JoinItems: []parsers.JoinItem{
|
||||
{
|
||||
Table: parsers.Table{Value: "hasTags"},
|
||||
SelectItem: parsers.SelectItem{
|
||||
Alias: "hasTags",
|
||||
Type: parsers.SelectItemTypeSubQuery,
|
||||
Value: parsers.SelectStmt{
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{
|
||||
IsTopLevel: true,
|
||||
Type: parsers.SelectItemTypeSubQuery,
|
||||
Value: parsers.SelectStmt{
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{Path: []string{"tag", "name"}},
|
||||
},
|
||||
Table: parsers.Table{
|
||||
Value: "tag",
|
||||
SelectItem: parsers.SelectItem{
|
||||
Path: []string{"c", "tags"},
|
||||
},
|
||||
},
|
||||
Exists: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Filters: parsers.SelectItem{
|
||||
Path: []string{"hasTags"},
|
||||
},
|
||||
},
|
||||
mockData,
|
||||
[]memoryexecutor.RowType{
|
||||
map[string]interface{}{"id": "456"},
|
||||
map[string]interface{}{"id": "789"},
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -6,32 +6,32 @@ import (
|
||||
"github.com/pikami/cosmium/parsers"
|
||||
)
|
||||
|
||||
func (c memoryExecutorContext) typeChecking_IsDefined(arguments []interface{}, row RowType) bool {
|
||||
func (r rowContext) typeChecking_IsDefined(arguments []interface{}) bool {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
return ex != nil
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) typeChecking_IsArray(arguments []interface{}, row RowType) bool {
|
||||
func (r rowContext) typeChecking_IsArray(arguments []interface{}) bool {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
_, isArray := ex.([]interface{})
|
||||
return isArray
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) typeChecking_IsBool(arguments []interface{}, row RowType) bool {
|
||||
func (r rowContext) typeChecking_IsBool(arguments []interface{}) bool {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
_, isBool := ex.(bool)
|
||||
return isBool
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) typeChecking_IsFiniteNumber(arguments []interface{}, row RowType) bool {
|
||||
func (r rowContext) typeChecking_IsFiniteNumber(arguments []interface{}) bool {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
switch num := ex.(type) {
|
||||
case int:
|
||||
@@ -43,41 +43,41 @@ func (c memoryExecutorContext) typeChecking_IsFiniteNumber(arguments []interface
|
||||
}
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) typeChecking_IsInteger(arguments []interface{}, row RowType) bool {
|
||||
func (r rowContext) typeChecking_IsInteger(arguments []interface{}) bool {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
_, isInt := ex.(int)
|
||||
return isInt
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) typeChecking_IsNull(arguments []interface{}, row RowType) bool {
|
||||
func (r rowContext) typeChecking_IsNull(arguments []interface{}) bool {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
return ex == nil
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) typeChecking_IsNumber(arguments []interface{}, row RowType) bool {
|
||||
func (r rowContext) typeChecking_IsNumber(arguments []interface{}) bool {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
_, isFloat := ex.(float64)
|
||||
_, isInt := ex.(int)
|
||||
return isFloat || isInt
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) typeChecking_IsObject(arguments []interface{}, row RowType) bool {
|
||||
func (r rowContext) typeChecking_IsObject(arguments []interface{}) bool {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
_, isObject := ex.(map[string]interface{})
|
||||
return isObject
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) typeChecking_IsPrimitive(arguments []interface{}, row RowType) bool {
|
||||
func (r rowContext) typeChecking_IsPrimitive(arguments []interface{}) bool {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
switch ex.(type) {
|
||||
case bool, string, float64, int, nil:
|
||||
@@ -87,9 +87,9 @@ func (c memoryExecutorContext) typeChecking_IsPrimitive(arguments []interface{},
|
||||
}
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) typeChecking_IsString(arguments []interface{}, row RowType) bool {
|
||||
func (r rowContext) typeChecking_IsString(arguments []interface{}) bool {
|
||||
exItem := arguments[0].(parsers.SelectItem)
|
||||
ex := c.getFieldValue(exItem, row)
|
||||
ex := r.resolveSelectItem(exItem)
|
||||
|
||||
_, isStr := ex.(string)
|
||||
return isStr
|
||||
|
||||
52
sharedlibrary/sharedlibrary.go
Normal file
52
sharedlibrary/sharedlibrary.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package main
|
||||
|
||||
import "C"
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/pikami/cosmium/api"
|
||||
"github.com/pikami/cosmium/api/config"
|
||||
"github.com/pikami/cosmium/internal/repositories"
|
||||
)
|
||||
|
||||
var currentServer *api.Server
|
||||
|
||||
//export Configure
|
||||
func Configure(configurationJSON *C.char) bool {
|
||||
var configuration config.ServerConfig
|
||||
err := json.Unmarshal([]byte(C.GoString(configurationJSON)), &configuration)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
config.Config = configuration
|
||||
return true
|
||||
}
|
||||
|
||||
//export InitializeRepository
|
||||
func InitializeRepository() {
|
||||
repositories.InitializeRepository()
|
||||
}
|
||||
|
||||
//export StartAPI
|
||||
func StartAPI() {
|
||||
currentServer = api.StartAPI()
|
||||
}
|
||||
|
||||
//export StopAPI
|
||||
func StopAPI() {
|
||||
if currentServer == nil {
|
||||
currentServer.StopServer <- true
|
||||
currentServer = nil
|
||||
}
|
||||
}
|
||||
|
||||
//export GetState
|
||||
func GetState() *C.char {
|
||||
stateJSON, err := repositories.GetState()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return C.CString(stateJSON)
|
||||
}
|
||||
|
||||
func main() {}
|
||||
Reference in New Issue
Block a user