|
57 | 57 |
|
58 | 58 | <option value="file0">github.com/hashicorp/mql/common.go (100.0%)</option>
|
59 | 59 |
|
60 |
| - <option value="file1">github.com/hashicorp/mql/expr.go (100.0%)</option> |
| 60 | + <option value="file1">github.com/hashicorp/mql/expr.go (97.9%)</option> |
61 | 61 |
|
62 | 62 | <option value="file2">github.com/hashicorp/mql/lex.go (100.0%)</option>
|
63 | 63 |
|
64 | 64 | <option value="file3">github.com/hashicorp/mql/mql.go (98.2%)</option>
|
65 | 65 |
|
66 | 66 | <option value="file4">github.com/hashicorp/mql/options.go (100.0%)</option>
|
67 | 67 |
|
68 |
| - <option value="file5">github.com/hashicorp/mql/parser.go (93.4%)</option> |
| 68 | + <option value="file5">github.com/hashicorp/mql/parser.go (93.6%)</option> |
69 | 69 |
|
70 | 70 | <option value="file6">github.com/hashicorp/mql/stack.go (100.0%)</option>
|
71 | 71 |
|
|
224 | 224 | if err != nil </span><span class="cov8" title="1">{
|
225 | 225 | return nil, fmt.Errorf("%s: %q in %s: %w", op, *e.value, e.String(), ErrInvalidParameter)
|
226 | 226 | }</span>
|
| 227 | + |
| 228 | + <span class="cov8" title="1">opts, err := getOpts(opt...) |
| 229 | + if err != nil </span><span class="cov0" title="0">{ |
| 230 | + return nil, fmt.Errorf("%s: %w", op, err) |
| 231 | + }</span> |
| 232 | + <span class="cov8" title="1">if n, ok := opts.withTableColumnMap[columnName]; ok </span><span class="cov8" title="1">{ |
| 233 | + // override our column name with the mapped column name |
| 234 | + columnName = n |
| 235 | + }</span> |
| 236 | + |
227 | 237 | <span class="cov8" title="1">if validator.typ == "time" </span><span class="cov8" title="1">{
|
228 | 238 | columnName = fmt.Sprintf("%s::date", columnName)
|
229 | 239 | }</span>
|
|
251 | 261 | func newLogicalOp(s string) (logicalOp, error) <span class="cov8" title="1">{
|
252 | 262 | const op = "newLogicalOp"
|
253 | 263 | switch logicalOp(s) </span>{
|
254 |
| - case |
255 |
| - andOp, |
256 |
| - orOp:<span class="cov8" title="1"> |
| 264 | + case andOp, orOp:<span class="cov8" title="1"> |
257 | 265 | return logicalOp(s), nil</span>
|
258 | 266 | default:<span class="cov8" title="1">
|
259 | 267 | return "", fmt.Errorf("%s: %w %q", op, ErrInvalidLogicalOp, s)</span>
|
|
758 | 766 | if err != nil </span><span class="cov8" title="1">{
|
759 | 767 | return nil, fmt.Errorf("%s: %w", op, err)
|
760 | 768 | }</span>
|
761 |
| - <span class="cov8" title="1">switch </span>{ |
762 |
| - case opts.withValidateConvertColumn == v.column && !isNil(opts.withValidateConvertFn):<span class="cov8" title="1"> |
763 |
| - return opts.withValidateConvertFn(v.column, v.comparisonOp, v.value)</span> |
| 769 | + <span class="cov8" title="1">switch validateConvertFn, ok := opts.withValidateConvertFns[v.column]; </span>{ |
| 770 | + case ok && !isNil(validateConvertFn):<span class="cov8" title="1"> |
| 771 | + return validateConvertFn(v.column, v.comparisonOp, v.value)</span> |
764 | 772 | default:<span class="cov8" title="1">
|
765 | 773 | columnName := strings.ToLower(v.column)
|
766 | 774 | if n, ok := opts.withColumnMap[columnName]; ok </span><span class="cov8" title="1">{
|
|
812 | 820 | )
|
813 | 821 |
|
814 | 822 | type options struct {
|
815 |
| - withSkipWhitespace bool |
816 |
| - withColumnMap map[string]string |
817 |
| - withValidateConvertFn ValidateConvertFunc |
818 |
| - withValidateConvertColumn string |
819 |
| - withIgnoredFields []string |
820 |
| - withPgPlaceholder bool |
| 823 | + withSkipWhitespace bool |
| 824 | + withColumnMap map[string]string |
| 825 | + withValidateConvertFns map[string]ValidateConvertFunc |
| 826 | + withIgnoredFields []string |
| 827 | + withPgPlaceholder bool |
| 828 | + withTableColumnMap map[string]string // map of model field names to their table.column name |
821 | 829 | }
|
822 | 830 |
|
823 | 831 | // Option - how options are passed as args
|
824 | 832 | type Option func(*options) error
|
825 | 833 |
|
826 | 834 | func getDefaultOptions() options <span class="cov8" title="1">{
|
827 | 835 | return options{
|
828 |
| - withColumnMap: make(map[string]string), |
| 836 | + withColumnMap: make(map[string]string), |
| 837 | + withValidateConvertFns: make(map[string]ValidateConvertFunc), |
| 838 | + withTableColumnMap: make(map[string]string), |
829 | 839 | }
|
830 | 840 | }</span>
|
831 | 841 |
|
|
848 | 858 | }</span>
|
849 | 859 | }
|
850 | 860 |
|
851 |
| -// WithColumnMap provides an optional map of columns from a column in the user |
852 |
| -// provided query to a column in the database model |
| 861 | +// WithColumnMap provides an optional map of columns from the user |
| 862 | +// provided query to a field in the given model |
853 | 863 | func WithColumnMap(m map[string]string) Option <span class="cov8" title="1">{
|
854 | 864 | return func(o *options) error </span><span class="cov8" title="1">{
|
855 | 865 | if !isNil(m) </span><span class="cov8" title="1">{
|
|
871 | 881 | return func(o *options) error </span><span class="cov8" title="1">{
|
872 | 882 | switch </span>{
|
873 | 883 | case fieldName != "" && !isNil(fn):<span class="cov8" title="1">
|
874 |
| - o.withValidateConvertFn = fn |
875 |
| - o.withValidateConvertColumn = fieldName</span> |
| 884 | + if _, exists := o.withValidateConvertFns[fieldName]; exists </span><span class="cov8" title="1">{ |
| 885 | + return fmt.Errorf("%s: duplicated convert: %w", op, ErrInvalidParameter) |
| 886 | + }</span> |
| 887 | + <span class="cov8" title="1">o.withValidateConvertFns[fieldName] = fn</span> |
876 | 888 | case fieldName == "" && !isNil(fn):<span class="cov8" title="1">
|
877 | 889 | return fmt.Errorf("%s: missing field name: %w", op, ErrInvalidParameter)</span>
|
878 | 890 | case fieldName != "" && isNil(fn):<span class="cov8" title="1">
|
|
902 | 914 | return nil
|
903 | 915 | }</span>
|
904 | 916 | }
|
| 917 | + |
| 918 | +// WithTableColumnMap provides an optional map of columns from the |
| 919 | +// model to the table.column name in the generated where clause |
| 920 | +// |
| 921 | +// For example, if you need to map the language field name to something |
| 922 | +// more complex in your SQL statement then you can use this map: |
| 923 | +// |
| 924 | +// WithTableColumnMap(map[string]string{"language":"preferences->>'language'"}) |
| 925 | +// |
| 926 | +// In the example above we're mapping "language" field to a json field in |
| 927 | +// the "preferences" column. A user can say `language="blah"` and the |
| 928 | +// mql-created SQL where clause will contain `preferences->>'language'="blah"` |
| 929 | +// |
| 930 | +// The field names in the keys to the map should always be lower case. |
| 931 | +func WithTableColumnMap(m map[string]string) Option <span class="cov8" title="1">{ |
| 932 | + return func(o *options) error </span><span class="cov8" title="1">{ |
| 933 | + if !isNil(m) </span><span class="cov8" title="1">{ |
| 934 | + o.withTableColumnMap = m |
| 935 | + }</span> |
| 936 | + <span class="cov8" title="1">return nil</span> |
| 937 | + } |
| 938 | +} |
905 | 939 | </pre>
|
906 | 940 |
|
907 | 941 | <pre class="file" id="file5" style="display: none">// Copyright (c) HashiCorp, Inc.
|
|
969 | 1003 | return nil, fmt.Errorf("%s: %w before right side expression in: %q", op, ErrMissingLogicalOp, p.raw)</span>
|
970 | 1004 | // finally, assign the right expr
|
971 | 1005 | case logicExpr.rightExpr == nil:<span class="cov8" title="1">
|
972 |
| - logicExpr.rightExpr = e.leftExpr |
| 1006 | + if e.rightExpr != nil </span><span class="cov8" title="1">{ |
| 1007 | + // if e.rightExpr isn't nil, then we've got a complete |
| 1008 | + // expr (left + op + right) and we need to assign this to |
| 1009 | + // our rightExpr |
| 1010 | + logicExpr.rightExpr = e |
| 1011 | + break TkLoop</span> |
| 1012 | + } |
| 1013 | + // otherwise, we need to assign the left side of e |
| 1014 | + <span class="cov8" title="1">logicExpr.rightExpr = e.leftExpr |
973 | 1015 | break TkLoop</span>
|
974 | 1016 | }
|
975 | 1017 | case stringToken, numberToken, symbolToken:<span class="cov8" title="1">
|
|
0 commit comments