Not() belongs wrong table - go

I am using go version go1.10.3 linux/amd64 and mysql 5.7.
Need to runnable with GORM's docker compose config or please provides your config.
package main
import (
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mssql"
_ "github.com/jinzhu/gorm/dialects/mysql"
_ "github.com/jinzhu/gorm/dialects/postgres"
_ "github.com/jinzhu/gorm/dialects/sqlite"
)
var db *gorm.DB
func init() {
var err error
db, err = gorm.Open("mysql", "gorm:gorm#tcp(localhost:9910)/gorm?charset=utf8&parseTime=True")
if err != nil {
panic(err)
}
db.LogMode(true)
}
type Res {
Id int `gorm:"column:id"`
age int `gorm:"column:age"`
}
func main() {
var result []Res
db.Table("A").Select("A.id,A.age").Joins("left join B on A.id=B.id").
Where("A.age=28").
Not("B.id", []{2,3,4,5}).
Scan(&result)
fmt.Printf("%v", result)
}
The sql log is:
select A.id,A.age from A left join B on A.id=B.id where a.age=28 and A.B.id not in(2,3,4,5)
As can be seen, the not operation is appended for the table A (A.B.id not in ...). How can it be appended to table B (B.id not in ...)?

First off, as per my comment: You're only using mysql as a DB, and yet you're importing all the dialect packages (which do call their respective init functions. These functions register callbacks that are dialect-specific (e.g. the init func in the MsSQL package). Remove all the dialects you're not using from your imports:
// remove lines that I've commented out here...
import (
"github.com/jinzhu/gorm"
// _ "github.com/jinzhu/gorm/dialects/mssql"
_ "github.com/jinzhu/gorm/dialects/mysql"
// _ "github.com/jinzhu/gorm/dialects/postgres"
// _ "github.com/jinzhu/gorm/dialects/sqlite"
)
You could move the NOT IN part of the WHERE clause to the JOIN condition based off of the documentation.
I'd also check any errors that you may encounter, they might give you more debug information on top of the log:
err := db.Table("A").Select("A.id,A.age").
Joins("LEFT JOIN B on A.id = B.id AND B.id NOT IN (?)", []int{2, 3, 4, 5}).
Where("age = ?", 28).
Scan(&result).Error
if err != nil {
fmt.Fatalf("Failed to execute query: %+v", err)
}
fmt.Prinln(result)

resolved
use
Where("B.id not in(?)", [] {2,3,4,5}) instead of Not()
anybody has a better idea?

Related

Gorm delete with clauses sqlmock test

I have a Gorm delete with the returning result:
expirationDate := time.Now().UTC().Add(-(48 * time.hour))
var deletedUsers Users
res := gormDB.WithContext(ctx).
Table("my_users").
Clauses(clause.Returning{Columns: []clause.Column{{Name: "email"}}}).
Where("created_at < ?", expirationDate).
Delete(&deletedUsers)
Now the test with clauses always fails. e.g. :
sqlMock.ExpectExec(`DELETE`)
.WithArgs(expirationDate)
.WillReturnResult(sqlmock.NewResult(1, 1))
Receiving error:
"call to Query 'DELETE FROM "my_users" WHERE created_at < $1 RETURNING "email"' with args [{Name: Ordinal:1 Value:2023-01-18 06:15:34.694274 +0000 UTC}], was not expected, next expectation is: ExpectedExec => expecting Exec or ExecContext which:\n - matches sql: 'DELETE'\n - is with arguments:\n 0 - 2023-01-18 06:15:34.694274 +0000 UTC\n - should return Result having:\n LastInsertId: 1\n RowsAffected: 1"
I tried many other sqlMock expectations, but they have a similar issue.
Also, we don't have a return value in ExpectExec, only in ExpectQuery...
Any chance someone has to test the Gorm query with the Clauses?
I was able to successfully manage what you need. First, let me share the files I wrote, and then I'll walk you through all of the relevant changes. The files are repo.go for production and repo_test.go for the test code.
repo.go
package gormdelete
import (
"context"
"time"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
type Users struct {
Email string
}
func Delete(ctx context.Context, gormDB *gorm.DB) error {
expirationDate := time.Now().UTC().Add(-(48 * time.Hour))
var deletedUsers Users
res := gormDB.WithContext(ctx).Table("my_users").Clauses(clause.Returning{Columns: []clause.Column{{Name: "email"}}}).Where("created_at < ?", expirationDate).Delete(&deletedUsers)
if res.Error != nil {
return res.Error
}
return nil
}
As you didn't provide the full file I tried to guess what was missing.
repo_test.go
package gormdelete
import (
"context"
"database/sql/driver"
"testing"
"time"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/assert"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
// this is taken directly from the docs
// https://github.com/DATA-DOG/go-sqlmock#matching-arguments-like-timetime
type AnyTime struct{}
// Match satisfies sqlmock.Argument interface
func (a AnyTime) Match(v driver.Value) bool {
_, ok := v.(time.Time)
return ok
}
func TestDelete(t *testing.T) {
db, mock, err := sqlmock.New()
if err != nil {
t.Fatalf("an error was not expected: %v", err)
}
conn, _ := db.Conn(context.Background())
gormDb, err := gorm.Open(postgres.New(postgres.Config{
Conn: conn,
}))
row := sqlmock.NewRows([]string{"email"}).AddRow("test#example.com")
mock.ExpectBegin()
mock.ExpectQuery("DELETE FROM \"my_users\" WHERE created_at < ?").WithArgs(AnyTime{}).WillReturnRows(row)
mock.ExpectCommit()
err = Delete(context.Background(), gormDb)
assert.Nil(t, err)
if err = mock.ExpectationsWereMet(); err != nil {
t.Errorf("not all expectations were met: %v", err)
}
}
Here, there are more changes that it's worth mentioning:
I instantiated the AnyTime as per the documentation (you can see the link in the comment).
Again, I guessed the setup of the db, mock, and gormDb but I think it should be more or less the same.
I switch the usage of ExpectExec to ExpectQuery as we'll have back a result set as specified by the Clauses method in your repo.go file.
You've to wrap the ExpectQuery within an ExpectBegin and an ExpectCommit.
Finally, pay attention to the difference in how the driver expects the parameters in the SQL statement. In the production code, you can choose to use ? or $1. However, in the test code, you can only use the ? otherwise it won't match the expectations.
Hope to help a little bit, otherwise, let me know!

Create TimescaleDB hypertable using golang batch query

My golang service needs to create a hypertable dynamically in Timescale DB. I use pgx driver. My code is the following (I removed an error handling):
func (s *Storage) CreateHyperTableBatch(ctx context.Context, tableName string) error {
indexName := "idx_" + tableName
conn, _ := s.pool.Acquire(ctx)
defer conn.Release()
b := pgx.Batch{}
b.Queue(fmt.Sprintf(`create table if not exists public.%s (
ts timestamptz primary key not null,
data jsonb not null
);
`, tableName))
b.Queue(fmt.Sprintf(`create index if not exists public.%s on public.%s using gin (data);`, indexName, tableName))
b.Queue(fmt.Sprintf(`select create_hypertable('%s', 'ts', if_not_exists => true);`, tableName))
batchResult := conn.SendBatch(ctx, &b)
defer func() { _ = batchResult.Close() }()
_, _ = batchResult.Exec()
return nil
}
Exec() returns an error
failed to create tableTwo: failed to run query: ERROR: relation "tabletwo" does not exist (SQLSTATE 42P01)
I added schema name to the query (as you see) but this doesn't help. If I split it into three queries everything works fine
func (s *Storage) CreateHyperTable(ctx context.Context, tableName string) error {
indexName := "idx_" + tableName
conn, _ := s.pool.Acquire(ctx)
defer conn.Release()
_, _ := conn.Exec(ctx, fmt.Sprintf(`create table if not exists %s (
ts timestamptz primary key not null,
data jsonb not null
);
`, tableName))
_, _ := conn.Exec(ctx, fmt.Sprintf(`create index if not exists %s on %s using gin (data);`, indexName, tableName))
_, _ := conn.Exec(ctx, fmt.Sprintf(`select create_hypertable('%s', 'ts', if_not_exists => true);`, tableName))
return nil
}
I suppose that the problem is that timescale db needs a plain table to be created and committed to create a hyper table. Is it right or what's the problem? Did anyone encountered the issue and how did you solve it?
Can't give a direct answer on your pgx question, but yes, you need to create a table first before trying to create a hypertable. So I'd try to do the former as a separate transaction and see if that succeeeds.

GORM Count returns 0

Problem Statement
Problem 1
I am getting a 0 when trying to retrieve the count.
The query expression is perfect, by which I assume that the query being built is correct.
However, the result is 0 irrespective of the query.
Problem 2
I am required to specify the db name in the Table clause as Table(dbname.tbname)
This isn't required in the any other queries and is required only when using Count()
What might I be missing?
package main
import (
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mssql"
_ "github.com/jinzhu/gorm/dialects/mysql"
_ "github.com/jinzhu/gorm/dialects/postgres"
_ "github.com/jinzhu/gorm/dialects/sqlite"
)
var db *gorm.DB
var err error
func main() {
db, err = gorm.Open("mysql", "gorm:gorm#tcp(localhost:9910)/gorm?charset=utf8&parseTime=True")
if err != nil {
panic(err)
}
db.LogMode(true)
var count int
var db = "db"
var tb = "tb"
var columnname = "somecol"
var date = "2014-01-02"
err := db.Table(db+"."+tb).Where("? >= ?", columnname, date+" 00:01:00").Count(&count)
if err != nil {
Error.Println(err.Error.Error())
}
fmt.Println("The Count is \n", count)
}
Update 1
The following works.
But this is as per my understanding using the result as *sql.Row and then retrieving the result using scan.
But I don't understand,why ...Count(&count) is giving a run time error?

golang gocql.NewCluster undefined no field or method

I'm trying to query a test keyspace like:
package main
import "fmt"
import _ "github.com/gocql/gocql"
var (
gocql string
)
func main() {
// connect to the cluster
cluster := gocql.NewCluster("127.0.0.1")
cluster.Keyspace = "dbaccess"
session, _ := cluster.CreateSession()
defer session.Close()
if err := session.Query("SELECT name, age FROM people WHERE name='doug'").Scan(&name, &age); err != nil {
log.Fatal(err)
}
fmt.Println(name, age)
}
But I get an error like:
12: gocql.NewCluster undefined (type string has no field or method NewCluster)
Does that mean it's trying to point to the method in the gocql/gocql folder but can't find it, or is the syntax wrong to import stuff or?
I think your problem is that you are declaring a gocql var as a string here:
var (
gocql string
)
You should just remove this and it should resolve that particular issue.
In addition your import statement:
import _ "github.com/gocql/gocql"
Shouldn't include an underscore (_) since you are explicitly using gocql and not just importing for its side effects.

DB2 AS/400 Querying with Go

I'm trying to connect Golang with an AS/400 DB2 Database, i have iSeries Access Driver Installed on the machine, this is the code by now:
package main
import (
"odbc"
)
func main() {
conn, _ := odbc.Connect("DSN=DSN/SCHEME;UID=USER;PWD=PASS")
stmt, _ := conn.Prepare("SELECT * FROM SCHEME.TABLE")
stmt.Execute()
rows, _ := stmt.FetchAll()
for i, row := range rows {
println(i, row)
}
stmt.Close()
conn.Close()
}
I was able to do this using Python with a DSN-Less configuration on the ODBC driver with something like this:
con = pyodbc.connect('DRIVER=iSeries Access ODBC Driver;SYSTEM=DSN;UID=USR;PWD=PASS;DBQ=PRUEBA')
I tried on Golang with odbc, mgodbc and db2cli, always getting an invalid memory address or nil pointer dereference error.
Any help is appreciated, Thanks!
Update –
bitbucket.org/phiggins/db2cli is now listed on https://github.com/golang/go/wiki/SQLDrivers
We're using bitbucket.org/phiggins/db2cli, which is based off of the greatcode.google.com/p/odbc library, and it's been working great!
package main
import (
"database/sql"
"log"
"time"
_ "bitbucket.org/phiggins/db2cli"
)
func main() {
db, err := sql.Open("db2-cli", "DATABASE=testdb; HOSTNAME=db2.domain.com; PORT=1234; PROTOCOL=TCPIP; UID=user1; PWD=password1;")
if err != nil {
log.Fatalln(err)
}
defer db.Close()
var t time.Time
row := db.QueryRow("SELECT current date FROM sysibm.sysdummy1;")
err = row.Scan(&t)
if err != nil {
log.Fatalln(err)
}
log.Println(t)
}
I managed to get it working, first, you need to install mgodbc go package:
go get bitbucket.org/miquella/mgodbc
To install this package you must have a working MinGW installation with gcc on your path (Windows), if you use Linux you should have gcc by installed by default.
Then, using this code i got it working:
package main
import (
_ "bitbucket.org/miquella/mgodbc"
"fmt"
"os"
"database/sql"
)
var (
db *sql.DB
checkError = func(err error, num int) {
if err != nil {
fmt.Println(err, num)
os.Exit(1)
}
}
)
func main() {
// Replace the DBQ value with the name of your ODBC data source.
db, err := sql.Open("mgodbc", "DRIVER=iSeries Access ODBC Driver;SYSTEM=HOSTNAME;UID=USER;PWD=PASS;DBQ=SCHEMA")
checkError(err,1)
rows, err := db.Query("SELECT * FROM TABLE")
checkError(err,2)
for rows.Next(){
fmt.Println(rows)
}
defer rows.Close()
defer db.Close()
}
The only thing missing here, is that sql Go package doesn't cast the Query to String, then you get weird codes when querying, now i'm looking to solve that.
I'm using Go + https://bitbucket.org/phiggins/db2cli.
This go lib for DB2 is using the DB2 CLI driver, that I found here and configured as described on phiggins page: http://public.dhe.ibm.com/ibmdl/export/pub/software/data/db2/drivers/odbc_cli/
Here is my code:
package main
import (
"fmt"
_ "bitbucket.org/phiggins/db2cli"
"database/sql"
"log"
"time"
)
func main() {
// JDBC conn string = jdbc:as400://127.0.0.1/;libraries=MYCUSTOM,hhpgm,trk35null,GX,TRK35B5ptf,TRK35B5PG,IFWMS,INTERTRK;user=MYUSER;password=MYPASS;naming=system;block criteria="0"
db, err := sql.Open("db2-cli", "DATABASE=MYCUSTOM; HOSTNAME=127.0.0.1; UID=MYUSER; PWD=MYPASS;")
if err != nil {
log.Fatalln(err)
}
defer db.Close()
var t time.Time
row := db.QueryRow("SELECT current date FROM SYSIBM.SYSDUMMY1;")
err = row.Scan(&t)
if err != nil {
log.Fatalln(err)
}
log.Println(t)
}
But I'm not being able to connect.
The error that I'm receiving is:
$ go run main.go
2016/10/27 18:07:46 SQLDriverConnect: {08001} [IBM][CLI Driver] SQL30081N A communication error has been detected. Communication protocol being used: "TCP/IP". Communication API being used: "SOCKETS". Location where the error was detected: "127.0.0.1". Communication function detecting the error: "connect". Protocol specific error code(s): "61", "*", "*". SQLSTATE=08001
exit status 1
I'm on mac, so I identified that the "protocol specific error code" 61 is "#define ECONNREFUSED 61 /* Connection refused */" according to:
http://opensource.apple.com//source/xnu/xnu-1456.1.26/bsd/sys/errno.h
However, I can connect using a SQL client and a JDBC driver.
So, the question is, how can I convert the JDBC connection string to the format expected by the DB2 CLI driver?
JDBC connection string: jdbc:as400://127.0.0.1/;libraries=MYCUSTOM,hhpgm,trk35null,GX,TRK35B5ptf,TRK35B5PG,IFWMS,INTERTRK;user=MYUSER;password=MYPASS;naming=system;block criteria="0"
CLI connection string: "HOSTNAME=127.0.0.1; UID=MYUSER; PWD=MYPASS;"
Vanessa, you might need to define the value of the port for your connection string. I not sure what is the default port value so just put it in there to be safe.
Have you tried the golang driver available for Db2?
https://github.com/ibmdb/go_ibm_db

Resources