func (v VendorDataExporter) JSON(ctx context.Context, w io.Writer, indent string) error {
	q := sqlutil.Select(
		"cve_id",
		"cve_json",
	).From(
		"vendor_data",
	).Literal(
		"LEFT JOIN vendor ON vendor.version = vendor_data.version",
	).Where(
		v.condition(),
	)
	query, args := q.String(), q.QueryArgs()
	if debug.V(1) {
		flog.Infof("running: %q / %#v", query, args)
	}
	rows, err := v.DB.QueryContext(ctx, query, args...)
	if err != nil {
		return errors.Wrap(err, "cannot query vendor data")
	}
	defer rows.Close()
	record := struct {
		CVE  string
		JSON []byte
	}{}
	f := &cveFile{}
	for rows.Next() {
		v := record
		err = rows.Scan(sqlutil.NewRecordType(&v).Values()...)
		if err != nil {
			return errors.Wrap(err, "cannot scan vendor data")
		}
		err = f.Add(v.CVE, v.JSON)
		if err != nil {
			return err
		}
	}
	if indent == "" {
		return f.EncodeJSON(w)
	}
	const prefix = ""
	return f.EncodeIndentedJSON(w, prefix, indent)
}
JSON writes NVD CVE JSON to w.
package vulndb
import (
	"context"
	"database/sql"
	"encoding/csv"
	"io"
	"strconv"
	"time"
	"github.com/pkg/errors"
	"github.com/facebookincubator/flog"
	"github.com/facebookincubator/nvdtools/vulndb/debug"
	"github.com/facebookincubator/nvdtools/vulndb/sqlutil"
)
type VendorRecord struct {
	Version  int64     `sql:"version"`
	TS       time.Time `sql:"ts"`
	Ready    bool      `sql:"ready"`
	Owner    string    `sql:"owner"`
	Provider string    `sql:"provider"`
}
type VendorDataRecord struct {
	Version   int64     `sql:"version"`
	CVE       string    `sql:"cve_id"`
	Published time.Time `sql:"published"`
	Modified  time.Time `sql:"modified"`
	BaseScore float64   `sql:"base_score"`
	Summary   string    `sql:"summary"`
	JSON      []byte    `sql:"cve_json"`
}
type VendorDataImporter struct {
	DB       *sql.DB
	Owner    string
	Provider string
	OnFile   func(filename string)
}
func (v VendorDataImporter) ImportFiles(ctx context.Context, files ...string) (*VendorRecord, error) {
	vendor, err := v.newVersion(ctx, v.Owner, v.Provider)
	if err != nil {
		return nil, err
	}
	for _, file := range files {
		if v.OnFile != nil {
			v.OnFile(file)
		}
		records, err := VendorDataFromFile(vendor, file)
		if err != nil {
			return nil, err
		}
		err = v.importData(ctx, records)
		if err != nil {
			return nil, err
		}
	}
	err = v.enableVersion(ctx, vendor)
	if err != nil {
		return nil, err
	}
	return vendor, nil
}
func (v VendorDataImporter) newVersion(ctx context.Context, owner, provider string) (*VendorRecord, error) {
	vendor := VendorRecord{
		TS:       time.Now().UTC(),
		Ready:    false,
		Owner:    owner,
		Provider: provider,
	}
	r := sqlutil.NewRecordType(vendor).Subset(
		"ts",
		"ready",
		"owner",
		"provider",
	)
	q := sqlutil.Insert().
		Into("vendor").
		Fields(r.Fields()...).
		Values(r)
	query, args := q.String(), q.QueryArgs()
	if debug.V(1) {
		flog.Infof("running: %q / %#v", query, args)
	}
	res, err := v.DB.ExecContext(ctx, query, args...)
	if err != nil {
		return nil, errors.Wrap(err, "cannot insert vendor record")
	}
	version, err := res.LastInsertId()
	if err != nil {
		return nil, errors.Wrap(err, "cannot get last id from vendor record")
	}
	vendor.Version = version
	return &vendor, nil
}
func (v VendorDataImporter) replaceVendorData(ctx context.Context, records sqlutil.Records) error {
	q := sqlutil.Replace().
		Into("vendor_data").
		Fields(records.Fields()...).
		Values(records...)
	query, args := q.String(), q.QueryArgs()
	if debug.V(2) {
		flog.Infof("running: %q", query)
	}
	_, err := v.DB.ExecContext(ctx, query, args...)
	if err != nil {
		return errors.Wrap(err, "cannot insert vendor data records")
	}
	return nil
}
func (v VendorDataImporter) replaceVendorDataBatch(ctx context.Context, records sqlutil.Records) error {
	from := 0
OuterLoop:
	for batchSize := len(records); batchSize > 0; batchSize /= 2 {
		for idx := from; idx < len(records); idx += batchSize {
			limit := idx + batchSize
			if limit > len(records) {
				limit = len(records)
			}
			if err := v.replaceVendorData(ctx, records[idx:limit]); err != nil {
				continue OuterLoop
			}
			from = limit
		}
		return nil
	}
	return errors.New("can't insert batch")
}
func (v VendorDataImporter) importData(ctx context.Context, data []VendorDataRecord) error {
	records := sqlutil.NewRecords(data)
	const batchSize = 100
	for i := 0; i < len(records); i += batchSize {
		limit := i + batchSize
		if limit > len(records) {
			limit = len(records)
		}
		if err := v.replaceVendorDataBatch(ctx, records[i:limit]); err != nil {
			return errors.Wrap(err, "cannot insert vendor data records")
		}
	}
	return nil
}
func (v VendorDataImporter) enableVersion(ctx context.Context, vendor *VendorRecord) error {
	q := sqlutil.Update("vendor").Set(
		sqlutil.Assign().Equal("ready", true),
	).Where(
		sqlutil.Cond().Equal("version", vendor.Version),
	)
	query, args := q.String(), q.QueryArgs()
	if debug.V(1) {
		flog.Infof("running: %q / %#v", query, args)
	}
	_, err := v.DB.ExecContext(ctx, query, args...)
	if err != nil {
		return errors.Wrap(err, "cannot update vendor record")
	}
	return nil
}
func VendorDataFromFile(vendor *VendorRecord, name string) ([]VendorDataRecord, error) {
	feed, err := readNVDCVEJSON(name)
	if err != nil {
		return nil, errors.Wrap(err, "cannot load vendor file")
	}
	records := make([]VendorDataRecord, len(feed.CVEItems))
	for i, item := range feed.CVEItems {
		cve := cveItem{item}
		records[i] = VendorDataRecord{
			Version:   vendor.Version,
			CVE:       cve.ID(),
			Published: cve.Published(),
			Modified:  cve.Modified(),
			BaseScore: cve.BaseScore(),
			Summary:   cve.Summary(),
			JSON:      cve.JSON(),
		}
	}
	return records, nil
}
type VendorDataExporter struct {
	DB         *sql.DB
	Provider   string
	FilterCVEs []string
}
func (v VendorDataExporter) condition() *sqlutil.QueryConditionSet {
	cond := sqlutil.Cond().InSelect("vendor.version",
		sqlutil.Select("latest.version").
			From().
			SelectGroup("latest", latestVendorVersion()).
			Where(
				sqlutil.Cond().Equal("provider", v.Provider),
			),
	)
	if len(v.FilterCVEs) > 0 {
		cond = cond.And().In("vendor_data.cve_id", v.FilterCVEs)
	}
	return cond
}
func (v VendorDataExporter) CSV(ctx context.Context, w io.Writer, header bool) error {
	q := sqlutil.Select(
		"vendor.version AS version",
		"vendor.ts AS ts",
		"vendor.owner AS owner",
		"vendor.provider AS provider",
		"vendor_data.cve_id AS cve_id",
		"vendor_data.published AS published",
		"vendor_data.modified AS modified",
		"vendor_data.base_score AS base_score",
		"vendor_data.summary AS summary",
	).From(
		"vendor_data",
	).Literal(
		"LEFT JOIN vendor ON vendor.version = vendor_data.version",
	).Where(
		v.condition(),
	)
	query, args := q.String(), q.QueryArgs()
	if debug.V(1) {
		flog.Infof("running: %q / %#v", query, args)
	}
	rows, err := v.DB.QueryContext(ctx, query, args...)
	if err != nil {
		return errors.Wrap(err, "cannot query vendor data")
	}
	defer rows.Close()
	record := struct {
		Version   string    `sql:"version"`
		TS        time.Time `sql:"ts"`
		Owner     string    `sql:"owner"`
		Provider  string    `sql:"provider"`
		CVE       string    `sql:"cve_id"`
		Published time.Time `sql:"published"`
		Modified  time.Time `sql:"modified"`
		BaseScore float64   `sql:"base_score"`
		Summary   string    `sql:"summary"`
	}{}
	cw := csv.NewWriter(w)
	defer cw.Flush()
	if header {
		fields := sqlutil.NewRecordType(record).Fields()
		cw.Write(fields)
	}
	for rows.Next() {
		v := record
		err = rows.Scan(sqlutil.NewRecordType(&v).Values()...)
		if err != nil {
			return errors.Wrap(err, "cannot scan vendor data")
		}
		cw.Write([]string{
			v.Version,
			v.TS.Format(TimeLayout),
			v.Owner,
			v.Provider,
			v.CVE,
			v.Published.Format(TimeLayout),
			v.Modified.Format(TimeLayout),
			strconv.FormatFloat(v.BaseScore, 'f', 3, 64),
			v.Summary,
		})
	}
	return nil
}