Reputation: 11
Using "database/sql
", sql.DB.Exec()
is used for queries that do not return rows (insert, delete, update
) and sql.DB.Query(
) for queries that return rows (selects
).
Assume you have an incoming query string which you wish to execute, however you do not know whether the query intends to return rows.
Can you think of a way to find out whether to use Exec or Query?
Upvotes: 1
Views: 2789
Reputation: 3537
Your question is not that simple as it may seem from the first sight.
Let's clarify the problem:
Question is not about particular database as pgsql/mysql/sqlite/other but for any generic database drive.
So the answer must be in database/sql/driver
.
Let's go further:
sql/driver
. Interfaces: Queryer
/QueryerContext
& Execer
/ExecerContext
Module sql/driver
defines interfaces:
Queryer
/QueryerContext
- for ability to (*DB).Query
Execer
/ExecerContext
- for (*DB).Exec
Pinger
Note that every interface described in documentation as " an optional interface that may be implemented by a Conn"
Which means that it's possible to implement sql driver without Query
and/or Exec
.
Experimental verification
Let's try to implement correct database driver that cannot Query
and Exec
: https://play.golang.org/p/sZiigEghphE
package main
import (
"database/sql"
"database/sql/driver"
"log"
)
type expdrv struct{}
type expconn struct{}
// sql.Driver implementation
func (*expdrv) Open(name string) (driver.Conn, error) {
return &expconn{}, nil
}
// driver.Conn implementation
func (c *expconn) Prepare(query string) (driver.Stmt, error) {
return nil, nil
}
func (c *expconn) Close() error {
return nil
}
func (c *expconn) Begin() (driver.Tx, error) {
return nil, nil
}
func main() {
sql.Register("drvexp", &expdrv{})
log.Printf("Registred drivers: %v\n", sql.Drivers())
db, err := sql.Open("drvexp", "")
log.Printf("sql.Open() success: %v, error: %v", db != nil, err)
log.Println("db.Close() error:", db.Close())
}
2009/11/10 23:00:00 Registred drivers: [drvexp] 2009/11/10 23:00:00 sql.Open() success: true, error: 2009/11/10 23:00:00 db.Ping() success: true 2009/11/10 23:00:00 db.Close() success: true 2009/11/10 23:00:00 db.Ping() result: sql: database is closed
It works!
This Driver
implementation is:
database/sql
standpointQuery
and Exec
(attempt will throw panic)Ping()
Possibility to implement Exec
or Query
independently of each other
Let's add Exec
:
// driver.Result & driver.Execer
type expresult struct{}
func (*expconn) Exec(query string, args []driver.Value) (driver.Result, error) {
return &expresult{}, nil
}
func (*expresult) LastInsertId() (int64, error) {
return 0, nil
}
func (*expresult) RowsAffected() (int64, error) {
return 0, nil
}
will allow:
_, err = db.Exec("BYE")
fmt.Println("db.Exec() success:", err == nil) // Outputs: true
Conclusion
Ability to call Query/QueryContext
and/or Exec/ExecContext
are optional
Behaviour of those methods can vary at great length between drivers
In general case there is no correct answer exists
For particular cases (with specific driver):
db.Query
would work for most casesQuery
will really work without experimental verificationThank you for your attention!
I suppose that's the final answer.
Upvotes: 0
Reputation: 398
In any cases, don't try to parse SQL since it's hacky. Just use Query
if you are not sure, Query
should be able to handle C_UD well and return an empty result set in that case. Both Query
and Exec
should have exactly the same effect, and they only differ in their response. Only use Exec
if the user of your API explicitly does not need the results.
Upvotes: 3