writer

package
v0.4.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 29, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package writer provides small streaming helpers for exporting Spanner rows using spanvalue formatters.

DelimitedWriter is the primary writer for CSV-style delimited text. NewCSVWriter is a thin helper for the common comma-delimited CSV case, while NewDelimitedWriter accepts an explicit delimiter for TSV and other delimited output. DelimitedWriter and JSONLWriter preserve explicit duplicate column names. Their UnnamedFieldNamer only fills empty column names, and generated names avoid collisions with existing names. DelimitedWriter buffers through encoding/csv, so callers must call Flush after the final write.

Writer streams *spanner.Row values; concrete writers also offer WriteValues, WriteGCVs, and WriteStructValues. FlushWriter adds Flush (required for buffered DelimitedWriter). Flush does not close the underlying io.Writer.

Constructors

NewDelimitedWriter, NewCSVWriter, NewJSONLWriter, and NewSQLInsertWriter accept WithFormatter and schema options below. WithSQLInsertKind selects the INSERT prefix (see Spanner INSERT DML syntax). RowData, FormatDelimitedRow, and FormatJSONLRow format a single row without a writer.

Column names and field types

Each write API either supplies schema on every call or expects it in the writer:

  • WriteRow, WriteValues: column names and GCV types per call; no registration.
  • WriteGCVs: register column names before the first row; types from each GCV.
  • WriteStructValues: register column names and field types before the first row.

Pre-register at construction with With* or later with Prepare* on the concrete writer. Names only: WithColumnNames, PrepareColumnNames. Names and types: WithRowType, WithMetadata, PrepareRowType. WithMetadata uses metadata.GetRowType(); from a cloud.google.com/go/spanner.RowIterator, Metadata is set after the first Next. The first [WriteRow] or [WriteValues] call can also register column names from a row. DelimitedWriter.Prepare, JSONLWriter.Prepare, and SQLInsertWriter.Prepare are deprecated.

Registered schema vs missing schema

Writers distinguish schema that was never registered from a registered schema with zero column names (len(names) == 0):

  • Not registered: no Prepare*, With*, or row has supplied names yet. DelimitedWriter.WriteHeader, DelimitedWriter.Flush (with WithHeader(true)), and DelimitedWriter.WriteGCVs without prior registration return ErrMissingColumnNames.
  • Registered empty: [PrepareRowType] or WithRowType with a nil row type or a row type whose fields slice is empty (for example DML without THEN RETURN: zero rows and zero columns). DelimitedWriter.Flush succeeds and writes no output; DelimitedWriter.WriteHeader is a no-op because there are no column names. A later [WriteRow] still initializes names from the row if one arrives.
  • [PrepareColumnNames] requires at least one column name and returns ErrMissingColumnNames for an empty list. Do not use it when metadata has zero columns; use [PrepareRowType] or WithRowType with nil or an empty fields slice instead (for example DML without THEN RETURN).
  • WithColumnNames with an empty name list is ignored at construction (the writer stays unregistered); it does not return an error because options cannot report one (#95 tracks error-returning options). Prefer [PrepareRowType] for zero-column metadata.

WithMetadata registers the same names and types as WithRowType; call one or the other, not both. Use it when metadata is already available (not iter.Metadata from a cloud.google.com/go/spanner.RowIterator before the first Next). For streaming, call [PrepareRowType] with iter.Metadata.GetRowType() after the first Next when the result may be empty but still has columns (including when Next returns iterator.Done); call DelimitedWriter.Flush after the loop and propagate its error (do not defer Flush—it returns an error). When every query returns at least one row, [WriteRow] registers names from the first row.

DelimitedWriter defaults to a CSV/TSV header once column names are known (WithHeader): before the first data row, or on DelimitedWriter.Flush when no data row was written (including zero-row SELECT when names were registered). WithHeader(false) omits the header. DelimitedWriter.WriteHeader forces the header earlier.

Delimited, JSONL, and SQL encodings differ after spanvalue formats each column.

Compatibility constructors

NewDelimitedWriterWithOptions, NewJSONLWriterWithOptions, and NewSQLInsertWriterWithOptions forward to the primary constructors above.

Index

Constants

View Source
const (
	// Comma is the standard CSV field delimiter. Pass Comma to
	// NewDelimitedWriter for CSV output.
	Comma rune = ','
)

Variables

View Source
var (
	// ErrEmptyTableName reports that SQLInsertWriter.Table is empty.
	ErrEmptyTableName = errors.New("empty table name")
	// ErrEmptyColumnName reports that a SQL writer received an empty column name.
	ErrEmptyColumnName = errors.New("empty column name")
	// ErrNilOutputWriter reports that a writer was constructed without an output.
	ErrNilOutputWriter = errors.New("nil output writer")
	// ErrNilRow reports that WriteRow was called with a nil row.
	ErrNilRow = spanvalue.ErrNilRow
	// ErrMissingColumnNames reports that an operation requires a registered column schema
	// when none was registered yet, or column names/types are insufficient for the write
	// (for example values without names). It is not returned for a registered zero-column
	// schema (see package doc "Registered schema vs missing schema"). [PrepareColumnNames]
	// with an empty name list returns this error; [WithColumnNames] with an empty list is
	// ignored (writer stays unregistered). Use [PrepareRowType] for zero-column result sets.
	ErrMissingColumnNames = errors.New("missing column names")
	// ErrColumnNamesMismatch reports that provided column names differ from initialized schema.
	ErrColumnNamesMismatch = errors.New("column names mismatch")
	// ErrHeaderAfterData reports that DelimitedWriter.WriteHeader was called after data rows were emitted.
	ErrHeaderAfterData = errors.New("header after data")
	// ErrInvalidDelimiter reports that DelimitedWriter received an invalid delimiter.
	ErrInvalidDelimiter = errors.New("invalid delimiter")
	// ErrMissingFieldTypes reports that WriteStructValues requires registered field types.
	ErrMissingFieldTypes = errors.New("missing field types schema")
	// ErrMismatchedStructValueCount reports that WriteStructValues value count does not match the schema.
	ErrMismatchedStructValueCount = errors.New("mismatched struct value count")
)

Functions

func FormatDelimitedRow added in v0.3.2

func FormatDelimitedRow(fc *spanvalue.FormatConfig, row *spanner.Row, delimiter rune) (string, error)

FormatDelimitedRow formats one row as a CSV-style delimited record without a trailing newline. Pass Comma for CSV output.

func FormatDelimitedValues added in v0.3.2

func FormatDelimitedValues(fc *spanvalue.FormatConfig, columnNames []string, values []spanner.GenericColumnValue, delimiter rune) (string, error)

FormatDelimitedValues formats one row represented as column names plus GCV values as a CSV-style delimited record without a trailing newline. Pass Comma for CSV output.

func FormatJSONLRow added in v0.3.2

func FormatJSONLRow(fc *spanvalue.FormatConfig, row *spanner.Row, namer spanvalue.UnnamedFieldNamer) (string, error)

FormatJSONLRow formats one row as a JSON object string without a trailing newline. Callers writing JSONL streams should add the newline at the stream boundary.

func FormatJSONLValues added in v0.3.2

func FormatJSONLValues(fc *spanvalue.FormatConfig, columnNames []string, values []spanner.GenericColumnValue, namer spanvalue.UnnamedFieldNamer) (string, error)

FormatJSONLValues formats one row represented as column names plus GCV values as a JSON object string without a trailing newline. Callers writing JSONL streams should add the newline at the stream boundary.

func RowData added in v0.3.2

func RowData(row *spanner.Row) ([]string, []spanner.GenericColumnValue, error)

RowData extracts column names and GenericColumnValue cells from row.

Types

type DelimitedOption added in v0.3.2

type DelimitedOption interface {
	// contains filtered or unexported methods
}

DelimitedOption configures a DelimitedWriter created by NewDelimitedWriter or NewCSVWriter.

func WithHeader added in v0.3.2

func WithHeader(header bool) DelimitedOption

WithHeader sets whether DelimitedWriter emits a CSV/TSV header (default true). The header is written before the first data row, or on DelimitedWriter.Flush if only names were registered. See DelimitedWriter.WriteHeader to emit it earlier.

type DelimitedWriter added in v0.3.2

type DelimitedWriter struct {
	Formatter *spanvalue.FormatConfig
	// Header enables a header line before the first data row when true (default).
	// See [WithHeader].
	Header bool
	// Set before the first write. Once names have been resolved for the current
	// schema, later changes do not retroactively rewrite cached header names.
	UnnamedFieldNamer spanvalue.UnnamedFieldNamer
	// contains filtered or unexported fields
}

DelimitedWriter writes rows as CSV-style delimited text. Call Flush after the final write. Header controls automatic header output; see WithHeader and DelimitedWriter.WriteHeader.

func NewCSVWriter

func NewCSVWriter(out io.Writer, opts ...DelimitedOption) *DelimitedWriter

NewCSVWriter returns a comma-delimited CSV writer configured by options. It is a thin helper for NewDelimitedWriter(out, Comma, opts...).

func NewDelimitedWriter added in v0.3.1

func NewDelimitedWriter(out io.Writer, delimiter rune, options ...DelimitedOption) *DelimitedWriter

NewDelimitedWriter returns a CSV-style writer using delimiter as the field delimiter and configured by options. Pass Comma for CSV output or '\t' for TSV output. Delimiter must be non-zero and a valid encoding/csv delimiter.

func NewDelimitedWriterWithOptions deprecated added in v0.3.2

func NewDelimitedWriterWithOptions(out io.Writer, delimiter rune, options ...DelimitedOption) *DelimitedWriter

NewDelimitedWriterWithOptions forwards to NewDelimitedWriter.

Deprecated: Use NewDelimitedWriter instead.

func (*DelimitedWriter) Flush added in v0.3.2

func (w *DelimitedWriter) Flush() error

Flush flushes buffered delimited data to the underlying writer. When DelimitedWriter.Header is true, a schema is registered, len(column names) > 0, and no header was written yet, Flush writes the header first (including zero-row SELECT exports). With a registered zero-column schema, Flush succeeds without writing. With no registered schema and DelimitedWriter.Header true, Flush returns ErrMissingColumnNames. Flush does not close the underlying writer.

func (*DelimitedWriter) Prepare deprecated added in v0.3.2

func (w *DelimitedWriter) Prepare(metadata *sppb.ResultSetMetadata) error

Prepare initializes the delimited schema from result-set metadata before the first row is written.

Deprecated: Use DelimitedWriter.PrepareRowType or DelimitedWriter.PrepareColumnNames.

func (*DelimitedWriter) PrepareColumnNames added in v0.4.1

func (w *DelimitedWriter) PrepareColumnNames(names []string) error

PrepareColumnNames registers column names only; same as WithColumnNames for non-empty names. Unlike WithColumnNames, an empty names slice returns ErrMissingColumnNames; for zero-column result sets use DelimitedWriter.PrepareRowType instead.

func (*DelimitedWriter) PrepareRowType added in v0.4.1

func (w *DelimitedWriter) PrepareRowType(rowType *sppb.StructType) error

PrepareRowType registers column names and field types; same as WithRowType. Nil rowType and a row type with no fields both register an empty schema (see package doc).

func (*DelimitedWriter) WriteGCVs added in v0.3.2

func (w *DelimitedWriter) WriteGCVs(values []spanner.GenericColumnValue) error

WriteGCVs writes one row from GCVs; see package doc "Column names and field types".

func (*DelimitedWriter) WriteHeader added in v0.3.2

func (w *DelimitedWriter) WriteHeader() error

WriteHeader writes the CSV/TSV header once; a column schema must already be registered. With zero registered column names (empty row type), WriteHeader succeeds without writing. With no registered schema, it returns ErrMissingColumnNames. When DelimitedWriter.Header is true, DelimitedWriter.Flush also writes a pending header.

func (*DelimitedWriter) WriteRow added in v0.3.2

func (w *DelimitedWriter) WriteRow(row *spanner.Row) error

WriteRow writes one delimited row; see package doc "Column names and field types".

func (*DelimitedWriter) WriteStructValues added in v0.4.1

func (w *DelimitedWriter) WriteStructValues(values []*structpb.Value) error

WriteStructValues writes one row from []*structpb.Value; see package doc "Column names and field types".

func (*DelimitedWriter) WriteValues added in v0.3.2

func (w *DelimitedWriter) WriteValues(columnNames []string, values []spanner.GenericColumnValue) error

WriteValues writes one row from column names and GCVs.

type FlushWriter added in v0.3.2

type FlushWriter interface {
	Writer
	Flusher
}

FlushWriter streams Spanner rows and finalizes any buffered output.

type Flusher added in v0.3.1

type Flusher interface {
	Flush() error
}

Flusher finalizes any buffered output. Flush does not close the underlying io.Writer. DelimitedWriter uses Flush to forward buffered CSV-style data; JSONLWriter and SQLInsertWriter implement it as a no-op so adapters can use one finalize path for all writer implementations.

type JSONLOption added in v0.3.2

type JSONLOption interface {
	// contains filtered or unexported methods
}

JSONLOption configures a JSONLWriter created by NewJSONLWriter.

type JSONLWriter

type JSONLWriter struct {
	Formatter *spanvalue.FormatConfig
	// Set before the first write. Once names have been resolved for the current
	// schema, later changes do not retroactively rewrite cached object keys.
	UnnamedFieldNamer spanvalue.UnnamedFieldNamer
	// contains filtered or unexported fields
}

JSONLWriter writes one JSON object per line.

func NewJSONLWriter

func NewJSONLWriter(out io.Writer, options ...JSONLOption) *JSONLWriter

NewJSONLWriter returns a JSONL writer configured by options.

func NewJSONLWriterWithOptions deprecated added in v0.3.2

func NewJSONLWriterWithOptions(out io.Writer, options ...JSONLOption) *JSONLWriter

NewJSONLWriterWithOptions forwards to NewJSONLWriter.

Deprecated: Use NewJSONLWriter instead.

func (*JSONLWriter) Flush added in v0.3.1

func (w *JSONLWriter) Flush() error

Flush finalizes JSONL output. JSONLWriter is unbuffered, so this is a no-op.

func (*JSONLWriter) Prepare deprecated added in v0.3.2

func (w *JSONLWriter) Prepare(metadata *sppb.ResultSetMetadata) error

Prepare initializes the JSONL schema from result-set metadata before the first row is written. If a schema is already initialized, Prepare verifies that the metadata column names match the existing schema.

Deprecated: Use JSONLWriter.PrepareRowType, JSONLWriter.PrepareColumnNames, WithRowType, WithColumnNames, or WithMetadata.

func (*JSONLWriter) PrepareColumnNames added in v0.4.1

func (w *JSONLWriter) PrepareColumnNames(names []string) error

PrepareColumnNames registers column names; see DelimitedWriter.PrepareColumnNames.

func (*JSONLWriter) PrepareRowType added in v0.4.1

func (w *JSONLWriter) PrepareRowType(rowType *sppb.StructType) error

PrepareRowType registers names and field types; see DelimitedWriter.PrepareRowType. Nil rowType registers an empty schema.

func (*JSONLWriter) WriteGCVs

func (w *JSONLWriter) WriteGCVs(values []spanner.GenericColumnValue) error

WriteGCVs writes one row; see DelimitedWriter.WriteGCVs.

func (*JSONLWriter) WriteRow

func (w *JSONLWriter) WriteRow(row *spanner.Row) error

WriteRow writes one JSONL row. Does not require With* or Prepare*; see DelimitedWriter.WriteRow.

func (*JSONLWriter) WriteStructValues added in v0.4.1

func (w *JSONLWriter) WriteStructValues(values []*structpb.Value) error

WriteStructValues writes one row; see DelimitedWriter.WriteStructValues.

func (*JSONLWriter) WriteValues

func (w *JSONLWriter) WriteValues(columnNames []string, values []spanner.GenericColumnValue) error

WriteValues writes one row; see DelimitedWriter.WriteValues.

type NameOption added in v0.3.2

type NameOption interface {
	DelimitedOption
	JSONLOption
}

NameOption configures field-name handling for delimited and JSONL writers.

func WithUnnamedFieldNamer added in v0.3.2

func WithUnnamedFieldNamer(namer spanvalue.UnnamedFieldNamer) NameOption

WithUnnamedFieldNamer sets the unnamed-field naming policy for delimited and JSONL writers.

type Option added in v0.3.2

type Option interface {
	DelimitedOption
	JSONLOption
	SQLInsertOption
}

Option configures any writer type created by a writer constructor.

func WithColumnNames added in v0.4.1

func WithColumnNames(names []string) Option

WithColumnNames registers column names only and clears any registered field types. An empty names slice is ignored (the writer stays unregistered); use WithRowType for a zero-column result set.

func WithFormatter added in v0.3.2

func WithFormatter(formatter *spanvalue.FormatConfig) Option

WithFormatter sets the FormatConfig used by a writer.

func WithMetadata added in v0.3.2

func WithMetadata(metadata *sppb.ResultSetMetadata) Option

WithMetadata registers names and types from metadata.GetRowType(); same as WithRowType. Other metadata fields are ignored.

func WithRowType added in v0.4.1

func WithRowType(rowType *sppb.StructType) Option

WithRowType registers column names and field types at construction. Nil rowType registers an empty schema (see package doc).

type SQLInsertKind added in v0.4.0

type SQLInsertKind int

SQLInsertKind selects the INSERT statement prefix written by SQLInsertWriter.

Variants follow Spanner GoogleSQL DML: INSERT OR IGNORE skips rows whose primary key already exists; INSERT OR UPDATE inserts or updates by primary key. They cannot be combined with ON CONFLICT in the same statement, and INSERT is not supported in Partitioned DML. See https://cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax .

const (
	// SQLInsert writes plain INSERT INTO statements.
	SQLInsert SQLInsertKind = iota
	// SQLInsertOrIgnore writes INSERT OR IGNORE INTO statements.
	SQLInsertOrIgnore
	// SQLInsertOrUpdate writes INSERT OR UPDATE INTO statements.
	SQLInsertOrUpdate
)

func (SQLInsertKind) String added in v0.4.0

func (k SQLInsertKind) String() string

type SQLInsertOption added in v0.3.2

type SQLInsertOption interface {
	// contains filtered or unexported methods
}

SQLInsertOption configures a SQLInsertWriter created by NewSQLInsertWriter.

func WithSQLInsertKind added in v0.4.0

func WithSQLInsertKind(kind SQLInsertKind) SQLInsertOption

WithSQLInsertKind sets the INSERT statement variant for a SQLInsertWriter.

type SQLInsertWriter

type SQLInsertWriter struct {
	Table     string
	Formatter *spanvalue.FormatConfig
	// contains filtered or unexported fields
}

SQLInsertWriter writes rows as GoogleSQL INSERT statements.

func NewSQLInsertWriter

func NewSQLInsertWriter(out io.Writer, table string, options ...SQLInsertOption) *SQLInsertWriter

NewSQLInsertWriter returns a SQL INSERT writer configured by options.

func NewSQLInsertWriterWithOptions deprecated added in v0.3.2

func NewSQLInsertWriterWithOptions(out io.Writer, table string, options ...SQLInsertOption) *SQLInsertWriter

NewSQLInsertWriterWithOptions forwards to NewSQLInsertWriter.

Deprecated: Use NewSQLInsertWriter instead.

func (*SQLInsertWriter) Flush added in v0.3.1

func (w *SQLInsertWriter) Flush() error

Flush finalizes SQL INSERT output. SQLInsertWriter is unbuffered, so this is a no-op.

func (*SQLInsertWriter) Prepare deprecated added in v0.3.2

func (w *SQLInsertWriter) Prepare(metadata *sppb.ResultSetMetadata) error

Prepare initializes the SQL INSERT schema from result-set metadata before the first row is written. If a schema is already initialized, Prepare verifies that the metadata column names match the existing schema.

Deprecated: Use SQLInsertWriter.PrepareRowType, SQLInsertWriter.PrepareColumnNames, WithRowType, WithColumnNames, or WithMetadata.

func (*SQLInsertWriter) PrepareColumnNames added in v0.4.1

func (w *SQLInsertWriter) PrepareColumnNames(names []string) error

PrepareColumnNames initializes the SQL INSERT schema from column names before the first row is written. See DelimitedWriter.PrepareColumnNames for empty-name behavior.

func (*SQLInsertWriter) PrepareRowType added in v0.4.1

func (w *SQLInsertWriter) PrepareRowType(rowType *sppb.StructType) error

PrepareRowType initializes the SQL INSERT schema from a row type before the first row is written. When the row type comes from a cloud.google.com/go/spanner.RowIterator, use iter.Metadata.GetRowType after the first Next. Nil rowType registers an empty schema; SQLInsertWriter.WriteGCVs still requires at least one column to emit SQL.

func (*SQLInsertWriter) WriteGCVs

func (w *SQLInsertWriter) WriteGCVs(values []spanner.GenericColumnValue) error

func (*SQLInsertWriter) WriteRow

func (w *SQLInsertWriter) WriteRow(row *spanner.Row) error

func (*SQLInsertWriter) WriteStructValues added in v0.4.1

func (w *SQLInsertWriter) WriteStructValues(values []*structpb.Value) error

WriteStructValues writes one row from structpb values using the field-type schema registered by WithRowType, WithMetadata, or [PrepareRowType].

func (*SQLInsertWriter) WriteValues

func (w *SQLInsertWriter) WriteValues(columnNames []string, values []spanner.GenericColumnValue) error

type Writer

type Writer interface {
	WriteRow(row *spanner.Row) error
}

Writer writes Spanner rows to an output stream.

WriteRow supplies column names and values on each call; see package doc "Column names and field types". Writer intentionally models row streaming only. Some concrete writers also implement Flusher; callers that own the full write lifecycle must call Flush after the final row when it is available. Factories that may return a buffered writer should return a concrete type or FlushWriter, not Writer alone.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL