GORM golang create does not work, getting an mariadb error - go

I'm trying to implement mariadb integration to my todo app, as I'm learning go. I decided to use gorm. The error I'm getting is
2022/10/16 21:47:49 C:/Users/xxx/go/src/go-todo-app/server/main.go:44 Error 1064: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'RETURNING `id`,`id`' at line 1
[0.000ms] [rows:0] INSERT INTO `todos` (`created_at`,`updated_at`,`deleted_at`,`title`,`done`,`body`) VALUES ('2022-10-16 21:47:49.1','2022-10-16 21:47:49.1',NULL,'Testing',0,'Finish Tutorial') RETURNING `id`,`id`
For my http server im using gofiber v2.
app.Post("/api/todos", func(c *fiber.Ctx) error {
todo := &Todo{}
if err := c.BodyParser(todo); err != nil {
return err
}
newTodo := &Todo{
Title: todo.Title,
Body: todo.Body,
Done: 0,
}
db.Create(&newTodo) // fails here
var todos []Todo
db.Find(&todos)
return c.JSON(todos)
})
and my Todo struct looks like this:
type Todo struct {
gorm.Model
ID int `json:"id"`
Title string `json:"title"`
Done int `json:"done"`
Body string `json:"body"`
}

Explicitly disabling the returning in your older MariaDB version will prevent the syntax error:
gorm.Open(mysql.New(mysql.Config{Conn: conn, DisableWithReturning: true}))
The gorm mysql driver shouldn't be enabling WithReturning without a version check. This is a bug that should be reported to hem.

Problem resolved.
The issue was that I had MariaDb installed with Xampp for windows (PHP 7.4). In that version, RETURNING statement is not available for MariaDb. I followed this guide to install MySQL 5.7 instead of MariaDb. How can I change MariaDB to MySQL in XAMPP?

Related

How to render HTML retrived from DB, using Gin Framwork built in Template engine?

A bit over my head here, and the for the life me I could not find suitable solution to my usage case scenario.
I'm currently working on a project where in some scenario, when a user visit a certain page "Using Gin Framework Templates", part of the page content is rendered via data retrieved from the DB.
The issue with this approach is that Gin dose not seam to provide the ability to allow me to render any content Without fully escaping it.
I have developed previously with "PHP Framework", and used its template engine "Balde" I was able to if the need arises-ed to have the option to render HTML directly without escaping by using the following Directive "{!! $variableName !!}".
But in Using Gin, I was not successful into finding any builtin template directive that would allow me to render retrieved HTML content directly from DB.
Hope if anyone could support or provide solution or direction on how to solve this issue.
The following is a could sample and which results I get and which results I hope to get.
The following is a quick example of my current issue:
router := gin.Default()
router.LoadHTMLGlob("templates/*")
router.GET("/", func(c *gin.Context) {
db, err := gorm.Open(mysql.Open(configs.DB_Connection()), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
c.HTML(http.StatusOK, "index.html", gin.H{
"title": "<h1>Hello World</h1>",
})
})
Using the builtin template engine in Gin, I get the following results:
<h1>Hello World</h1>
What I'm hoping to get is:
Hello World
Please note that I'm getting that HTML directly from the DB.
I have managed to find a solution to my use case scenario, based on the following Thread , and my own adjustments to make work with GORM as well.
Solution is:
create the following function:
func getStructFeild(v *migrations.TableName, field string) string {
r := reflect.ValueOf(v)
f := reflect.Indirect(r).FieldByName(field).String()
return f
}
router := gin.Default()
router.LoadHTMLGlob("templates/*")
router.GET("/", func(c *gin.Context) {
db, err := gorm.Open(mysql.Open(configs.DB_Connection()), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
var table migrations.TableName
db.Where("id = ?", 1).Select("column_name").Find(&table)
c.HTML(http.StatusOK, "index.html", gin.H{
"title": template.HTML(getStructFeild(&table, "ModalStructColumnName")),
})
})
and that's it Gin Template is now able fully render the HTML that is retrieved from the DB.
Go has a builtin library for rendering html template but it's syntax might be a little different then others like blade, mustache.

GORM Automigrate on two databases/Shared databases

I am trying to implement very simple shard using GORM and DBResolver
Here is the code for it
func (mysqlDB *MySQLDB) InitDB() {
dsn_master := "root:rootroot#tcp(127.0.0.1:3306)/workspace_master?charset=utf8mb4&parseTime=True&loc=Local"
dsn_shard1 := "root:rootroot#tcp(127.0.0.1:3306)/workspace_shard1?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn_master), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info)})
if err != nil {
log.Println("Connection Failed to Open")
} else {
log.Println("Connection Established")
}
db.Use(dbresolver.Register(dbresolver.Config{
Sources: []gorm.Dialector{mysql.Open(dsn_shard1)}},
&models.WorkspaceGroup{}, "shard1"))
db.AutoMigrate(&models.Workspace{}, &models.WorkspaceMember{})
//db.AutoMigrate(&models.Workspace{}, &models.WorkspaceMember{}, &models.WorkspaceGroup{}, &models.GroupMember{})
db.Clauses(dbresolver.Use("shard1")).AutoMigrate(&models.WorkspaceGroup{}, &models.GroupMember{})
mysqlDB.Database = db
}
I am creating two databases workspace_master and workspace_shard1
Issue is that automigration is not working as expected. Shard Tables are not getting created in respective database. I have tried the commented code as well (automigrate having all the tables and setting db resolver earlier)
Expected Result:
Workspace and WorkspaceMember will get created in workspace_master database
WorkspaceGroup and GroupMember will get created in workspace_shard1 database
Current Result:
All tables are created in workspace_master database
However if I create WorkspaceGroup and GroupMember manually in workspace_shard1, any subsequent queries for create, select, delete etc is going correctly to workspace_shard1. So DBResolver seems to be working as expected.
Only issue is db.AutoMigrate is not working as expected. Can anyone suggest how it can be achieved?

Force error handling in Golang - or disallow ignoring return values

Say I have a func that returns an error:
func conditionalError() error {
if maybe {
return errors.New("here")
}
return nil
}
err := conditionalError()
but if we call it without handling the return value:
conditionalError()
is there a flag to get the compiler to complain, or maybe just an IDE setting in JetBrains or VSCode to warn about it?
There is an app for that! This type of functionality falls in the category of linters.
https://github.com/kisielk/errcheck

Permissions error when trying to connect GCP SQL Database with Golang cloud sql proxy package

I'm trying to connect my go app on my VM (Debain 9) server to my mysql database using the package: github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/dialers/mysql
Looking at connection function:
// Cfg returns the effective *mysql.Config to represent connectivity to the
// provided instance via the given user and password. The config can be
// modified and passed to DialCfg to connect. If you don't modify the returned
// config before dialing, consider using Dial or DialPassword.
func Cfg(instance, user, password string) *mysql.Config {
I assume I put in the instance 'Instance connection name that is found on the GCP instance connection database page:
`
package main
import (
"fmt"
"os"
"github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/dialers/mysql"
)
func main() {
cfg := mysql.Cfg("MyAccount:us-west2:myDatabase", "root", os.Getenv("GCPDBPass"))
db, err := mysql.DialCfg(cfg)
if err != nil {
panic(err)
}
fmt.Print(db)
}
Where I find my instance connection name
I get an error:
panic: ensure that the account has access to "MyAccount" (and make sure there's no typo in that name)
I've hooked up the VM instance IP to the Authorized networks in the Database so I don't know if I'm using the correct instance or user with this package.
In my case the issue was because of the lower version of the "github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/dialers/mysql" package. Upgrading the package using the command go get -v -u github.com/GoogleCloudPlatform/cloudsql-proxy resolved the issue. Below is the code snippet
"database/sql"
_ "github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/dialers/mysql"
connString := "user:password#cloudsql(project_id:region:instance)/dbname?charset=utf8&parseTime=True&loc=Local"
db, err := sql.Open("mysql", connectionString)

Google Cloud Bigtable authentication with Go

I'm trying to insert a simple record as in GoDoc. But this returns,
rpc error: code = 7 desc = "User can't access project: tidy-groove"
When I searched for grpc codes, it says..
PermissionDenied Code = 7
// Unauthenticated indicates the request does not have valid
// authentication credentials for the operation.
I've enabled Big table in my console and created a cluster and a service account and recieved the json. What I'm doing wrong here?
package main
import (
"fmt"
"golang.org/x/net/context"
"golang.org/x/oauth2/google"
"google.golang.org/cloud"
"google.golang.org/cloud/bigtable"
"io/ioutil"
)
func main() {
fmt.Println("Start!")
put()
}
func getClient() *bigtable.Client {
jsonKey, err := ioutil.ReadFile("TestProject-7854ea9op741.json")
if err != nil {
fmt.Println(err.Error())
}
config, err := google.JWTConfigFromJSON(
jsonKey,
bigtable.Scope,
) // or bigtable.AdminScope, etc.
if err != nil {
fmt.Println(err.Error())
}
ctx := context.Background()
client, err := bigtable.NewClient(ctx, "tidy-groove", "asia-east1-b", "test1-bigtable", cloud.WithTokenSource(config.TokenSource(ctx)))
if err != nil {
fmt.Println(err.Error())
}
return client
}
func put() {
ctx := context.Background()
client := getClient()
tbl := client.Open("table1")
mut := bigtable.NewMutation()
mut.Set("links", "maps.google.com", bigtable.Now(), []byte("1"))
mut.Set("links", "golang.org", bigtable.Now(), []byte("1"))
err := tbl.Apply(ctx, "com.google.cloud", mut)
if err != nil {
fmt.Println(err.Error())
}
}
I've solved the problem. It's nothing wrong with the code, but config json itself. So anyone who out there want to authenticate and came here by google search... This code is correct and working perfectly. What I've done wrong is follows.
First I made a service account and got the json. But google warned me that im not an owner of project hence it wont be added to accept list but anyway it let me download the json.
Then I deleted that key from console and requested project owner to create a key for me.
There he has created another key with the same name I given.. And since he's the owner no error/warning msgs displayed and successfully json file was downloaded.
When I tried with that... my question begun. That's when i posted this question.
After that with no solutions. I asked owner to delete that key and create another key but with a different name..
Then it worked! It seems if you try to create a key with non-owner account and then again create with same name ( after deleting original of course ) has no effect. Hope this helps everyone out there :)
Take a look at: helloworld.go or search.go which uses GOOGLE_APPLICATION_CREDENTIALS environment variable.
For most environments, you no longer even need to set GOOGLE_APPLICATION_CREDENTIALS. Google Cloud Platform, Managed VMs or Google App Engine all have the right thing set for you. Your desktop environment will also be correct if you've used gcloud init or it's predecessor gcloud auth login followed by gcloud config set project <projectID>.

Resources