Comments (6)
Went ahead and tossed a couple PRs your way, if they suit you. I could go either way between ExpectResults
and ExpectRows
, but ExpectResults
is at least symmetrical with the (newly-exported) ErrNoResults
.
I think they could both be useful, but I can also continue to work around the issue, so please feel free to close if they don't fit the project goals. π«‘
from go-sqlite.
Thanks for the PR. I agree this is a common pattern, but having this in the library seems like it would make application control flow less clear.
I'm going to close #87 and retitle this issue to match new scope, but I'd like to have an example of this pattern in the documentation. Would you want to send a PR over for that?
from go-sqlite.
I'd like to push back a little on this being just a doc change before I give up the ghost.
I see your point wrt control flow; however, I also think that forcing two statements in different scopes to address such a common use case creates friction. In my case, it was exacerbated by the presence of other conveniences that seemed to do what I wanted, but didn't.
To elaborate: I didn't discover that this might bite me until I was writing tests, only to find my zero values untouched, and I ended up beating my head against the wall for a couple hours trying to find a way to do something declarative in ResultFunc
before I dug into the code and realized it wasn't getting called at all. It's true that you can read between the lines in the docs and realize that ResultFunc
won't get called if there are no results, and I might have made that leap sooner but for the presence of the Result*
functions, which I initially assumed were what I should be using as a convenience for the one-result use case, since they consume a *sqlite.Stmt
, and are in the same package. Eventually I realized they serve a different usage pattern entirely with a different level of convenience. I suppose you could argue that a grab-bag of convenience workflows is the whole raison d'Γͺtre for x
packages, but it was confusing.
So, essentially, the gap I was left with was how to use the otherwise elegant pattern of embedding my .sql
files with the convenience of the Result*
functions, which is how #86 got started. Unfortunately, it doesn't actually help me much, since there's no bridge to preparing the statement and binding values as there is with Execute
and friends.
I considered a PR for something like PrepareFS(conn *sqlite.Conn, fsys fs.FS, filename string) (*sqlite.Stmt, error)
, but that's relatively clunky, since it can't slot neatly into a Result*
call, and it loses out on the convenience of ExecOptions
bindings. This is why I opted for the bool field approach in #87, since it's backwards-compatible and had the smallest blast radius.
Another option might be to use a little closure magic to automate the process with a SingleRow
family of functions:
// SingleRow is [Exec], but it returns an error if there is not exactly one result returned.
func SingleRow(conn *sqlite.Conn, query string, opts *ExecOptions) error {
oOpts, gotResult := oneResult(opts)
err := Execute(conn, query, oOpts)
if err != nil {
return err
}
if !gotResult() {
return errNoResults
}
return nil
}
// SingleRowFS is [ExecuteFS], but but it returns an error if there is not exactly one result returned.
func SingleRowFS(conn *sqlite.Conn, fsys fs.FS, filename string, opts *ExecOptions) error {
oOpts, gotResult := oneResult(opts)
err := ExecuteFS(conn, fsys, filename, oOpts)
if err != nil {
return err
}
if !gotResult() {
return errNoResults
}
return nil
}
func oneResult(opts *ExecOptions) (*ExecOptions, func() bool) {
if opts == nil {
opts = &ExecOptions{}
}
if opts.ResultFunc== nil {
opts.ResultFunc=func(*sqlite.Stmt) error { return nil }
}
called := false
opts.ResultFunc=func(stmt *sqlite.Stmt) error {
if called {
return errMultipleResults
}
called = true
return opts.ResultFunc(stmt)
}
return opts, func()bool{return called}
}
from go-sqlite.
In light of the contribution guide rework, now I'm worried I'm being too presumptuous π. If you'd prefer, I can close the PR and move the above to a discussion.
If so, I'll be happy to proceed with the doc PR in the meantime.
from go-sqlite.
Thanks for the detailed experience report, and I'm sorry to hear that the library's behavior led to a frustrating debugging experience. I'm in agreement that this is an area that can be improved, and I'm genuinely appreciative for the conversation and code contribution.
Just to set expectations, I'm currently travelling, so I won't be able to review this for a week or so.
In light of the contribution guide rework, now I'm worried I'm being too presumptuous π.
I reworked the contribution guide mostly to remove the line "this is a project that fills my own needs" because it's definitely grown beyond that. I hope the new guide is not off-putting. I would appreciate any feedback you have on it (feel free to email privately if that's more comfortable to you).
As stated above, I appreciate you reaching out on this issue. I'm in agreement with you that this experience can be improved, but I'm trying to start with the smallest maintenance cost change to address, then working up.
If you'd prefer, I can close the PR and move the above to a discussion.
Yeah, let's start with a discussion. π There's some interesting options here and I don't want you to churn on code until we have more alignment.
from go-sqlite.
As requested! #90
from go-sqlite.
Related Issues (20)
- how to use SetCollation HOT 1
- ability to customize connection creatd by sqlitex.Pool HOT 1
- Support the VFS API HOT 1
- error creating a transaction: sqlitex.Exec: sqlite: clear bindings: interrupted
- Pool.Close blocks indefinitely when a panic happens during iteration of statement rows
- Add a Pool interface? HOT 2
- BlockOnBusy backoff period is long HOT 5
- Undefined symbols in sqlite v.1.29.0 HOT 2
- Support pointer-passing
- JSONB support
- sqlitex.Pool.Get should be able to return an error HOT 3
- Extented error codes aren't reported properly HOT 1
- file:memory does not appear to work HOT 4
- INSERT multiple VALUES? HOT 2
- Stmt.findBindName shoud be public HOT 3
- Unexpected error: "migrate database: sqlite: step: database is locked"
- [UNHELPFUL ERROR MSG] obscure panic on closed connection HOT 1
- Possible to bind arrays, for use in e.g. `IN (?)` queries? HOT 1
- should sqlitex.Save() check *perr for a nil pointer inside the error, not just *perr == nil ? HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. πππ
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google β€οΈ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from go-sqlite.