Golang反射用于SQL扫描报告错误。

huangapple go评论85阅读模式
英文:

golang reflect for sql scan report error

问题

/**
-- name是character varying(64)
CREATE TABLE users
(
id bigint NOT NULL,
no character(24),
identity name,
name name,
head character varying(256),
email character varying(256),
site character varying(256),
create_time timestamp without time zone,
city_id bigint,
timezone jsonb,
update_time timestamp without time zone,
CONSTRAINT user__pk PRIMARY KEY (id)
)

spottly=# SELECT "id", "no", "identity", "name", "head", "email", "site", "create_time", "city_id", "update_time", "timezone" FROM "users" WHERE "email" = 'fixopen@yeah.net' LIMIT 1 OFFSET 0;
id | no | identity | name | head | email | site | create_time | city_id | update_time | timezone
----------------+----+----------+---------+----------------------------------------+------------------+----------------------------+-------------+---------+-------------+----------
95083655397376 | | fixopen | fixopen | /uploader/52e2762edf4e633666000867.png | fixopen@yeah.net | http://spottly.com/fixopen | | | |
(1 row)
*/

package main

import (
"database/sql"
//"errors"
"fmt"
_ "github.com/lib/pq"
"log"
"net/url"
"reflect"
"strings"
"time"
)

type User struct {
Id uint64 json:"id"
No *string json:"no"
Identity string json:"identity"
Name string json:"name"
Head url.URL json:"head"
Email *string json:"email"
Site url.URL json:"site"
Create_time time.Time json:"create-time"
City_id *uint64 json:"city-id"
Update_time time.Time json:"update-time"
Timezone *string json:"timezone"
}

func main() {
connectionString := "host=localhost port=5432 user=postgres dbname=spottly password=123456 sslmode=disable"
db, err := sql.Open("postgres", connectionString)
defer db.Close()
if err != nil {
log.Fatal(err)
}

t := reflect.TypeOf(User{})
//u := reflect.New(t).Elem().Interface()
//fmt.Printf("u is %T, %#v\n", u, u)

resultValuePtr := reflect.New(t)
resultValue := resultValuePtr.Elem()
fieldCount := t.NumField()
fields := make([]reflect.StructField, fieldCount)
for i := 0; i < fieldCount; i++ {
	fields[i] = t.Field(i)
}
columns := make([]string, fieldCount)
fieldAddrs := make([]interface{}, fieldCount)
for i := 0; i < fieldCount; i++ {
	columns[i] = strings.ToLower(fields[i].Name)
	fieldAddrs[i] = resultValue.Field(i).Addr().Interface()
}
columnNames := strings.Join(columns, `","`)
command := `SELECT "` + columnNames + `" FROM "users" WHERE "email" = 'fixopen@yeah.net' LIMIT 1 OFFSET 0`
fmt.Printf("查询命令是 %v\n", command)
// fmt.Printf("meta field is %v\n", fields)
// fmt.Printf("field addr is %#v\n", fieldAddrs)
err = db.QueryRow(command).Scan(fieldAddrs...)
if err == nil {
	result := resultValuePtr.Interface()
	fmt.Printf("结果是 %#v\n", result)
} else {
	fmt.Printf("查询错误是 %v\n", err)
	//panic(errors.New("halt"))
}

}

数据库模式和数据显示在注释中。

查询命令是:

SELECT "id", "no", "identity", "name", "head", "email", "site", "create_time", "city_id", "update_time", "timezone" FROM "users" WHERE "email" = 'fixopen@yeah.net' LIMIT 1 OFFSET 0

与注释中相同,此命令使用 psql 执行返回一行数据。

sql Scan 方法执行错误,输出:

查询错误是 sql: Scan error on column index 4: unsupported driver -> Scan pair: []uint8 -> *url.URL

我的 Golang 版本是 go version go1.5.2 darwin/amd64

PostgreSQL 版本是 9.4.2

使用 github.com/lib/pq 访问数据库。

这是数据库驱动程序不支持将字符串列扫描到 url.URL 类型吗?

我该怎么办?

英文:
/**
-- name is character varying(64)
CREATE TABLE users
(
  id bigint NOT NULL,
  no character(24),
  identity name,
  name name,
  head character varying(256),
  email character varying(256),
  site character varying(256),
  create_time timestamp without time zone,
  city_id bigint,
  timezone jsonb,
  update_time timestamp without time zone,
  CONSTRAINT user__pk PRIMARY KEY (id)
)

spottly=# SELECT &quot;id&quot;, &quot;no&quot;, &quot;identity&quot;, &quot;name&quot;, &quot;head&quot;, &quot;email&quot;, &quot;site&quot;, &quot;create_time&quot;, &quot;city_id&quot;, &quot;update_time&quot;, &quot;timezone&quot; FROM &quot;users&quot; WHERE &quot;email&quot; = &#39;fixopen@yeah.net&#39; LIMIT 1 OFFSET 0;
       id       | no | identity |  name   |                  head                  |      email       |            site            | create_time | city_id | update_time | timezone
----------------+----+----------+---------+----------------------------------------+------------------+----------------------------+-------------+---------+-------------+----------
 95083655397376 |    | fixopen  | fixopen | /uploader/52e2762edf4e633666000867.png | fixopen@yeah.net | http://spottly.com/fixopen |             |         |             |
(1 row)
*/

package main

import (
	&quot;database/sql&quot;
	//&quot;errors&quot;
	&quot;fmt&quot;
	_ &quot;github.com/lib/pq&quot;
	&quot;log&quot;
	&quot;net/url&quot;
	&quot;reflect&quot;
	&quot;strings&quot;
	&quot;time&quot;
)

type User struct {
	Id          uint64    `json:&quot;id&quot;`
	No          *string   `json:&quot;no&quot;`
	Identity    string    `json:&quot;identity&quot;`
	Name        string    `json:&quot;name&quot;`
	Head        url.URL   `json:&quot;head&quot;`
	Email       *string   `json:&quot;email&quot;`
	Site        url.URL   `json:&quot;site&quot;`
	Create_time time.Time `json:&quot;create-time&quot;`
	City_id     *uint64   `json:&quot;city-id&quot;`
	Update_time time.Time `json:&quot;update-time&quot;`
	Timezone    *string   `json:&quot;timezone&quot;`
}

func main() {
	connectionString := &quot;host=localhost port=5432 user=postgres dbname=spottly password=123456 sslmode=disable&quot;
	db, err := sql.Open(&quot;postgres&quot;, connectionString)
	defer db.Close()
	if err != nil {
		log.Fatal(err)
	}

	t := reflect.TypeOf(User{})
	//u := reflect.New(t).Elem().Interface()
	//fmt.Printf(&quot;u is %T, %#v\n&quot;, u, u)

	resultValuePtr := reflect.New(t)
	resultValue := resultValuePtr.Elem()
	fieldCount := t.NumField()
	fields := make([]reflect.StructField, fieldCount)
	for i := 0; i &lt; fieldCount; i++ {
		fields[i] = t.Field(i)
	}
	columns := make([]string, fieldCount)
	fieldAddrs := make([]interface{}, fieldCount)
	for i := 0; i &lt; fieldCount; i++ {
		columns[i] = strings.ToLower(fields[i].Name)
		fieldAddrs[i] = resultValue.Field(i).Addr().Interface()
	}
	columnNames := strings.Join(columns, &quot;\&quot;, \&quot;&quot;)
	command := &quot;SELECT \&quot;&quot; + columnNames + &quot;\&quot; FROM \&quot;users\&quot; WHERE \&quot;email\&quot; = &#39;fixopen@yeah.net&#39; LIMIT 1 OFFSET 0&quot;
	fmt.Printf(&quot;query command is %v\n&quot;, command)
	// fmt.Printf(&quot;meta field is %v\n&quot;, fields)
	// fmt.Printf(&quot;field addr is %#v\n&quot;, fieldAddrs)
	err = db.QueryRow(command).Scan(fieldAddrs...)
	if err == nil {
		result := resultValuePtr.Interface()
		fmt.Printf(&quot;result is %#v\n&quot;, result)
	} else {
		fmt.Printf(&quot;Select one error is %v\n&quot;, err)
		//panic(errors.New(&quot;halt&quot;))
	}
}

database schema and data show in comment.

the query command is:

SELECT &quot;id&quot;, &quot;no&quot;, &quot;identity&quot;, &quot;name&quot;, &quot;head&quot;, &quot;email&quot;, &quot;site&quot;, &quot;create_time&quot;, &quot;city_id&quot;, &quot;update_time&quot;, &quot;timezone&quot; FROM &quot;users&quot; WHERE &quot;email&quot; = &#39;fixopen@yeah.net&#39; LIMIT 1 OFFSET 0

same as comment, this command execute with psql return one row data.

sql Scan method execute error, output:

Select one error is sql: Scan error on column index 4: unsupported driver -&gt; Scan pair: []uint8 -&gt; *url.URL

My golang version is go version go1.5.2 darwin/amd64

PostgreSQL version is 9.4.2

use github.com/lib/pq access DB.

It's the db driver not support scan string column to url.URL?

what do I can?

答案1

得分: 1

url.URL没有实现sql.Scanner,因此无法从数据库中进行扫描。你可以创建一个实现了sql.Scanner接口的包装类型:

type sqlURL struct { url.URL }
func (su *sqlURL) Scan(v interface{}) error {
    u, err := url.Parse(string(v.([]byte)))
    if err != nil {
        return err
    }
    *su = sqlURL{*u}
    return nil
}

或者将URL单独扫描为字符串,然后进行解析:

var urlStr string
err = db.QueryRow(command).Scan(/* ... */, &urlStr)
// 检查错误
u, err := url.Parse(urlStr)
// 检查错误
user.Site = *u

以上是翻译的内容。

英文:

url.URL doesn't implement sql.Scanner, so it cannot be scanned from DB. You can either make a wrapper type that does implement sql.Scanner:

type sqlURL struct { url.URL }
func (su *sqlURL) Scan(v interface{}) error {
    u, err := url.Parse(string(v.([]byte)))
    if err != nil {
        return err
    }
    *su = sqlURL{*u}
    return nil
}

or scan the URL into a string separately and then parse it:

var urlStr string
err = db.QueryRow(command).Scan(/* ... */, &amp;urlStr)
// check err
u, err := url.Parse(urlStr)
// check err
user.Site = *u

huangapple
  • 本文由 发表于 2015年12月24日 00:48:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/34440242.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定