TLDR: There's nothing wrong with using db.Query or db.QueryRow with INSERT, UPDATE, or DELETE. It is actually the right tool for the job if your SQL commands contain the RETURNING clause.
2) is, in a way, already answered in the linked tutorial by way of actually excluding your proposed scenario, since it is talking explicitly about cases where you do not care about the returned rows and you therefore choose to assign the returned value to the blank identifier.
What if you don’t care about the result? What if you just want to
execute a statement and check if there were any errors, but ignore the
result? Wouldn’t the following two statements do the same thing?
_, err := db.Exec("DELETE FROM users") // OK
_, err := db.Query("DELETE FROM users") // BAD
The answer is no. They do not do the same thing, and you should never
use Query() like this. The Query() will return a sql.Rows, which
reserves a database connection until the sql.Rows is closed.
The statement "... you should never use Query() like this ..." does not refer to db.Query being used with DELETE, UPDATE, or INSERT, instead it states, and correctly so, that you should never call Query and assign its result to _, i.e. you should never do _, err := db.Query("..., whatever the sql command is.
Note that the same applies to QueryRow, i.e. you should never discard the returned sql.Row value or in any other way omit calling its Scan method. This is because sql.Row is just a simple wrapper around sql.Rows and its Scan method closes the underlying sql.Rows before it returns (link).
1) As far as I understand, all the above still holds even for transactions. If you have an sql.Rows instance, created by an sql.Tx, and that sql.Rows instance hasn't yet been closed, the sql.Tx itself will be prevented from closing (link). That is, make sure that when you use tx.Query you close the returned rows, and when you use tx.QueryRow you call the returned row's Scan method.