Repository: brimsec/zq (go)

Issue:
Unexpected output format of nulls casted to strings in ZSON
Repro is with `zq` commit `2294ca24`.

Based on my understanding of the ZSON spec, this representation of a `null` value of type `string` looks correct.

```
$ echo '{"foo": null}' | zq -f zson -
{
    foo: null (string)
} (=0)
```

However, if I use `put` to create an additional `null` field while using casting to assign `string` as its explicit type, it's rendered as a hyphen.

```
$ echo '{"foo": null}' | zq -f zson 'put bar=null:string' -
{
    foo: null (string),
    bar: "-"
} (=0)
```

If I grab the null value from the named field and apply the cast (which is unnecessary here, but let's say I wanted to be sure), it shows up as an empty string.


```
$ echo '{"foo": null}' | zq -f zson 'put bar=foo:string' -
{
    foo: null (string),
    bar: ""
} (=0)
```

Pull request description:
handle null casts properly for all Z types
The cast expressions are still a work in progress, but in particular,
the various casters aren't handling null properly.  Since Z allows
any value to be null, this is very easy handed as universal check
before calling the type-specific caster.  This commit adds this logic
and a simple test to cover it.

This is related to issue #1603 and fixes #2065 


Patch:
diff --git a/expr/eval.go b/expr/eval.go
index a4eb05c7d..1759281a3 100644
--- a/expr/eval.go
+++ b/expr/eval.go
@@ -718,12 +718,13 @@ func NewCast(expr Evaluator, styp string) (Evaluator, error) {
 		// XXX See issue #1572.   To implement aliascast here.
 		return nil, fmt.Errorf("cast to %s not implemented", styp)
 	}
-	return &evalCast{expr, c}, nil
+	return &evalCast{expr, c, typ}, nil
 }
 
 type evalCast struct {
 	expr   Evaluator
 	caster PrimitiveCaster
+	typ    zng.Type
 }
 
 func (c *evalCast) Eval(rec *zng.Record) (zng.Value, error) {
@@ -731,6 +732,11 @@ func (c *evalCast) Eval(rec *zng.Record) (zng.Value, error) {
 	if err != nil {
 		return zng.Value{}, err
 	}
+	if zv.Bytes == nil {
+		// Take care of null here so the casters don't have to
+		// worry about it.  Any value can be null after all.
+		return zng.Value{c.typ, nil}, nil
+	}
 	return c.caster(zv)
 }
 


Test patch:
diff --git a/expr/ztests/null-cast.yaml b/expr/ztests/null-cast.yaml
new file mode 100644
index 000000000..6539130b8
--- /dev/null
+++ b/expr/ztests/null-cast.yaml
@@ -0,0 +1,9 @@
+zql: cut s=null:string,a=null:ip
+
+input: |
+  #0:record[x:string]
+  0:[foo;]
+
+output: |
+  #0:record[s:string,a:ip]
+  0:[-;-;]