Skip to content

Commit

Permalink
Merge pull request #890 from go-kivik/moreAttsSince
Browse files Browse the repository at this point in the history
More sqlite progress
  • Loading branch information
flimzy authored Feb 19, 2024
2 parents cbe4d27 + 34ce793 commit 23218f2
Show file tree
Hide file tree
Showing 5 changed files with 452 additions and 6 deletions.
4 changes: 0 additions & 4 deletions x/sqlite/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ type db struct {

var _ driver.DB = (*db)(nil)

func (db) AllDocs(context.Context, driver.Options) (driver.Rows, error) {
return nil, nil
}

func (db) CreateDoc(context.Context, interface{}, driver.Options) (string, string, error) {
return "", "", nil
}
Expand Down
5 changes: 5 additions & 0 deletions x/sqlite/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,11 @@ func (d *db) conflicts(ctx context.Context, tx *sql.Tx, id string, r revision, d
// getAttachments returns the attachments for the given docID and revision.
// It may return nil if there are no attachments.
func (d *db) getAttachments(ctx context.Context, tx *sql.Tx, id string, rev revision, includeAttachments bool, since []string) (*attachments, error) {
for _, s := range since {
if _, err := parseRev(s); err != nil {
return nil, err
}
}
args := []interface{}{id, rev.rev, rev.id, includeAttachments}
for _, s := range since {
args = append(args, s)
Expand Down
56 changes: 54 additions & 2 deletions x/sqlite/get_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -999,10 +999,62 @@ func TestDBGet(t *testing.T) {
},
},
},
{
name: "atts_since with invalid rev format",
setup: func(t *testing.T, d driver.DB) {
_, err := d.Put(context.Background(), "foo", map[string]interface{}{
"foo": "aaa",
"_attachments": map[string]interface{}{
"att.txt": map[string]interface{}{
"content_type": "text/plain",
"data": "YXR0LnR4dA==",
},
},
}, mock.NilOption)
if err != nil {
t.Fatal(err)
}
},
id: "foo",
options: kivik.Param("atts_since", []string{"this is an invalid rev"}),
wantStatus: http.StatusBadRequest,
wantErr: `strconv.ParseInt: parsing "this is an invalid rev": invalid syntax`,
},
{
name: "atts_since with non-existent rev",
setup: func(t *testing.T, d driver.DB) {
_, err := d.Put(context.Background(), "foo", map[string]interface{}{
"foo": "aaa",
"_attachments": map[string]interface{}{
"att.txt": map[string]interface{}{
"content_type": "text/plain",
"data": "YXR0LnR4dA==",
},
},
}, mock.NilOption)
if err != nil {
t.Fatal(err)
}
},
id: "foo",
options: kivik.Param("atts_since", []string{"1-asdfasdf"}),
wantDoc: map[string]interface{}{
"_id": "foo",
"_rev": "1-5f3e7150f872a1dd295f44b1e4a9fa41",
"foo": "aaa",
"_attachments": map[string]interface{}{
"att.txt": map[string]interface{}{
"content_type": "text/plain",
"digest": "md5-a4NyknGw7YOh+a5ezPdZ4A==",
"revpos": float64(1),
"length": float64(7),
"stub": true,
},
},
},
},
/*
TODO:
atts_since with invalid rev
atts_since with non-existent rev
att_encoding_info = true
open_revs = [] // TODO: driver.OpenRever
*/
Expand Down
110 changes: 110 additions & 0 deletions x/sqlite/views.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

package sqlite

import (
"bytes"
"context"
"database/sql"
"encoding/json"
"fmt"
"io"

"github.com/go-kivik/kivik/v4/driver"
)

func (d *db) AllDocs(ctx context.Context, options driver.Options) (driver.Rows, error) {
opts := map[string]interface{}{}
options.Apply(opts)

optIncludeDocs, _ := opts["include_docs"].(bool)

query := fmt.Sprintf(`
WITH RankedRevisions AS (
SELECT
rev.id AS id,
rev.rev || '-' || rev.rev_id AS rev,
IIF($1, doc.doc, NULL) AS doc,
doc.deleted AS deleted,
ROW_NUMBER() OVER (PARTITION BY rev.id ORDER BY rev.rev DESC, rev.rev_id DESC) AS rank
FROM %[1]q AS rev
LEFT JOIN %[1]q AS child ON rev.id = child.id AND rev.rev = child.parent_rev AND rev.rev_id = child.parent_rev_id
JOIN %[2]q AS doc ON rev.id = doc.id AND rev.rev = doc.rev AND rev.rev_id = doc.rev_id
WHERE child.id IS NULL
AND NOT doc.deleted
)
SELECT
id,
rev,
doc
FROM RankedRevisions
WHERE rank = 1
AND NOT deleted
`, d.name+"_revs", d.name)
results, err := d.db.QueryContext(ctx, query, optIncludeDocs) //nolint:rowserrcheck // Err checked in Next
if err != nil {
return nil, err
}

return &rows{rows: results}, nil
}

type rows struct {
rows *sql.Rows
}

var _ driver.Rows = (*rows)(nil)

func (r *rows) Next(row *driver.Row) error {
if !r.rows.Next() {
if err := r.rows.Err(); err != nil {
return err
}
return io.EOF
}
var doc []byte
if err := r.rows.Scan(&row.ID, &row.Rev, &doc); err != nil {
return err
}
var buf bytes.Buffer
_ = json.NewEncoder(&buf).Encode(map[string]interface{}{"value": map[string]string{"rev": row.Rev}})
row.Value = &buf
if doc != nil {
toMerge := map[string]interface{}{
"_id": row.ID,
"_rev": row.Rev,
}
doc, err := mergeIntoDoc(doc, toMerge)
if err != nil {
return err
}
row.Doc = bytes.NewReader(doc)
}
return nil
}

func (r *rows) Close() error {
return r.rows.Close()
}

func (r *rows) UpdateSeq() string {
return ""
}

func (r *rows) Offset() int64 {
return 0
}

func (r *rows) TotalRows() int64 {
return 0
}
Loading

0 comments on commit 23218f2

Please sign in to comment.