github.com/mithrandie/csvq@v1.18.1/lib/query/table_path.go (about)

     1  package query
     2  
     3  import (
     4  	"context"
     5  	"net/url"
     6  	"path/filepath"
     7  	"strings"
     8  
     9  	"github.com/mithrandie/csvq/lib/parser"
    10  	"github.com/mithrandie/csvq/lib/value"
    11  )
    12  
    13  type DataObject struct {
    14  	*parser.BaseExpr
    15  	Raw string
    16  }
    17  
    18  func (o DataObject) String() string {
    19  	return o.Raw
    20  }
    21  
    22  type HttpObject struct {
    23  	*parser.BaseExpr
    24  	URL string
    25  }
    26  
    27  func (o HttpObject) String() string {
    28  	return o.URL
    29  }
    30  
    31  func ParseTableName(ctx context.Context, scope *ReferenceScope, table parser.Table) (parser.Identifier, error) {
    32  	if table.Alias != nil {
    33  		return table.Alias.(parser.Identifier), nil
    34  	}
    35  
    36  	name := parser.Identifier{
    37  		BaseExpr: table.Object.GetBaseExpr(),
    38  	}
    39  
    40  	tableObject, err := NormalizeTableObject(ctx, scope, table.Object)
    41  	if err != nil {
    42  		return name, err
    43  	}
    44  
    45  	switch obj := tableObject.(type) {
    46  	case parser.Identifier:
    47  		name.Literal = FormatTableName(obj.Literal)
    48  	case parser.Stdin:
    49  		name.Literal = obj.String()
    50  	case parser.FormatSpecifiedFunction:
    51  		return ParseTableName(ctx, scope, parser.Table{Object: obj.Path})
    52  	default:
    53  		// Do Nothing
    54  	}
    55  	return name, nil
    56  }
    57  
    58  func NormalizeTableObject(ctx context.Context, scope *ReferenceScope, tableObject parser.QueryExpression) (parser.QueryExpression, error) {
    59  	if tableFunction, ok := tableObject.(parser.TableFunction); ok {
    60  		p, err := ConvertTableFunction(ctx, scope, tableFunction)
    61  		if err != nil {
    62  			return nil, err
    63  		}
    64  		tableObject = p
    65  	}
    66  
    67  	if urlExpr, ok := tableObject.(parser.Url); ok {
    68  		p, err := ConvertUrlExpr(urlExpr)
    69  		if err != nil {
    70  			return nil, err
    71  		}
    72  		tableObject = p
    73  	}
    74  
    75  	return tableObject, nil
    76  }
    77  
    78  func ConvertTableFunction(ctx context.Context, scope *ReferenceScope, tableFunction parser.TableFunction) (parser.QueryExpression, error) {
    79  	name := strings.ToUpper(tableFunction.Name)
    80  
    81  	switch name {
    82  	case "FILE", "INLINE", "URL", "DATA":
    83  		if len(tableFunction.Args) != 1 {
    84  			return nil, NewFunctionArgumentLengthError(tableFunction, strings.ToUpper(tableFunction.Name), []int{1})
    85  		}
    86  	default:
    87  		return nil, NewFunctionNotExistError(tableFunction, strings.ToUpper(tableFunction.Name))
    88  	}
    89  
    90  	args := make([]value.Primary, len(tableFunction.Args))
    91  	for i, v := range tableFunction.Args {
    92  		arg, err := Evaluate(ctx, scope, v)
    93  		if err != nil {
    94  			return nil, err
    95  		}
    96  		args[i] = arg
    97  	}
    98  
    99  	var expr parser.QueryExpression
   100  
   101  	switch name {
   102  	case "FILE", "INLINE":
   103  		p := value.ToString(args[0])
   104  		if value.IsNull(p) {
   105  			return nil, NewFunctionInvalidArgumentError(tableFunction, strings.ToUpper(tableFunction.Name), "the first argument must be a string")
   106  		}
   107  		expr = parser.Identifier{BaseExpr: tableFunction.GetBaseExpr(), Literal: p.(*value.String).Raw()}
   108  	case "URL":
   109  		p := value.ToString(args[0])
   110  		if value.IsNull(p) {
   111  			return nil, NewFunctionInvalidArgumentError(tableFunction, strings.ToUpper(tableFunction.Name), "the first argument must be a string")
   112  		}
   113  		expr = parser.Url{BaseExpr: tableFunction.GetBaseExpr(), Raw: p.(*value.String).Raw()}
   114  	case "DATA":
   115  		p := value.ToString(args[0])
   116  		if value.IsNull(p) {
   117  			return nil, NewFunctionInvalidArgumentError(tableFunction, strings.ToUpper(tableFunction.Name), "the first argument must be a string")
   118  		}
   119  		expr = DataObject{BaseExpr: tableFunction.GetBaseExpr(), Raw: p.(*value.String).Raw()}
   120  	}
   121  	return expr, nil
   122  }
   123  
   124  func ConvertUrlExpr(urlExpr parser.Url) (parser.QueryExpression, error) {
   125  	u, e := url.Parse(urlExpr.Raw)
   126  	if e != nil {
   127  		return nil, NewInvalidUrlError(urlExpr)
   128  	}
   129  
   130  	switch u.Scheme {
   131  	case "http", "https":
   132  		return HttpObject{BaseExpr: urlExpr.GetBaseExpr(), URL: u.String()}, nil
   133  	case "file":
   134  		path := u.Path
   135  		var err error
   136  
   137  		if strings.HasPrefix(u.String(), "file://") {
   138  			if 0 < len(u.Host) {
   139  				return nil, NewInvalidUrlError(urlExpr)
   140  			}
   141  			if u.IsAbs() && 0 < len(path) && !filepath.IsAbs(path) {
   142  				path = path[1:]
   143  			}
   144  		} else {
   145  			path = u.String()[5:]
   146  		}
   147  
   148  		if len(path) < 1 {
   149  			path = "."
   150  		}
   151  
   152  		path, err = url.PathUnescape(path)
   153  		if err != nil {
   154  			return nil, NewInvalidUrlError(urlExpr)
   155  		}
   156  
   157  		return parser.Identifier{BaseExpr: urlExpr.GetBaseExpr(), Literal: path}, nil
   158  	}
   159  	return nil, NewUnsupportedUrlSchemeError(urlExpr, u.Scheme)
   160  }