Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ Cross-compiling **to** darwin from anything that is not darwin is the one direct
## What you get

- `context.Context` first on every call that can block. Cancelling it calls into the engine's interrupt, and the failure that comes back answers `errors.Is` against both `context.Canceled` and `zu.Interrupted`, because a caller who wrote the deadline and a caller handling the failure ask different questions of the same error. A context that can never be cancelled starts no goroutine.
- `errors.Is` and `errors.As` against `*zu.Error`, which carries `Code`, `Message`, `StandardText`, `DocURL`, `Severity`, `Retryable`, `Position` and the source line the position points into. The statuses are sentinels of their own: `errors.Is(err, zu.Conflict)` is the retry question and needs no unwrapping.
- `errors.Is` and `errors.As` against `*zu.Error`, which carries `Code`, `Message`, `StandardText`, `DocURL`, `Severity`, `Retryable`, `Position` and the source line the position points into. It also carries the rest of the diagnostic record ISO 39075 subclause 23.2 asks for: `SubjectKind` and `Subject` are what the condition is about when it is about something the statement named, and `Graph` and `Schema` are where the statement was running. A program that highlights the offending label in an editor reads the name off the record rather than out of an English sentence. The statuses are sentinels of their own: `errors.Is(err, zu.Conflict)` is the retry question and needs no unwrapping.
- Range-over-func iteration: `rows.All()` is an `iter.Seq[Row]`, and `zu.Iter[T]` is an `iter.Seq2[T, error]` that streams into your own type.
- Three levels of reading, in the order you reach for them: `Scan` into concrete destinations, `Collect[T]` and `Iter[T]` into a struct matched by `zu` tags or by name, and `Int64s`, `Float64s`, `NodeOffsets` and `Valid` for a whole column borrowed from the result without a copy.
- The seven temporal types spelled out rather than flattened into `time.Time`. A date is a `zu.Date`, a time of day is a `zu.LocalTime`, and a year-month duration is a `zu.YearMonth`, because a `time.Time` made out of a time of day is a date somebody invented. The three that name an instant scan into a `time.Time` when you ask for one.
Expand Down
4 changes: 4 additions & 0 deletions api/surface.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,16 @@ pkg github.com/tamnd/zu-go, field Date.Days int32
pkg github.com/tamnd/zu-go, field Error.Code string
pkg github.com/tamnd/zu-go, field Error.DocURL string
pkg github.com/tamnd/zu-go, field Error.Excerpt string
pkg github.com/tamnd/zu-go, field Error.Graph string
pkg github.com/tamnd/zu-go, field Error.Message string
pkg github.com/tamnd/zu-go, field Error.Position Position
pkg github.com/tamnd/zu-go, field Error.Retryable bool
pkg github.com/tamnd/zu-go, field Error.Schema string
pkg github.com/tamnd/zu-go, field Error.Severity Severity
pkg github.com/tamnd/zu-go, field Error.StandardText string
pkg github.com/tamnd/zu-go, field Error.Status Status
pkg github.com/tamnd/zu-go, field Error.Subject string
pkg github.com/tamnd/zu-go, field Error.SubjectKind string
pkg github.com/tamnd/zu-go, field LocalDateTime.Nanos int64
pkg github.com/tamnd/zu-go, field LocalTime.Nanos int64
pkg github.com/tamnd/zu-go, field Node.Offset uint64
Expand Down
10 changes: 5 additions & 5 deletions bytes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,13 @@ func TestACharacterStringScansIntoOctetsBecauseThatIsWhatItIs(t *testing.T) {
// is whether the slice is nil.
func TestAByteStringOfNoOctetsIsNotTheSameAsNoByteString(t *testing.T) {
conn := memory(t)
rows := query(t, conn, `RETURN X'' AS empty, null AS nothing`)
rows := query(t, conn, `RETURN X'' AS empty, null AS missing`)
if !rows.Next() {
t.Fatal("no row")
}

var empty, nothing []byte
if err := rows.Scan(&empty, &nothing); err != nil {
var empty, missing []byte
if err := rows.Scan(&empty, &missing); err != nil {
t.Fatalf("scanning the two: %v", err)
}
if empty == nil {
Expand All @@ -116,8 +116,8 @@ func TestAByteStringOfNoOctetsIsNotTheSameAsNoByteString(t *testing.T) {
if len(empty) != 0 {
t.Errorf("X'' came back as %#v", empty)
}
if nothing != nil {
t.Errorf("a null came back as %#v rather than as nil", nothing)
if missing != nil {
t.Errorf("a null came back as %#v rather than as nil", missing)
}

// And through the untyped read, where the two are a []byte and an
Expand Down
8 changes: 7 additions & 1 deletion corpus/runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,14 @@ func TestEveryWayACaseCanFailIsReportedInFull(t *testing.T) {
// A case the engine has not caught up to is unsupported and not a
// failure, which is what lets the corpus be the contract and the engine
// catch up to it. The two classes that say so are 42 and 0A.
//
// The statement has to be one the engine really has not reached, and a
// test like this is a canary by construction: the day CREATE lands, this
// stops testing what it says it tests and has to pick another spelling.
// It was SELECT before, which the engine now parses.
func TestACaseAheadOfTheEngineIsUnsupportedAndNotAFailure(t *testing.T) {
got := only(t, " - name: one\n doc: d\n query: SELECT 1\n columns:\n - n\n"+
got := only(t, " - name: one\n doc: d\n query: CREATE NODE TABLE person(uid INT64)\n"+
" columns:\n - n\n"+
" rows:\n")
if got.Outcome != Unsupported {
t.Errorf("came to %s: %s", got.Outcome.mark(), got.Detail)
Expand Down
24 changes: 24 additions & 0 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,26 @@ type Error struct {
// under a caret, since a line cut to fit would put the column
// somewhere it is not.
Excerpt string
// SubjectKind is what kind of thing the condition is about, when
// it is about something the statement named: one lower case word
// out of graph, schema, label, property, variable, type and
// function. It is kept apart from Subject rather than glued to the
// front of it, so that asking whether a failure is about a label
// is one string compared against one word.
SubjectKind string
// Subject is the name the condition is about, written the way the
// statement wrote it and with nothing around it, which is what an
// editor underlines. It and SubjectKind are both empty or both
// set.
Subject string
// Graph is the graph the statement was running in, which ISO 39075
// subclause 23.2 asks a diagnostic record to name. It is empty
// when the failure happened before there was one, which is the
// case for a connection that never opened.
Graph string
// Schema is the schema the statement was running in, empty on the
// same terms as Graph.
Schema string

// cause is the context error behind an interruption, so that a
// query stopped by a cancelled context answers to
Expand Down Expand Up @@ -284,6 +304,10 @@ func take(status C.zu_status, handle *C.zu_error) *Error {
e.StandardText = text(C.zu_error_standard_text(handle, &n), n)
e.DocURL = text(C.zu_error_doc_url(handle, &n), n)
e.Excerpt = text(C.zu_error_excerpt(handle, &n), n)
e.SubjectKind = text(C.zu_error_subject_kind(handle, &n), n)
e.Subject = text(C.zu_error_subject(handle, &n), n)
e.Graph = text(C.zu_error_graph(handle, &n), n)
e.Schema = text(C.zu_error_schema(handle, &n), n)
e.Severity = Severity(C.zu_error_severity(handle))
e.Retryable = C.zu_error_retryable(handle) == 1

Expand Down
8 changes: 4 additions & 4 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,20 +113,20 @@ func ExampleNamed() {

// Parameters are named rather than positional, so there is no order
// to get wrong. The dollar may be written or left off.
rows, err := conn.Query(ctx, `RETURN $a + $b AS sum`,
rows, err := conn.Query(ctx, `RETURN $a + $b AS total`,
zu.Named("a", 40), zu.Named("b", 2))
if err != nil {
log.Fatal(err)
}
defer rows.Close()

var sum int64
var total int64
if rows.Next() {
if err := rows.Scan(&sum); err != nil {
if err := rows.Scan(&total); err != nil {
log.Fatal(err)
}
}
fmt.Println(sum)
fmt.Println(total)
// Output: 42
}

Expand Down
19 changes: 19 additions & 0 deletions include/zu.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,21 @@ const char *zu_version(void);
* is longer than anyone would read under a caret, since a line cut to
* fit would put the column somewhere it is not.
*
* zu_error_subject_kind and zu_error_subject are what the condition is
* about, when it is about something the statement named: the kind is
* one lower-case word out of graph, schema, label, property, variable,
* type and function, and the subject is the name itself, written the
* way the statement wrote it and with nothing around it. They are both
* NULL or both set. A binding that highlights the offending label in
* an editor wants the name on its own, and the kind is kept in its own
* accessor rather than glued to the front of it so that asking "is
* this about a label" is one string compared against one word.
*
* zu_error_graph and zu_error_schema are where the statement was
* running, which ISO 39075 subclause 23.2 asks a diagnostic record to
* name. They are NULL when the failure happened before there was one,
* which is the case for a connection that never opened.
*
* The message says all of this in words and keeps saying it, so
* printing it alone is still a complete report. The fields are for the
* caller that would rather underline the token than read the numbers
Expand All @@ -245,6 +260,10 @@ int32_t zu_error_retryable(const zu_error *e); /* -1 for a NULL error */
zu_status zu_error_position(const zu_error *e, uint32_t *line, uint32_t *column);
zu_status zu_error_offset(const zu_error *e, uint32_t *offset);
const char *zu_error_excerpt(const zu_error *e, size_t *len);
const char *zu_error_subject_kind(const zu_error *e, size_t *len);
const char *zu_error_subject(const zu_error *e, size_t *len);
const char *zu_error_graph(const zu_error *e, size_t *len);
const char *zu_error_schema(const zu_error *e, size_t *len);
void zu_error_free(zu_error *e);

/* How a database is opened. The only struct that crosses this boundary
Expand Down
2 changes: 1 addition & 1 deletion lib/darwin-amd64/REVISION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
f0ba1219a26cd735f1da63e3fddaba9cf6370248
a92d9c1b9c511cbff8b49b426ffe04f73f0d96d4
Binary file modified lib/darwin-amd64/libzu.a
Binary file not shown.
2 changes: 1 addition & 1 deletion lib/darwin-arm64/REVISION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
f0ba1219a26cd735f1da63e3fddaba9cf6370248
a92d9c1b9c511cbff8b49b426ffe04f73f0d96d4
Loading
Loading