func (ca Criteria) SQL() (stmt string, args []interface{}, err error) {
	var slist []string
	for _, c := range ca {
		s, a, err := c.SQL()
		if err != nil {
			return "", nil, err
		}
		slist = append(slist, s)
		args = append(args, a...)
	}
	return strings.Join(slist, " AND "), args, nil
}
SQL converts to a SQL where clause and the corresponding arguments for it.
package tmetautil
import (
	"bytes"
	"fmt"
	"strings"
)
type Op string
const (
	EqOp   Op = "="
	NeOp   Op = "<>"
	LtOp   Op = "<"
	LteOp  Op = "<="
	GtOp   Op = ">"
	GteOp  Op = ">="
	LikeOp Op = "like"
	InOp   Op = "in"
)
type Criterion struct {
	Not   bool        `json:"not"`
	Field string      `json:"field"`
	Op    Op          `json:"op"`
	Value interface{} `json:"value"`
	Or    Criteria    `json:"or"`
}
func (c Criterion) CheckFieldNames(fields ...string) error {
	err := c.Or.CheckFieldNames(fields...)
	if err != nil {
		return err
	}
	for _, f := range fields {
		if f == c.Field {
			return nil
		}
	}
	return fmt.Errorf("%q is not a valid field name", c.Field)
}
func (c Criterion) ContainsMatch(likePrefixLen int, fields ...string) bool {
	for _, ci := range c.Or {
		if !ci.ContainsMatch(likePrefixLen, fields...) {
			return false
		}
	}
	if c.Not {
		return false
	}
	matchField := func(f string) bool {
		for _, fi := range fields {
			if fi == f {
				return true
			}
		}
		return false
	}
	if !matchField(c.Field) {
		return false
	}
	if c.Op == LikeOp {
		s, _ := c.Value.(string)
		if len(s) < likePrefixLen {
			return false
		}
		ilen := len(s)
		if ilen > likePrefixLen {
			ilen = likePrefixLen
		}
		for i := 0; i < ilen; i++ {
			if s[i] == '%' || s[i] == '_' {
				return false
			}
		}
		return true
	}
	return true
}
func (ca Criterion) SQL() (stmt string, args []interface{}, err error) {
	var buf bytes.Buffer
	if ca.Not {
		buf.WriteString("NOT ")
	}
	noOp := false
	switch ca.Op {
	case EqOp, NeOp, LtOp, LteOp, GtOp, GteOp, LikeOp,
		InOp:  
		buf.WriteString(ca.Field)
		buf.WriteString(" ")
		buf.WriteString(string(ca.Op))
		buf.WriteString(" ?")
		args = append(args, ca.Value)
	case Op(""):
		noOp = true
	default:
		return "", nil, fmt.Errorf("unknown operator %q", ca.Op)
	}
	if len(ca.Or) > 0 {
		var sl []string
		for _, ci := range ca.Or {
			s, a, err := ci.SQL()
			if err != nil {
				return "", nil, err
			}
			sl = append(sl, s)
			args = append(args, a...)
		}
		if buf.Len() > 0 {
			buf.WriteString(" AND ")
		}
		buf.WriteString("(")
		buf.WriteString(strings.Join(sl, " OR "))
		buf.WriteString(")")
	} else if noOp {
		return "", nil, nil
	}
	return buf.String(), args, nil
}
type Criteria []Criterion
func (ca Criteria) CheckFieldNames(fields ...string) error {
	for _, c := range ca {
		err := c.CheckFieldNames(fields...)
		if err != nil {
			return err
		}
	}
	return nil
}
func (ca Criteria) ContainsMatch(likePrefixLen int, fields ...string) bool {
	for _, ci := range ca {
		if ci.ContainsMatch(likePrefixLen, fields...) {
			return true
		}
	}
	return false
}