Golang SQL error expected 0 arguments got 3 - sql

I'm using github.com/denisenkom/go-mssqldb library and driver but getting an error sql: expected 0 arguments, got 3 exit status 1 when inserting new row.
tsql := "INSERT INTO Uploads (Fname, Fsize, Ftype) VALUES (#Fname, #Fsize, #Ftype );"
fmt.Printf("tsql = %s\n", tsql)
//Execute non-query with named parameters
res, err := db.ExecContext(
ctx,
tsql,
sql.Named("Fname", fname),
sql.Named("Fsize", fsize),
sql.Named("Ftype", ftype))
if err != nil {
log.Fatal(" AddRow_v1() -> Error creating new row: " + err.Error())
return -1, err
}

This issue might be related to driver name used in the connection string.
I've tried the same query with yours, the record is created without any errors.
I believe that you are currently using mssql in connection string; sql.Open("mssql", conn) (This issue has already been discussed in https://github.com/denisenkom/go-mssqldb/issues/594#issuecomment-809922317)
If you try again by replacing "mssql" to "sqlserver", the problem should be solved.

Related

there is a remaining expectation which was not matched: ExpectedQuery => expecting Query, QueryContext or QueryRow which

I have this repository
func (r *WorkspaceRepository) Delete(id any) (bool, error) {
if err := r.db.Delete(&model.Workspace{}, "id = ?", id).Error; err != nil {
return false, err
}
return true, nil
}
Which does a Soft-Delete, the query which this Delete operation runs is this one
UPDATE "workspaces" SET "deleted_at"='2022-07-04 09:09:20.778' WHERE id = 'c4610193-b43a-4ed7-9ed6-9d67b3f97502' AND "workspaces"."deleted_at" IS NULL
My test
uid := uuid.New()
mock.ExpectBegin()
mock.ExpectQuery(regexp.QuoteMeta(`UPDATE "workspaces" SET`)).WithArgs(sqlmock.AnyArg(), uid)
mock.ExpectCommit()
_, err = repository.Delete(uid)
assert.NoError(t, err)
I got the error
workspace_test.go:165:
Error Trace: workspace_test.go:165
Error: Received unexpected error:
call to ExecQuery 'UPDATE "workspaces" SET "deleted_at"=$1 WHERE id = $2 AND "workspaces"."deleted_at" IS NULL' with args [{Name: Ordinal:1 Value:2022-07-04 10:54:16.4863731 -0300 -03} {Name: Ordinal:2 Value:ae4124d9-ed16-4dcf-beb9-fcdcf27ca7da}], was not expected, next expectation is: ExpectedQuery => expecting Query, QueryContext or QueryRow which:
- matches sql: 'UPDATE "workspaces" SET'
- is with arguments:
0 - {}
1 - ae4124d9-ed16-4dcf-beb9-fcdcf27ca7da; call to Rollback transaction, was not expected, next expectation is: ExpectedQuery => expecting Query, QueryContext or QueryRow which:
- matches sql: 'UPDATE "workspaces" SET'
- is with arguments:
0 - {}
1 - ae4124d9-ed16-4dcf-beb9-fcdcf27ca7da
Test: TestDeleteWorkspace
It took me some time to figure out, but again, sqlmock error explains the problem if you know how to read the error message.
In our code, we execute UPDATE statement that triggers Exec db driver command instead of Query that is used for selects.
The error message explains that sqlmock expects code to perform one of these operations:
next expectation is: ExpectedQuery => expecting Query, QueryContext or
QueryRow
We need to use ExpectExec() instead of ExpectQuery() in test to match our code. We also need to define how many records are updated in our mocked DB by adding WillReturnResult(sqlmock.NewResult(1, 1)).
Fix one line and test should pass:
mock.ExpectExec(regexp.QuoteMeta(`UPDATE "workspaces" SET`)).WithArgs(sqlmock.AnyArg(), uid).WillReturnResult(sqlmock.NewResult(1, 1))

PlaceHolderFormat doesn't replace the dollar sign for the parameter value during SQL using pgx driver for postgres

I am new to Go and am trying to check a password against a username in a postgresql database.
I can't get dollar substitution to occur and would rather not resort to concatenating strings.
I am currently using squirrel but also tried it without and didn't have much luck.
I have the following code:
package datalayer
import (
"database/sql"
"encoding/json"
"fmt"
"net/http"
sq "github.com/Masterminds/squirrel"
_ "github.com/jackc/pgx/v4/stdlib"
"golang.org/x/crypto/bcrypt"
"github.com/gin-gonic/gin"
)
var (
// for the database
db *sql.DB
)
func InitDB(sqlDriver string, dataSource string) error {
var err error
// Connect to the postgres db (sqlDriver is literal string "pgx")
db, err = sql.Open(sqlDriver, dataSource)
if err != nil {
panic(err)
}
return db.Ping()
}
// Create a struct that models the structure of a user, both in the request body, and in the DB
type Credentials struct {
Password string `json:"password", db:"password"`
Username string `json:"username", db:"username"`
}
func Signin(c *gin.Context) {
// Parse and decode the request body into a new `Credentials` instance
creds := &Credentials{}
err := json.NewDecoder(c.Request.Body).Decode(creds)
if err != nil {
// If there is something wrong with the request body, return a 400 status
c.Writer.WriteHeader(http.StatusBadRequest)
return
}
query := sq.
Select("password").
From("users").
Where("username = $1", creds.Username).
PlaceholderFormat(sq.Dollar)
// The line below doesn't substitute the $ sign, it shows this: SELECT password FROM users WHERE username = $1 [rgfdgfd] <nil>
fmt.Println(sq.
Select("password").
From("users").
Where("username = $1", creds.Username).
PlaceholderFormat(sq.Dollar).ToSql())
rows, sqlerr := query.RunWith(db).Query()
if sqlerr != nil {
panic(fmt.Sprintf("QueryRow failed: %v", sqlerr))
}
if err != nil {
// If there is an issue with the database, return a 500 error
c.Writer.WriteHeader(http.StatusInternalServerError)
return
}
// We create another instance of `Credentials` to store the credentials we get from the database
storedCreds := &Credentials{}
// Store the obtained password in `storedCreds`
err = rows.Scan(&storedCreds.Password)
if err != nil {
// If an entry with the username does not exist, send an "Unauthorized"(401) status
if err == sql.ErrNoRows {
c.Writer.WriteHeader(http.StatusUnauthorized)
return
}
// If the error is of any other type, send a 500 status
c.Writer.WriteHeader(http.StatusInternalServerError)
return
}
// Compare the stored hashed password, with the hashed version of the password that was received
if err = bcrypt.CompareHashAndPassword([]byte(storedCreds.Password), []byte(creds.Password)); err != nil {
// If the two passwords don't match, return a 401 status
c.Writer.WriteHeader(http.StatusUnauthorized)
}
fmt.Printf("We made it !")
// If we reach this point, that means the users password was correct, and that they are authorized
// The default 200 status is sent
}
I see the following when I check pgAdmin, which shows the dollar sign not being substituted:
The substitution of the placeholders is done by the postgres server, it SHOULD NOT be the job of the Go code, or squirrel, to do the substitution.
When you are executing a query that takes parameters, a rough outline of what the database driver has to do is something like the following:
Using the query string, with placeholders untouched, a parse request is sent to the postgres server to create a prepared statement.
Using the parameter values and the identifier of the newly-created statement, a bind request is sent to make the statement ready for execution by creating a portal. A portal (similar to, but not the same as, a cursor) represents a ready-to-execute or already-partially-executed statement, with any missing parameter values filled in.
Using the portal's identifier an execute request is sent to the server which then executes the portal's query.
Note that the above steps are just a rough outline, in reality there are more request-response cycles involved between the db client and server.
And as far as pgAdmin is concerned I believe what it is displaying to you is the prepared statement as created by the parse request, although I can't tell for sure as I am not familiar with it.
In theory, a helper library like squirrel, or a driver library like pgx, could implement the substitution of parameters themselves and then send a simple query to the server. In general, however, given the possibility of SQL injections, it is better to leave it to the authority of the postgres server, in my opinion.
The PlaceholderFormat's job is to simply translate the placeholder to the specified format. For example you could write your SQL using the MySQL format (?,?,...) and then invoke the PlaceholderFormat(sql.Dollar) method to translate that into the PostgreSQL format ($1,$2,...).

How to get detailed error msg from monetdbe_query

Looking at the C examples for monetdbe_query in the GitHub repository: https://github.com/MonetDBSolutions/monetdbe-examples
The prescribed way to run queries is to run SQL queries in an IF statement that captures Null return values as shown below:
if (monetdbe_query(db, "SELECT * FROM mystrings", &result, NULL) != NULL) {
fprintf(stderr, "Failed to run select query\n");
goto cleanup;
}
is it possible to get any detailed information regarding why the SQL statement failed?
Many monetdbe_ functions return an error message if something went wrong, or NULL otherwise. So, just catch the return value. See the example copy_into.c in monetdbe-examples:
if((err=monetdbe_query(db, sql, NULL, NULL)) != NULL) {
printf("%s\r\n", err);
return -1;
}
grep for err in the examples for more.

SqlCommand.ExecuteReader Fails to Report Errors

I'm executing a SQL command to create a new record in a database table and get the ID of the created record. However, there's a constraint error generated by the SQL command (uninitialized non-null field) which is not being picked up by the VB code. The code is roughly:-
connection = New SqlConnection(connection_string)
connection.Open()
sql_command = New SqlCommand(command) 'command = the SQL command to execute
sql_command.Connection = connection
sql_command.Parameters.AddRange(sql_parameters.ToArray()) ' sql_parameters is a parameter to the function
reader = sql_command.ExecuteReader()
If reader IsNot Nothing Then
If reader.HasRows Then
While reader.Read
response_handler(reader, data) 'response handler is a callback which populates the data object
End While
End If
reader.Close()
End If
The reader object is non-null but contains no data and no exception is generated. The SQL command is:-
insert into [table] ([column1], [column2], [column3], [column4])
output Inserted.[pk]
values (#1, #2, #3, #4)
Executing the SQL statement using SQL Server Management Studio, I get the error:-
Msg 515, Level 16, State 2, Line 2
Cannot insert the value NULL into column 'somecolumn', table 'tablename'; column does not allow nulls. INSERT fails.
I have also added a handler for the InfoMessage event on the SqlConnection object but that doesn't get called, even when I set FireInfoMessageEventOnUserErrors to true.
Why am I not getting an error and what is the correct way to ensure the error is reported to VB?
I'm using Visual Studio 2008.
The HasRows call returns false if there is an error. That way you will never see the error. Remove both If statements. The null check is redundant, the other one suppresses errors.
The severity level is 16 which does not interrupt the current session. If you want to throw a hard error you could add something like this directly after your insert...
If ##Error <> 0
Begin
Raiserror('Constraint Error Encountered',20,1) With Log;
End
The security context will have to have sysadmin rights in order to perform the RAISERROR WITH LOG, but if you can't do this I'm sure there are other ways to throw a hard error. In any event a warning isn't going to throw an error to your VB code.
I don't know exactly how it works internally, but the property HasRows is set by a private method within the SqlDataReader class called something like TryGetNextResult, which uses a lot of try/catch blocks to set a boolean output parameter, so this method never throws any exception, or even provides any feedback about any errors. What I can't work out is exactly how it realises there will be errors without even attempting the insert, but it does.
With this sample table:
CREATE TABLE T (ID INT IDENTITY, A INT NOT NULL);
I ran:
string sql = #"INSERT T (A) OUTPUT inserted.ID, inserted.A VALUES (1);";
using (var connection = new SqlConnection(connectionString))
using (var command = new SqlCommand(sql, connection))
{
connection.Open();
using (var reader = command.ExecuteReader())
{
Console.WriteLine(reader.HasRows); // True
while (reader.Read())
{
Console.WriteLine(reader.GetInt32(1)); // 1
}
}
}
When I changed this to try and insert null, reader.HasRows was false, but by putting a breakpoint in I was able to test before reader.Read() was called, and this shows that the identity value of the table T was unchanged, so the reader can't be validating inserts by simply rolling back transactions. It is a mystery to me why a SqlDataReader is able to identify that this insert will fail before execution, but yet if the SQL is executed it will still attempt the insert before raising an error.
To further prove the behaviour I ran a similar test with a simple select command, and saw the same behaviour:
static void Main(string[] args)
{
string sql = #" SELECT Date = CAST(D AS DATE)
FROM (VALUES
(1, '20130129'),
(2, '20130130'),
(3, '20130131'),
(4, '20130132')
) t (A, D)
ORDER BY A;";
using (var connection = new SqlConnection(connectionString))
using (var command = new SqlCommand(sql, connection))
{
connection.Open();
using (var reader = command.ExecuteReader())
{
Console.WriteLine(reader.HasRows); // false
while (reader.Read()) // Exception
{
Console.WriteLine(reader.GetString(0));
}
}
}
}
Again this shows, that somehow the SqlDataReader has picked up that there will be an error casting 20130132 to a date. If I remove this row from the SQL the HasRows property becomes true.
I realise this doesn't actually answer your question but it was too long for a comment, hopefully it will help a little, and/or maybe prompt somebody who knows a lot more about c# than me to add their own answer.

java.sql.SQLException: operation cannot be mixed with Oracle-style batching

I'm not really sure about the mixing JDBC and Oracle batching... I read that I can't mix it on one instance of preparedStatment ... because of: Oracle Update Batching Models - Using both batching models in same application
But I also find something saying that it can't be mixed even in a single application... http://docs.oracle.com/cd/B10500_01/java.920/a96654/oraperf.htm
So know I'm not sure where the problem is...
I create a new prepared statment in each call of function:
public void functionCall(int Id)
PreparedStatement insStm = null;
String featureName = "";
String featureTypeName = "";
String sql = "BEGIN insert into " + TABLE_FEATURE_INSET + " values (?, ?, ?, ?); EXCEPTION WHEN others THEN IF SQLCODE != -1 THEN RAISE; END IF; END;";
insStm = getConnection().prepareStatement(sql);
for(xxx)
for (yyy) {
for (zzz) {
insStm.setObject(1, TypeName);
insStm.setObject(2, tableName);
insStm.setObject(3, sourceId);
insStm.setObject(4, Id);
insStm.addBatch();
}
}
// per feature
insStm.executeBatch();
}
statement .close();
}
And then I sometimes get an error...:
Caused by: java.sql.SQLException: operation not allowed: operation
cannot be mixed with Oracle-style batching
Maybe is problem that prepareStatment can be from same connection? I'm really not sure about this.
Can somebody help me? Thanks
EDIT:
That error is caused by this call: insStm.addBatch();