Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow attribute transformations in SELECT clause #23

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compare/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"strings"
"time"

"github.com/kshvmdn/fsql/query"
"../query"
)

// Alpha compares two strings a and b.
Expand Down
12 changes: 6 additions & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import (
"strings"
"time"

cmp "github.com/kshvmdn/fsql/compare"
"github.com/kshvmdn/fsql/query"
cmp "./compare"
"./query"
)

const (
Expand Down Expand Up @@ -145,29 +145,29 @@ func main() {
}

if q.HasAttribute("mode") {
fmt.Printf("%s", info.Mode())
fmt.Printf("%s", q.PerformTransformations("mode",info.Mode()))
if q.HasAttribute("size", "time", "name") {
fmt.Print("\t")
}
}

if q.HasAttribute("size") {
fmt.Printf("%d", info.Size())
fmt.Printf("%s", q.PerformTransformations("size",info.Size()))
if q.HasAttribute("time", "name") {
fmt.Print("\t")
}
}

if q.HasAttribute("time") {
fmt.Printf("%s", info.ModTime().Format(time.Stamp))
fmt.Printf("%s", q.PerformTransformations("time",info.ModTime().Format(time.Stamp)) )
if q.HasAttribute("name") {
fmt.Print("\t")
}
}

if q.HasAttribute("name") {
// TODO: Only show file name, instead of the full path?
fmt.Printf("%s", path)
fmt.Printf("%s", q.PerformTransformations("name",path))
}

fmt.Printf("\n")
Expand Down
56 changes: 50 additions & 6 deletions query/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ func (p *parser) showAllAttributes() (bool, error) {
func (p *parser) parse(input string) (*Query, error) {
p.tokenizer = NewTokenizer(input)
q := new(Query)
q.Transformations = make(map[string][]Function)

all, err := p.showAllAttributes()
if err != nil {
Expand All @@ -68,7 +69,7 @@ func (p *parser) parse(input string) (*Query, error) {
q.Attributes = allAttributes
} else {
q.Attributes = make(map[string]bool)
err := p.parseAttributes(&q.Attributes)
err := p.parseAttributes(&q.Attributes,&q.Transformations)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -123,24 +124,67 @@ func (p *parser) parse(input string) (*Query, error) {
}

// Parse the list of attributes provided to the SELECT clause.
func (p *parser) parseAttributes(attributes *map[string]bool) error {
func (p *parser) parseAttributes(attributes *map[string]bool, transformations *map[string][]Function) error {
attribute := p.expect(Identifier)
if attribute == nil {
return p.currentError()
}
if attribute.Raw == "*" || attribute.Raw == "all" {
*attributes = allAttributes
} else if _, ok := allAttributes[attribute.Raw]; !ok {
return &ErrUnknownToken{attribute.Raw}
} else {
} else{
p.current = attribute
attribute, err := p.parseAttribute(transformations)
if err != nil{
return err
}
if _, ok := allAttributes[attribute.Raw]; !ok {
return &ErrUnknownToken{attribute.Raw}
}
(*attributes)[attribute.Raw] = true

}

if p.expect(Comma) == nil {
return nil
}

return p.parseAttributes(attributes)
return p.parseAttributes(attributes, transformations)
}

// Parses all the transformation applied to given attribute recursively
func (p *parser) parseAttribute( transformations *map[string][]Function) (*Token,error) {
identifier := p.expect(Identifier)
var currFunction Function
if identifier != nil {
if p.expect(OpenParen) != nil {
currFunction = Function{Name: identifier.Raw, Arguments: make([]string,0)}
attribute,err := p.parseAttribute( transformations )
if attribute == nil{
return attribute,err
}
for{
if token := p.expect(Identifier); token != nil{
currFunction.Arguments = append(currFunction.Arguments,token.Raw)
continue
} else if token := p.expect(Comma); token != nil{
continue
} else if token := p.expect(CloseParen); token != nil{
if _,ok := (*transformations)[attribute.Raw]; !ok{
(*transformations)[attribute.Raw] = make([]Function,0)
}
(*transformations)[attribute.Raw] = append((*transformations)[attribute.Raw], currFunction)
return attribute,err
}
return nil, p.currentError()
}
} else{
if _,ok := allAttributes[identifier.Raw]; !ok{
return nil, &ErrUnknownToken{identifier.Raw}
}
return identifier,nil
}
}
return nil, p.currentError()
}

// Parse the list of directories passed to the FROM clause. Expects that
Expand Down
28 changes: 28 additions & 0 deletions query/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,41 @@ package query
import (
"fmt"
"os"
"bytes"
)

// Function entity used for defining transformations on attributes
type Function struct {
Name string
Arguments []string
}


// Query represents an input query.
type Query struct {
Attributes map[string]bool
Sources map[string][]string
ConditionTree *ConditionNode // Root node of this query's condition tree.
Transformations map[string][]Function
}

// PrintTransformations function prints all transformations parsed from given query
func (q *Query) PrintTransformations() {
for _, functions := range q.Transformations{
for _, function := range functions{
fmt.Println(function.String())
}
}
}

// Return string representation of a given function
func (f *Function) String() string{
var buffer bytes.Buffer
buffer.WriteString(f.Name)
for _,v := range f.Arguments{
buffer.WriteString(fmt.Sprintf(", %s",v))
}
return buffer.String()
}

// HasAttribute checks if the query's attribute map contains the provided
Expand Down
77 changes: 77 additions & 0 deletions query/transformations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package query

import (
"fmt"
"os"
"strings"
"path/filepath"
)

// Function mapping of transformations
var allTransformations = map[string]func (interface{}, []string) interface{}{
"format": format,
"upper": upper,
"fullpath": fullpath,
}

//format function
func format(inp interface{}, args []string) interface{}{

if size,ok := inp.(int64); ok{
if args[0] == "kb"{
return fmt.Sprintf("%fkb",float64(size)/(1<<10))
} else if args[0] == "mb"{
return fmt.Sprintf("%fmb",float64(size)/(1<<20))
} else if args[0] == "gb"{
return fmt.Sprintf("%fgb",float64(size)/(1<<30))
}
} else{
fmt.Fprintf(os.Stderr,"type for attribute:%s for function: format is not implemented\n",inp)
os.Exit(1)
}
return ""
}

//Upper Function
func upper(inp interface{}, args []string) interface{}{
if str,ok := inp.(string); ok{
return strings.ToUpper(str)
}
fmt.Fprintf(os.Stderr,"type for attribute:%s for function: format is not implemented\n",inp)
os.Exit(1)
return ""
}

//Fullpath function
func fullpath(inp interface{}, args []string) interface{}{
if str,ok := inp.(string); ok{
res,_:=filepath.Abs(str)
return res
}
fmt.Fprintf(os.Stderr,"type for attribute:%s for function: format is not implemented\n",inp)
os.Exit(1)
return ""
}

//PerformTransformations fetchs all transformation to be applied on a given attribute and applies them one by one
func (q *Query) PerformTransformations(attribute string, in interface{}) string{
functions,ok := q.Transformations[attribute]
res := in
if ok{
for _, function := range functions{
if funcImpl,ok:=allTransformations[function.Name];ok{
res = funcImpl(res, function.Arguments)
} else{
fmt.Fprintf(os.Stderr,"Given function:%s is not implemented\n",function.Name)
os.Exit(1)
}
}
}

if v,ok:=res.(int64);ok{
return fmt.Sprintf("%d",v)
} else if v,ok:=res.(string);ok{
return v
}
return ""
}