Compare commits

...

27 Commits

Author SHA1 Message Date
9227139cf8 add schema changing feature for gdb 2020-01-07 22:14:32 +08:00
f2190e50b2 improve unit testing for gdb 2020-01-06 20:43:59 +08:00
c4537b4753 improve ghttp.Client.Get 2020-01-06 17:57:07 +08:00
167d58490b Merge pull request #455 from gnever/191229_gfile_add_readline_mst
Add ReadLines and ReadByteLines read file content line by line
2020-01-06 17:52:41 +08:00
d36aceb9f1 Merge pull request #463 from sth4me/patch-1
fix mysql debug time
2020-01-06 17:51:33 +08:00
eb31922124 fix mysql debug time
调试模式时间格式改为毫秒,原来是微秒.
2020-01-06 10:28:34 +08:00
bec9f5a847 add buildin function 'concat' for gview 2020-01-05 19:55:17 +08:00
506552c3a9 example codes update; donator updates 2020-01-04 17:19:50 +08:00
2bacc77224 improve JSON/XML parsing feature for ghttp.Request 2020-01-04 15:35:21 +08:00
bc53f265af improve gconv.Map/String 2020-01-03 20:23:10 +08:00
344f232c36 add Timestamp*Str functions for gtime; improve unit testing cases for gfile 2020-01-02 21:29:06 +08:00
27b677b0c0 improve Map converting feature for gconv; improve package gproc for local shell searching; improve JSON/XML response for ghttp.Response 2020-01-02 19:45:41 +08:00
a5a0e381bd add more examples for ghttp.Server 2020-01-01 16:45:43 +08:00
d528d7f5ab donator updates 2020-01-01 15:24:10 +08:00
821c71bd8d improve parameter parsing feature for ghttp.Request 2020-01-01 15:20:03 +08:00
604a10400d improve parameter parsing feature for ghttp.Request 2020-01-01 14:57:57 +08:00
9219471f67 improve parameter parsing feature for ghttp.Request 2020-01-01 14:26:00 +08:00
fe5d2e5685 improve parameter parsing feature for ghttp.Request 2020-01-01 14:18:00 +08:00
0a89daa513 add more unit testing cases for gdb.TX 2019-12-31 16:02:18 +08:00
d6e6ddf996 Add gfile.ReadLines and gfile.ReadByteLines read file content line by line 2019-12-29 19:24:56 +08:00
5dbda8aedc fix issue in unit testing codes in gfile 2019-12-29 14:15:17 +08:00
134e4cf28f improve gfile.CopyDir function; add Model function for gdb.Model 2019-12-28 13:55:05 +08:00
56a85abef7 add AutoEncode feature for gview 2019-12-26 11:03:59 +08:00
80c6ceaf26 fix issue 437 2019-12-25 21:22:06 +08:00
a10f428715 add Iterator* functions for garray; add ReplaceDir*/ReplaceFile* functions for gfile; remove gfile.Replace/ReplaceFunc functions 2019-12-25 20:56:39 +08:00
597f7468e9 fix issue in prefix feature for method operations of gdb 2019-12-23 23:14:54 +08:00
5db8851213 comment update for gtype 2019-12-20 23:23:50 +08:00
170 changed files with 3029 additions and 9445 deletions

View File

@ -1,7 +1,7 @@
# MySQL数据库配置
[database]
debug = true
# debug = true
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local"
#[database]

View File

@ -1,8 +1,6 @@
package main
import (
"fmt"
"github.com/gogf/gf/database/gdb"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/os/glog"
@ -23,7 +21,7 @@ func main() {
if err != nil {
panic(err)
}
db.SetDebug(true)
//db.SetDebug(false)
glog.SetPath("/tmp")
@ -36,7 +34,4 @@ func main() {
db.Table("user").Data(g.Map{"name": "smith"}).Where("uid=?", 1).Save()
db.PrintQueriedSqls()
fmt.Println(db.GetLastSql())
}

View File

@ -6,6 +6,7 @@ import (
func main() {
db := g.DB()
// 执行3条SQL查询
for i := 1; i <= 3; i++ {
db.Table("user").Where("id=?", i).One()

View File

@ -1,3 +0,0 @@
package article
// Fill with you ideas below.

View File

@ -1,68 +0,0 @@
// ==========================================================================
// This is auto-generated by gf cli tool. You may not really want to edit it.
// ==========================================================================
package article
import (
"database/sql"
"github.com/gogf/gf/database/gdb"
"github.com/gogf/gf/os/gtime"
)
// Entity is the golang structure for table gf_article.
type Entity struct {
Id int `orm:"id,primary" json:"id"` //
CatId int `orm:"cat_id" json:"cat_id"` // 分类ID
Uid int `orm:"uid" json:"uid"` // 用户ID
Title string `orm:"title" json:"title"` // 标题
Content string `orm:"content" json:"content"` // 内容
Order int `orm:"order" json:"order"` // 排序
Brief string `orm:"brief" json:"brief"` // 摘要
Thumb string `orm:"thumb" json:"thumb"` // 缩略图
Tags string `orm:"tags" json:"tags"` // 标签
Referer string `orm:"referer" json:"referer"` // 内容来源
Status int `orm:"status" json:"status"` // 状态\n0: 禁用\n1: 正常
CreateTime *gtime.Time `orm:"create_time" json:"create_time"` // 创建时间
UpdateTime *gtime.Time `orm:"update_time" json:"update_time"` // 修改时间
}
// Article is alias of Entity, which some developers say they just want.
type Article = Entity
// OmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers
// the data and where attributes for empty values.
func (r *Entity) OmitEmpty() *arModel {
return Model.Data(r).OmitEmpty()
}
// Inserts does "INSERT...INTO..." statement for inserting current object into table.
func (r *Entity) Insert() (result sql.Result, err error) {
return Model.Data(r).Insert()
}
// Replace does "REPLACE...INTO..." statement for inserting current object into table.
// If there's already another same record in the table (it checks using primary key or unique index),
// it deletes it and insert this one.
func (r *Entity) Replace() (result sql.Result, err error) {
return Model.Data(r).Replace()
}
// Save does "INSERT...INTO..." statement for inserting/updating current object into table.
// It updates the record if there's already another same record in the table
// (it checks using primary key or unique index).
func (r *Entity) Save() (result sql.Result, err error) {
return Model.Data(r).Save()
}
// Update does "UPDATE...WHERE..." statement for updating current object from table.
// It updates the record if there's already another same record in the table
// (it checks using primary key or unique index).
func (r *Entity) Update() (result sql.Result, err error) {
return Model.Data(r).Where(gdb.GetWhereConditionOfStruct(r)).Update()
}
// Delete does "DELETE FROM...WHERE..." statement for deleting current object from table.
func (r *Entity) Delete() (result sql.Result, err error) {
return Model.Where(gdb.GetWhereConditionOfStruct(r)).Delete()
}

View File

@ -1,362 +0,0 @@
// ==========================================================================
// This is auto-generated by gf cli tool. You may not really want to edit it.
// ==========================================================================
package article
import (
"database/sql"
"github.com/gogf/gf/database/gdb"
"github.com/gogf/gf/frame/g"
"time"
)
// arModel is a active record design model for table gf_article operations.
type arModel struct {
M *gdb.Model
}
var (
// Table is the table name of gf_article.
Table = "gf_article"
// Model is the model object of gf_article.
Model = &arModel{g.DB("default").Table(Table).Safe()}
)
// FindOne is a convenience method for Model.FindOne.
// See Model.FindOne.
func FindOne(where ...interface{}) (*Entity, error) {
return Model.FindOne(where...)
}
// FindAll is a convenience method for Model.FindAll.
// See Model.FindAll.
func FindAll(where ...interface{}) ([]*Entity, error) {
return Model.FindAll(where...)
}
// FindValue is a convenience method for Model.FindValue.
// See Model.FindValue.
func FindValue(fieldsAndWhere ...interface{}) (gdb.Value, error) {
return Model.FindValue(fieldsAndWhere...)
}
// FindCount is a convenience method for Model.FindCount.
// See Model.FindCount.
func FindCount(where ...interface{}) (int, error) {
return Model.FindCount(where...)
}
// Insert is a convenience method for Model.Insert.
func Insert(data ...interface{}) (result sql.Result, err error) {
return Model.Insert(data...)
}
// Replace is a convenience method for Model.Replace.
func Replace(data ...interface{}) (result sql.Result, err error) {
return Model.Replace(data...)
}
// Save is a convenience method for Model.Save.
func Save(data ...interface{}) (result sql.Result, err error) {
return Model.Save(data...)
}
// Update is a convenience method for Model.Update.
func Update(dataAndWhere ...interface{}) (result sql.Result, err error) {
return Model.Update(dataAndWhere...)
}
// Delete is a convenience method for Model.Delete.
func Delete(where ...interface{}) (result sql.Result, err error) {
return Model.Delete(where...)
}
// TX sets the transaction for current operation.
func (m *arModel) TX(tx *gdb.TX) *arModel {
return &arModel{m.M.TX(tx)}
}
// Master marks the following operation on master node.
func (m *arModel) Master() *arModel {
return &arModel{m.M.Master()}
}
// Slave marks the following operation on slave node.
// Note that it makes sense only if there's any slave node configured.
func (m *arModel) Slave() *arModel {
return &arModel{m.M.Slave()}
}
// LeftJoin does "LEFT JOIN ... ON ..." statement on the model.
func (m *arModel) LeftJoin(joinTable string, on string) *arModel {
return &arModel{m.M.LeftJoin(joinTable, on)}
}
// RightJoin does "RIGHT JOIN ... ON ..." statement on the model.
func (m *arModel) RightJoin(joinTable string, on string) *arModel {
return &arModel{m.M.RightJoin(joinTable, on)}
}
// InnerJoin does "INNER JOIN ... ON ..." statement on the model.
func (m *arModel) InnerJoin(joinTable string, on string) *arModel {
return &arModel{m.M.InnerJoin(joinTable, on)}
}
// Fields sets the operation fields of the model, multiple fields joined using char ','.
func (m *arModel) Fields(fields string) *arModel {
return &arModel{m.M.Fields(fields)}
}
// FieldsEx sets the excluded operation fields of the model, multiple fields joined using char ','.
func (m *arModel) FieldsEx(fields string) *arModel {
return &arModel{m.M.FieldsEx(fields)}
}
// Option sets the extra operation option for the model.
func (m *arModel) Option(option int) *arModel {
return &arModel{m.M.Option(option)}
}
// OmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers
// the data and where attributes for empty values.
func (m *arModel) OmitEmpty() *arModel {
return &arModel{m.M.OmitEmpty()}
}
// Filter marks filtering the fields which does not exist in the fields of the operated table.
func (m *arModel) Filter() *arModel {
return &arModel{m.M.Filter()}
}
// Where sets the condition statement for the model. The parameter <where> can be type of
// string/map/gmap/slice/struct/*struct, etc. Note that, if it's called more than one times,
// multiple conditions will be joined into where statement using "AND".
// Eg:
// Where("uid=10000")
// Where("uid", 10000)
// Where("money>? AND name like ?", 99999, "vip_%")
// Where("uid", 1).Where("name", "john")
// Where("status IN (?)", g.Slice{1,2,3})
// Where("age IN(?,?)", 18, 50)
// Where(User{ Id : 1, UserName : "john"})
func (m *arModel) Where(where interface{}, args ...interface{}) *arModel {
return &arModel{m.M.Where(where, args...)}
}
// And adds "AND" condition to the where statement.
func (m *arModel) And(where interface{}, args ...interface{}) *arModel {
return &arModel{m.M.And(where, args...)}
}
// Or adds "OR" condition to the where statement.
func (m *arModel) Or(where interface{}, args ...interface{}) *arModel {
return &arModel{m.M.Or(where, args...)}
}
// Group sets the "GROUP BY" statement for the model.
func (m *arModel) Group(groupBy string) *arModel {
return &arModel{m.M.Group(groupBy)}
}
// Order sets the "ORDER BY" statement for the model.
func (m *arModel) Order(orderBy string) *arModel {
return &arModel{m.M.Order(orderBy)}
}
// Limit sets the "LIMIT" statement for the model.
// The parameter <limit> can be either one or two number, if passed two number is passed,
// it then sets "LIMIT limit[0],limit[1]" statement for the model, or else it sets "LIMIT limit[0]"
// statement.
func (m *arModel) Limit(limit ...int) *arModel {
return &arModel{m.M.Limit(limit...)}
}
// Offset sets the "OFFSET" statement for the model.
// It only makes sense for some databases like SQLServer, PostgreSQL, etc.
func (m *arModel) Offset(offset int) *arModel {
return &arModel{m.M.Offset(offset)}
}
// Page sets the paging number for the model.
// The parameter <page> is started from 1 for paging.
// Note that, it differs that the Limit function start from 0 for "LIMIT" statement.
func (m *arModel) Page(page, limit int) *arModel {
return &arModel{m.M.Page(page, limit)}
}
// Batch sets the batch operation number for the model.
func (m *arModel) Batch(batch int) *arModel {
return &arModel{m.M.Batch(batch)}
}
// Cache sets the cache feature for the model. It caches the result of the sql, which means
// if there's another same sql request, it just reads and returns the result from cache, it
// but not committed and executed into the database.
//
// If the parameter <duration> < 0, which means it clear the cache with given <name>.
// If the parameter <duration> = 0, which means it never expires.
// If the parameter <duration> > 0, which means it expires after <duration>.
//
// The optional parameter <name> is used to bind a name to the cache, which means you can later
// control the cache like changing the <duration> or clearing the cache with specified <name>.
//
// Note that, the cache feature is disabled if the model is operating on a transaction.
func (m *arModel) Cache(expire time.Duration, name ...string) *arModel {
return &arModel{m.M.Cache(expire, name...)}
}
// Data sets the operation data for the model.
// The parameter <data> can be type of string/map/gmap/slice/struct/*struct, etc.
// Eg:
// Data("uid=10000")
// Data("uid", 10000)
// Data(g.Map{"uid": 10000, "name":"john"})
// Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"})
func (m *arModel) Data(data ...interface{}) *arModel {
return &arModel{m.M.Data(data...)}
}
// Insert does "INSERT INTO ..." statement for the model.
// The optional parameter <data> is the same as the parameter of Model.Data function,
// see Model.Data.
func (m *arModel) Insert(data ...interface{}) (result sql.Result, err error) {
return m.M.Insert(data...)
}
// Replace does "REPLACE INTO ..." statement for the model.
// The optional parameter <data> is the same as the parameter of Model.Data function,
// see Model.Data.
func (m *arModel) Replace(data ...interface{}) (result sql.Result, err error) {
return m.M.Replace(data...)
}
// Save does "INSERT INTO ... ON DUPLICATE KEY UPDATE..." statement for the model.
// It updates the record if there's primary or unique index in the saving data,
// or else it inserts a new record into the table.
//
// The optional parameter <data> is the same as the parameter of Model.Data function,
// see Model.Data.
func (m *arModel) Save(data ...interface{}) (result sql.Result, err error) {
return m.M.Save(data...)
}
// Update does "UPDATE ... " statement for the model.
//
// If the optional parameter <dataAndWhere> is given, the dataAndWhere[0] is the updated
// data field, and dataAndWhere[1:] is treated as where condition fields.
// Also see Model.Data and Model.Where functions.
func (m *arModel) Update(dataAndWhere ...interface{}) (result sql.Result, err error) {
return m.M.Update(dataAndWhere...)
}
// Delete does "DELETE FROM ... " statement for the model.
// The optional parameter <where> is the same as the parameter of Model.Where function,
// see Model.Where.
func (m *arModel) Delete(where ...interface{}) (result sql.Result, err error) {
return m.M.Delete(where...)
}
// Count does "SELECT COUNT(x) FROM ..." statement for the model.
// The optional parameter <where> is the same as the parameter of Model.Where function,
// see Model.Where.
func (m *arModel) Count(where ...interface{}) (int, error) {
return m.M.Count(where...)
}
// All does "SELECT FROM ..." statement for the model.
// It retrieves the records from table and returns the result as []*Entity.
// It returns nil if there's no record retrieved with the given conditions from table.
//
// The optional parameter <where> is the same as the parameter of Model.Where function,
// see Model.Where.
func (m *arModel) All(where ...interface{}) ([]*Entity, error) {
all, err := m.M.All(where...)
if err != nil {
return nil, err
}
var entities []*Entity
if err = all.Structs(&entities); err != nil && err != sql.ErrNoRows {
return nil, err
}
return entities, nil
}
// One retrieves one record from table and returns the result as *Entity.
// It returns nil if there's no record retrieved with the given conditions from table.
//
// The optional parameter <where> is the same as the parameter of Model.Where function,
// see Model.Where.
func (m *arModel) One(where ...interface{}) (*Entity, error) {
one, err := m.M.One(where...)
if err != nil {
return nil, err
}
var entity *Entity
if err = one.Struct(&entity); err != nil && err != sql.ErrNoRows {
return nil, err
}
return entity, nil
}
// Value retrieves a specified record value from table and returns the result as interface type.
// It returns nil if there's no record found with the given conditions from table.
//
// If the optional parameter <fieldsAndWhere> is given, the fieldsAndWhere[0] is the selected fields
// and fieldsAndWhere[1:] is treated as where condition fields.
// Also see Model.Fields and Model.Where functions.
func (m *arModel) Value(fieldsAndWhere ...interface{}) (gdb.Value, error) {
return m.M.Value(fieldsAndWhere...)
}
// FindOne retrieves and returns a single Record by Model.WherePri and Model.One.
// Also see Model.WherePri and Model.One.
func (m *arModel) FindOne(where ...interface{}) (*Entity, error) {
one, err := m.M.FindOne(where...)
if err != nil {
return nil, err
}
var entity *Entity
if err = one.Struct(&entity); err != nil && err != sql.ErrNoRows {
return nil, err
}
return entity, nil
}
// FindAll retrieves and returns Result by by Model.WherePri and Model.All.
// Also see Model.WherePri and Model.All.
func (m *arModel) FindAll(where ...interface{}) ([]*Entity, error) {
all, err := m.M.FindAll(where...)
if err != nil {
return nil, err
}
var entities []*Entity
if err = all.Structs(&entities); err != nil && err != sql.ErrNoRows {
return nil, err
}
return entities, nil
}
// FindValue retrieves and returns single field value by Model.WherePri and Model.Value.
// Also see Model.WherePri and Model.Value.
func (m *arModel) FindValue(fieldsAndWhere ...interface{}) (gdb.Value, error) {
return m.M.FindValue(fieldsAndWhere...)
}
// FindCount retrieves and returns the record number by Model.WherePri and Model.Count.
// Also see Model.WherePri and Model.Count.
func (m *arModel) FindCount(where ...interface{}) (int, error) {
return m.M.FindCount(where...)
}
// Chunk iterates the table with given size and callback function.
func (m *arModel) Chunk(limit int, callback func(entities []*Entity, err error) bool) {
m.M.Chunk(limit, func(result gdb.Result, err error) bool {
var entities []*Entity
err = result.Structs(&entities)
if err == sql.ErrNoRows {
return false
}
return callback(entities, err)
})
}

View File

@ -1,33 +0,0 @@
{
"viewpath" : "/home/www/templates/",
"database" : {
"default" : [
{
"host" : "127.0.0.1",
"port" : "3306",
"user" : "root",
"pass" : "123456",
"name" : "test",
"type" : "mysql",
"role" : "master",
"charset" : "utf8",
"priority" : "1"
},
{
"host" : "127.0.0.1",
"port" : "3306",
"user" : "root",
"pass" : "123456",
"name" : "test",
"type" : "mysql",
"role" : "master",
"charset" : "utf8",
"priority" : "1"
}
]
},
"redis" : {
"disk" : "127.0.0.1:6379,0",
"cache" : "127.0.0.1:6379,1"
}
}

View File

@ -1,11 +0,0 @@
viewpath = "/home/www/templates"
# MySQL数据库配置
[database]
debug = true
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/gf"
[redis]
disk = "127.0.0.1:6379,0"
cache = "127.0.0.1:6379,1"

View File

@ -1,35 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<config>
<!-- 模板引擎目录 -->
<viewpath>/home/www/templates/</viewpath>
<!-- MySQL数据库配置 -->
<database>
<default>
<host>127.0.0.1</host>
<port>3306</port>
<user>root</user>
<pass>123456</pass>
<name>test</name>
<role>master</role>
<type>mysql</type>
<charset>utf8</charset>
<priority>1</priority>
</default>
<default>
<host>127.0.0.1</host>
<port>3306</port>
<user>root</user>
<pass>123456</pass>
<name>test</name>
<role>master</role>
<type>mysql</type>
<charset>utf8</charset>
<priority>1</priority>
</default>
</database>
<!-- Redis数据库配置 -->
<redis>
<disk>127.0.0.1:6379,0</disk>
<cache>127.0.0.1:6379,1</cache>
</redis>
</config>

View File

@ -1,27 +0,0 @@
# 模板引擎目录
viewpath: /home/www/templates/
# MySQL数据库配置
database:
default:
- host: 127.0.0.1
port: 3306
user: root
pass: "8692651"
name: test
type: mysql
role: master
charset: utf8
priority: 1
- host: 127.0.0.1
port: 3306
user: root
pass: "8692651"
name: test
type: mysql
role: master
charset: utf8
priority: 1
# Redis数据库配置
redis:
default: 127.0.0.1:6379,0
cache : 127.0.0.1:6379,2

View File

@ -1,25 +0,0 @@
package demo
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
func init() {
s := g.Server()
s.BindHandler("/apple", Apple)
s.BindHandler("/pen", Pen)
s.BindHandler("/apple-pen", ApplePen)
}
func Apple(r *ghttp.Request) {
r.Response.Write("Apple")
}
func Pen(r *ghttp.Request) {
r.Response.Write("Pen")
}
func ApplePen(r *ghttp.Request) {
r.Response.Write("Apple-Pen")
}

View File

@ -1,16 +0,0 @@
package demo
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
type Order struct{}
func init() {
g.Server().BindObject("/{.struct}-{.method}", new(Order))
}
func (o *Order) List(r *ghttp.Request) {
r.Response.Write("List")
}

View File

@ -1,12 +0,0 @@
package demo
import (
"github.com/gogf/gf/frame/gins"
"github.com/gogf/gf/net/ghttp"
)
func init() {
ghttp.GetServer().BindHandler("/config", func(r *ghttp.Request) {
r.Response.Write(gins.Config().GetString("database.default.0.host"))
})
}

View File

@ -1,16 +0,0 @@
package demo
import (
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/gtime"
)
func init() {
ghttp.GetServer().BindHandler("/cookie", Cookie)
}
func Cookie(r *ghttp.Request) {
datetime := r.Cookie.Get("datetime")
r.Cookie.Set("datetime", gtime.Datetime())
r.Response.Write("datetime:" + datetime)
}

View File

@ -1,20 +0,0 @@
package demo
import (
"github.com/gogf/gf/net/ghttp"
)
type ControllerDomain struct{}
// 初始化控制器对象并绑定操作到Web Server
func init() {
// 只有localhost域名下才能访问该对象
// 对应URL为http://localhost:8199/test/show
// 通过该地址将无法访问到内容http://127.0.0.1:8199/test/show
ghttp.GetServer().Domain("localhost").BindObject("/domain", &ControllerDomain{})
}
// 用于对象映射
func (d *ControllerDomain) Show(r *ghttp.Request) {
r.Response.Write("It's show time bibi!")
}

View File

@ -1,24 +0,0 @@
package demo
import (
"github.com/gogf/gf/frame/gmvc"
"github.com/gogf/gf/net/ghttp"
)
type ControllerExit struct {
gmvc.Controller
}
func (c *ControllerExit) Init(r *ghttp.Request) {
c.Controller.Init(r)
c.Response.Write("exit, it will not print \"show\"")
c.Request.Exit()
}
func (c *ControllerExit) Show() {
c.Response.Write("show")
}
func init() {
ghttp.GetServer().BindController("/exit", &ControllerExit{})
}

View File

@ -1,36 +0,0 @@
package demo
import (
"fmt"
"github.com/gogf/gf/net/ghttp"
)
func Form(r *ghttp.Request) {
fmt.Println(r.GetPostMap())
fmt.Println(r.GetPostString("name"))
fmt.Println(r.GetPostString("age"))
}
func FormShow(r *ghttp.Request) {
r.Response.Write(`
<html>
<head>
<title>表单提交</title>
</head>
<body>
<form enctype="application/x-www-form-urlencoded" action="/form" method="post">
<input type="input" name="name" />
<input type="input" name="age" />
<input type="submit" value="submit" />
</form>
</body>
</html>
`)
}
func init() {
ghttp.GetServer().BindHandler("/form", Form)
ghttp.GetServer().BindHandler("/form/show", FormShow)
}

View File

@ -1,9 +0,0 @@
package demo
import "github.com/gogf/gf/net/ghttp"
func init() {
ghttp.GetServer().BindHandler("/", func(r *ghttp.Request) {
r.Response.Write("Hello World!")
})
}

View File

@ -1,30 +0,0 @@
package demo
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/frame/gmvc"
)
type Method struct {
gmvc.Controller
}
func init() {
// 第三个参数指定主要注册的方法,其他方法不注册,方法名称会自动追加到给定路由后面,构成新路由
// 以下注册会中注册两个新路由: /method/name, /method/age
g.Server().BindController("/method", new(Method), "Name, Age")
// 绑定路由到指定的方法执行,以下注册只会注册一个路由: /method-name
g.Server().BindControllerMethod("/method-name", new(Method), "Name")
}
func (c *Method) Name() {
c.Response.Write("John")
}
func (c *Method) Age() {
c.Response.Write("18")
}
func (c *Method) Info() {
c.Response.Write("Info")
}

View File

@ -1,20 +0,0 @@
package demo
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
type Object struct{}
func init() {
g.Server().BindObject("/object", new(Object))
}
func (o *Object) Index(r *ghttp.Request) {
r.Response.Write("object index")
}
func (o *Object) Show(r *ghttp.Request) {
r.Response.Write("object show")
}

View File

@ -1,31 +0,0 @@
package demo
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
type ObjectMethod struct{}
func init() {
obj := &ObjectMethod{}
g.Server().BindObject("/object-method", obj, "Show1, Show2, Show3")
g.Server().BindObjectMethod("/object-method-show1", obj, "Show1")
g.Server().Domain("localhost").BindObject("/object-method", obj, "Show4")
}
func (o *ObjectMethod) Show1(r *ghttp.Request) {
r.Response.Write("show 1")
}
func (o *ObjectMethod) Show2(r *ghttp.Request) {
r.Response.Write("show 2")
}
func (o *ObjectMethod) Show3(r *ghttp.Request) {
r.Response.Write("show 3")
}
func (o *ObjectMethod) Show4(r *ghttp.Request) {
r.Response.Write("show 4")
}

View File

@ -1,30 +0,0 @@
package demo
import "github.com/gogf/gf/net/ghttp"
// 测试绑定对象
type ObjectRest struct{}
func init() {
ghttp.GetServer().BindObjectRest("/object-rest", &ObjectRest{})
}
// RESTFul - GET
func (o *ObjectRest) Get(r *ghttp.Request) {
r.Response.Write("RESTFul HTTP Method GET")
}
// RESTFul - POST
func (c *ObjectRest) Post(r *ghttp.Request) {
r.Response.Write("RESTFul HTTP Method POST")
}
// RESTFul - DELETE
func (c *ObjectRest) Delete(r *ghttp.Request) {
r.Response.Write("RESTFul HTTP Method DELETE")
}
// 该方法无法映射,将会无法访问到
func (c *ObjectRest) Hello(r *ghttp.Request) {
r.Response.Write("Hello")
}

View File

@ -1,25 +0,0 @@
package demo
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
type Product struct {
total int
}
func init() {
p := &Product{}
g.Server().BindHandler("/product/total", p.Total)
g.Server().BindHandler("/product/list/{page}.html", p.List)
}
func (p *Product) Total(r *ghttp.Request) {
p.total++
r.Response.Write("total: ", p.total)
}
func (p *Product) List(r *ghttp.Request) {
r.Response.Write("page: ", r.Get("page"))
}

View File

@ -1,34 +0,0 @@
package demo
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/frame/gmvc"
)
type Rest struct {
gmvc.Controller
}
func init() {
g.Server().BindControllerRest("/rest", &Rest{})
}
// RESTFul - GET
func (c *Rest) Get() {
c.Response.Write("RESTFul HTTP Method GET")
}
// RESTFul - POST
func (c *Rest) Post() {
c.Response.Write("RESTFul HTTP Method POST")
}
// RESTFul - DELETE
func (c *Rest) Delete() {
c.Response.Write("RESTFul HTTP Method DELETE")
}
// 该方法无法映射,将会无法访问到
func (c *Rest) Hello() {
c.Response.Write("Hello")
}

View File

@ -1,18 +0,0 @@
package demo
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/frame/gmvc"
)
type ControllerRule struct {
gmvc.Controller
}
func init() {
g.Server().BindController("/rule/{method}/:name", &ControllerRule{})
}
func (c *ControllerRule) Show() {
c.Response.Write(c.Request.Get("name"))
}

View File

@ -1,17 +0,0 @@
package demo
import (
"strconv"
"github.com/gogf/gf/net/ghttp"
)
func init() {
ghttp.GetServer().BindHandler("/session", Session)
}
func Session(r *ghttp.Request) {
id := r.Session.GetInt("id")
r.Session.Set("id", id+1)
r.Response.Write("id:" + strconv.Itoa(id))
}

View File

@ -1,23 +0,0 @@
package demo
import (
"github.com/gogf/gf/frame/gmvc"
"github.com/gogf/gf/net/ghttp"
)
type ControllerTemplate struct {
gmvc.Controller
}
func (c *ControllerTemplate) Info() {
c.View.Assign("name", "john")
c.View.Assigns(map[string]interface{}{
"age": 18,
"score": 100,
})
c.View.Display("view/user/index.tpl")
}
func init() {
ghttp.GetServer().BindController("/template", &ControllerTemplate{})
}

View File

@ -1,16 +0,0 @@
package demo
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
func init() {
ghttp.GetServer().BindHandler("/template2", func(r *ghttp.Request) {
content, _ := g.View().Parse("index.tpl", map[string]interface{}{
"id": 123,
"name": "john",
})
r.Response.Write(content)
})
}

View File

@ -1,17 +0,0 @@
package demo
import (
"github.com/gogf/gf/frame/gins"
"github.com/gogf/gf/net/ghttp"
)
func init() {
gins.View().SetPath("/home/www/template/")
ghttp.GetServer().BindHandler("/template3", func(r *ghttp.Request) {
content, _ := gins.View().Parse("index.tpl", map[string]interface{}{
"id": 123,
"name": "john",
})
r.Response.Write(content)
})
}

View File

@ -1,40 +0,0 @@
package demo
import (
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/gfile"
)
func Upload(r *ghttp.Request) {
if f, h, e := r.FormFile("upload-file"); e == nil {
defer f.Close()
fname := gfile.Basename(h.Filename)
buffer := make([]byte, h.Size)
f.Read(buffer)
gfile.PutBytes("/tmp/"+fname, buffer)
r.Response.Write(fname + " uploaded successly")
} else {
r.Response.Write(e.Error())
}
}
func UploadShow(r *ghttp.Request) {
r.Response.Write(`
<html>
<head>
<title>上传文件</title>
</head>
<body>
<form enctype="multipart/form-data" action="/upload" method="post">
<input type="file" name="upload-file" />
<input type="submit" value="upload" />
</form>
</body>
</html>
`)
}
func init() {
ghttp.GetServer().BindHandler("/upload", Upload)
ghttp.GetServer().BindHandler("/upload/show", UploadShow)
}

View File

@ -1,29 +0,0 @@
package demo
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/frame/gmvc"
)
type User struct {
gmvc.Controller
}
func init() {
s := g.Server()
s.BindController("/user", new(User))
s.BindController("/user/{.method}/{uid}", new(User), "Info")
s.BindController("/user/{.method}/{page}.html", new(User), "List")
}
func (u *User) Index() {
u.Response.Write("User")
}
func (u *User) Info() {
u.Response.Write("Info - Uid: ", u.Request.Get("uid"))
}
func (u *User) List() {
u.Response.Write("List - Page: ", u.Request.Get("page"))
}

View File

@ -1,19 +0,0 @@
package stats
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
var (
total int
)
func init() {
g.Server().BindHandler("/stats/total", showTotal)
}
func showTotal(r *ghttp.Request) {
total++
r.Response.Write("total:", total)
}

View File

@ -1,10 +0,0 @@
package main
import (
"github.com/gogf/gf/.example/frame/mvc/app/model/article"
)
func main() {
m := article.Model
m.All()
}

View File

@ -1,11 +0,0 @@
<html>
<head>
<title>上传文件</title>
</head>
<body>
<form enctype="multipart/form-data" action="/upload" method="post">
<input type="file" name="upload-file" />
<input type="submit" value="upload" />
</form>
</body>
</html>

View File

@ -1,2 +0,0 @@
<h3>This is footer</h3>
<div style="color:red">tpl vals: {{.}}</div>

View File

@ -1,11 +0,0 @@
<html>
<head>
<title></title>
</head>
<body>
<h3>This is index</h3>
<p>tpl vals: {{.}}</p>
{{include "user/footer.tpl" }}
</body>
</html>

View File

@ -7,9 +7,7 @@ import (
func main() {
s := ghttp.GetServer()
s.BindHandler("/log/error", func(r *ghttp.Request) {
if j := r.GetJson(); j != nil {
r.Response.Write(j.Get("test"))
}
panic("OMG")
})
s.SetErrorLogEnabled(true)
s.SetPort(8199)

View File

@ -0,0 +1,17 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
func main() {
s := g.Server()
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.Writeln(r.Get("amount"))
r.Response.Writeln(r.GetInt("amount"))
r.Response.Writeln(r.GetFloat32("amount"))
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,18 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
func main() {
s := g.Server()
s.BindHandler("/", func(r *ghttp.Request) {
if r.GetInt("type") == 1 {
r.Response.Writeln("john")
}
r.Response.Writeln("smith")
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,15 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
func main() {
s := g.Server()
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.Writef("name: %v, pass: %v", r.Get("name"), r.Get("pass"))
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,47 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/util/gvalid"
)
type RegisterReq struct {
Name string `p:"username" v:"required|length:6,30#请输入账号|账号长度为:min到:max位"`
Pass string `p:"password1" v:"required|length:6,30#请输入密码|密码长度不够"`
Pass2 string `p:"password2" v:"required|length:6,30|same:password1#请确认密码|两次密码不一致"`
}
type RegisterRes struct {
Code int `json:"code"`
Error string `json:"error"`
Data interface{} `json:"data"`
}
func main() {
s := g.Server()
s.BindHandler("/register", func(r *ghttp.Request) {
var req *RegisterReq
//fmt.Println(r.GetBody())
if err := r.Parse(&req); err != nil {
// Validation error.
if v, ok := err.(*gvalid.Error); ok {
r.Response.WriteJsonExit(RegisterRes{
Code: 1,
Error: v.FirstString(),
})
}
// Other error.
r.Response.WriteJsonExit(RegisterRes{
Code: 1,
Error: err.Error(),
})
}
// ...
r.Response.WriteJsonExit(RegisterRes{
Data: req,
})
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,15 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
func main() {
s := g.Server()
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.Write(r.Get("array"))
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,15 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
func main() {
s := g.Server()
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.Write(r.Get("map"))
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,15 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
func main() {
s := g.Server()
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.Write(r.Get("name"))
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,18 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
func main() {
s := g.Server()
s.BindHandler("/input", func(r *ghttp.Request) {
r.Response.Writeln(r.Get("amount"))
})
s.BindHandler("/query", func(r *ghttp.Request) {
r.Response.Writeln(r.GetQuery("amount"))
})
s.SetPort(8199)
s.Run()
}

View File

@ -5,20 +5,20 @@ import (
"github.com/gogf/gf/net/ghttp"
)
type User struct {
Uid int `json:"uid"`
Name string `json:"name" params:"username"`
Pass1 string `json:"pass1" params:"password1,userpass1"`
Pass2 string `json:"pass2" params:"password3,userpass2"`
}
func main() {
type User struct {
Uid int `json:"uid"`
Name string `json:"name" p:"username"`
Pass1 string `json:"pass1" p:"password1"`
Pass2 string `json:"pass2" p:"password2"`
}
s := g.Server()
s.BindHandler("/user", func(r *ghttp.Request) {
user := new(User)
r.GetToStruct(user)
//r.GetPostToStruct(user)
//r.GetQueryToStruct(user)
var user *User
if err := r.Parse(&user); err != nil {
panic(err)
}
r.Response.WriteJson(user)
})
s.SetPort(8199)

View File

@ -0,0 +1,25 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
func main() {
type User struct {
Id int `json:"id"`
Name string `json:"name"`
Pass1 string `json:"password1" p:"password1"`
Pass2 string `json:"password2" p:"password2"`
}
s := g.Server()
s.BindHandler("/", func(r *ghttp.Request) {
var user *User
if err := r.Parse(&user); err != nil {
r.Response.WriteExit(err)
}
r.Response.WriteExit(user)
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,37 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
type RegisterReq struct {
Name string
Pass string `p:"password1"`
Pass2 string `p:"password2"`
}
type RegisterRes struct {
Code int `json:"code"`
Error string `json:"error"`
Data interface{} `json:"data"`
}
func main() {
s := g.Server()
s.BindHandler("/register", func(r *ghttp.Request) {
var req *RegisterReq
if err := r.Parse(&req); err != nil {
r.Response.WriteJsonExit(RegisterRes{
Code: 1,
Error: err.Error(),
})
}
// ...
r.Response.WriteJsonExit(RegisterRes{
Data: req,
})
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,37 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
type RegisterReq struct {
Name string `p:"username" v:"required|length:6,30#请输入账号|账号长度为:min到:max位"`
Pass string `p:"password1" v:"required|length:6,30#请输入密码|密码长度不够"`
Pass2 string `p:"password2" v:"required|length:6,30|same:password1#请确认密码|两次密码不一致"`
}
type RegisterRes struct {
Code int `json:"code"`
Error string `json:"error"`
Data interface{} `json:"data"`
}
func main() {
s := g.Server()
s.BindHandler("/register", func(r *ghttp.Request) {
var req *RegisterReq
if err := r.Parse(&req); err != nil {
r.Response.WriteJsonExit(RegisterRes{
Code: 1,
Error: err.Error(),
})
}
// ...
r.Response.WriteJsonExit(RegisterRes{
Data: req,
})
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,46 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/util/gvalid"
)
type RegisterReq struct {
Name string `p:"username" v:"required|length:6,30#请输入账号|账号长度为:min到:max位"`
Pass string `p:"password1" v:"required|length:6,30#请输入密码|密码长度不够"`
Pass2 string `p:"password2" v:"required|length:6,30|same:password1#请确认密码|两次密码不一致"`
}
type RegisterRes struct {
Code int `json:"code"`
Error string `json:"error"`
Data interface{} `json:"data"`
}
func main() {
s := g.Server()
s.BindHandler("/register", func(r *ghttp.Request) {
var req *RegisterReq
if err := r.Parse(&req); err != nil {
// Validation error.
if v, ok := err.(*gvalid.Error); ok {
r.Response.WriteJsonExit(RegisterRes{
Code: 1,
Error: v.FirstString(),
})
}
// Other error.
r.Response.WriteJsonExit(RegisterRes{
Code: 1,
Error: err.Error(),
})
}
// ...
r.Response.WriteJsonExit(RegisterRes{
Data: req,
})
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,22 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
func main() {
s := g.Server()
group := s.Group("/api")
group.ALL("/all", func(r *ghttp.Request) {
r.Response.Write("all")
})
group.GET("/get", func(r *ghttp.Request) {
r.Response.Write("get")
})
group.POST("/post", func(r *ghttp.Request) {
r.Response.Write("post")
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,38 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
type Object struct{}
func (o *Object) Show(r *ghttp.Request) {
r.Response.Writeln("Show")
}
func (o *Object) Delete(r *ghttp.Request) {
r.Response.Writeln("REST Delete")
}
func Handler(r *ghttp.Request) {
r.Response.Writeln("Handler")
}
func HookHandler(r *ghttp.Request) {
r.Response.Writeln("HOOK Handler")
}
func main() {
s := g.Server()
obj := new(Object)
s.Group("/api").Bind([]ghttp.GroupItem{
{"ALL", "*", HookHandler, ghttp.HOOK_BEFORE_SERVE},
{"ALL", "/handler", Handler},
{"ALL", "/obj", obj},
{"GET", "/obj/show", obj, "Show"},
{"REST", "/obj/rest", obj},
})
s.SetPort(8199)
s.Run()
}

View File

@ -5,7 +5,6 @@ import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/glog"
)
func MiddlewareAuth(r *ghttp.Request) {
@ -24,39 +23,37 @@ func MiddlewareCORS(r *ghttp.Request) {
func MiddlewareLog(r *ghttp.Request) {
r.Middleware.Next()
glog.Println(r.Response.Status, r.URL.Path)
g.Log().Println(r.Response.Status, r.URL.Path)
}
func main() {
s := g.Server()
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareLog)
})
s.Use(MiddlewareLog)
s.Group("/api.v2", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareAuth, MiddlewareCORS)
g.GET("/test", func(r *ghttp.Request) {
group.GET("/test", func(r *ghttp.Request) {
r.Response.Write("test")
})
g.Group("/order", func(group *ghttp.RouterGroup) {
g.GET("/list", func(r *ghttp.Request) {
group.Group("/order", func(group *ghttp.RouterGroup) {
group.GET("/list", func(r *ghttp.Request) {
r.Response.Write("list")
})
g.PUT("/update", func(r *ghttp.Request) {
group.PUT("/update", func(r *ghttp.Request) {
r.Response.Write("update")
})
})
g.Group("/user", func(group *ghttp.RouterGroup) {
g.GET("/info", func(r *ghttp.Request) {
group.Group("/user", func(group *ghttp.RouterGroup) {
group.GET("/info", func(r *ghttp.Request) {
r.Response.Write("info")
})
g.POST("/edit", func(r *ghttp.Request) {
group.POST("/edit", func(r *ghttp.Request) {
r.Response.Write("edit")
})
g.DELETE("/drop", func(r *ghttp.Request) {
group.DELETE("/drop", func(r *ghttp.Request) {
r.Response.Write("drop")
})
})
g.Group("/hook", func(group *ghttp.RouterGroup) {
group.Group("/hook", func(group *ghttp.RouterGroup) {
group.Hook("/*", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
r.Response.Write("hook any")
})

View File

@ -1,9 +1,12 @@
package main
import "github.com/gogf/gf/net/ghttp"
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
func main() {
s := ghttp.GetServer()
s := g.Server()
s.BindHandler("/user/:name", func(r *ghttp.Request) {
r.Response.Writeln(r.Router.Uri)
})

View File

@ -0,0 +1,16 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
// https://github.com/gogf/gf/issues/437
func main() {
s := g.Server()
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.WriteTpl("client/layout.html")
})
s.SetPort(8199)
s.Run()
}

View File

@ -12,16 +12,18 @@ func main() {
s.SetConfigWithMap(g.Map{
"SessionMaxAge": time.Minute,
})
s.BindHandler("/set", func(r *ghttp.Request) {
r.Session.Set("time", gtime.Second())
r.Response.Write("ok")
})
s.BindHandler("/get", func(r *ghttp.Request) {
r.Response.Write(r.Session.Map())
})
s.BindHandler("/del", func(r *ghttp.Request) {
r.Session.Clear()
r.Response.Write("ok")
s.Group("/", func(group *ghttp.RouterGroup) {
group.ALL("/set", func(r *ghttp.Request) {
r.Session.Set("time", gtime.Timestamp())
r.Response.Write("ok")
})
group.ALL("/get", func(r *ghttp.Request) {
r.Response.Write(r.Session.Map())
})
group.ALL("/del", func(r *ghttp.Request) {
r.Session.Clear()
r.Response.Write("ok")
})
})
s.SetPort(8199)
s.Run()

View File

@ -14,16 +14,18 @@ func main() {
"SessionMaxAge": time.Minute,
"SessionStorage": gsession.NewStorageMemory(),
})
s.BindHandler("/set", func(r *ghttp.Request) {
r.Session.Set("time", gtime.Second())
r.Response.Write("ok")
})
s.BindHandler("/get", func(r *ghttp.Request) {
r.Response.Write(r.Session.Map())
})
s.BindHandler("/del", func(r *ghttp.Request) {
r.Session.Clear()
r.Response.Write("ok")
s.Group("/", func(group *ghttp.RouterGroup) {
group.ALL("/set", func(r *ghttp.Request) {
r.Session.Set("time", gtime.Timestamp())
r.Response.Write("ok")
})
group.ALL("/get", func(r *ghttp.Request) {
r.Response.Write(r.Session.Map())
})
group.ALL("/del", func(r *ghttp.Request) {
r.Session.Clear()
r.Response.Write("ok")
})
})
s.SetPort(8199)
s.Run()

View File

@ -14,16 +14,18 @@ func main() {
"SessionMaxAge": time.Minute,
"SessionStorage": gsession.NewStorageRedisHashTable(g.Redis()),
})
s.BindHandler("/set", func(r *ghttp.Request) {
r.Session.Set("time", gtime.Second())
r.Response.Write("ok")
})
s.BindHandler("/get", func(r *ghttp.Request) {
r.Response.Write(r.Session.Map())
})
s.BindHandler("/del", func(r *ghttp.Request) {
r.Session.Clear()
r.Response.Write("ok")
s.Group("/", func(group *ghttp.RouterGroup) {
group.ALL("/set", func(r *ghttp.Request) {
r.Session.Set("time", gtime.Timestamp())
r.Response.Write("ok")
})
group.ALL("/get", func(r *ghttp.Request) {
r.Response.Write(r.Session.Map())
})
group.ALL("/del", func(r *ghttp.Request) {
r.Session.Clear()
r.Response.Write("ok")
})
})
s.SetPort(8199)
s.Run()

View File

@ -14,16 +14,18 @@ func main() {
"SessionMaxAge": time.Minute,
"SessionStorage": gsession.NewStorageRedis(g.Redis()),
})
s.BindHandler("/set", func(r *ghttp.Request) {
r.Session.Set("time", gtime.Second())
r.Response.Write("ok")
})
s.BindHandler("/get", func(r *ghttp.Request) {
r.Response.Write(r.Session.Map())
})
s.BindHandler("/del", func(r *ghttp.Request) {
r.Session.Clear()
r.Response.Write("ok")
s.Group("/", func(group *ghttp.RouterGroup) {
group.ALL("/set", func(r *ghttp.Request) {
r.Session.Set("time", gtime.Timestamp())
r.Response.Write("ok")
})
group.ALL("/get", func(r *ghttp.Request) {
r.Response.Write(r.Session.Map())
})
group.ALL("/del", func(r *ghttp.Request) {
r.Session.Clear()
r.Response.Write("ok")
})
})
s.SetPort(8199)
s.Run()

View File

@ -1,4 +1,7 @@
[database]
debug = true
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
[redis]
default = "127.0.0.1:6379,0"

View File

@ -1,13 +1,14 @@
package main
import (
"fmt"
"github.com/gogf/gf/text/gstr"
)
import "fmt"
func main() {
a := "aaaaa_post"
b := "aaaaa_"
c := gstr.TrimLeftStr(a, b)
fmt.Println(c)
type User struct {
Id int
}
u1 := &User{1}
u2 := *u1
u2.Id = 2
fmt.Println(u1)
fmt.Println(u2)
}

11
.example/other/test2.go Normal file
View File

@ -0,0 +1,11 @@
package main
import (
"fmt"
"github.com/gogf/gf/net/ghttp"
)
func main() {
r := ghttp.PostContent("http://127.0.0.1:8199/test", `<doc><id>1</id><name>john</name><password1>123Abc!@#</password1><password2>123Abc!@#</password2></doc>`)
fmt.Println(r)
}

View File

@ -7,18 +7,18 @@ import (
func main() {
type Ids struct {
Id int `json:"id"`
Uid int `json:"uid"`
Id int `c:"id"`
Uid int `c:"uid"`
}
type Base struct {
Ids
CreateTime string `json:"create_time"`
CreateTime string `c:"create_time"`
}
type User struct {
Base
Passport string `json:"passport"`
Password string `json:"password"`
Nickname string `json:"nickname"`
Passport string `c:"passport"`
Password string `c:"password"`
Nickname string `c:"nickname"`
}
user := new(User)
user.Id = 1
@ -27,5 +27,6 @@ func main() {
user.Passport = "johng"
user.Password = "123456"
user.CreateTime = "2019"
g.Dump(gconv.Map(user))
g.Dump(gconv.MapDeep(user))
}

View File

@ -1,5 +1,7 @@
# Donators
We currently accept donation by Alipay/WechatPay, please note your github/gitee account in your payment bill.
| Name | Channel | Amount | Comment
|---|---|--- | ---
@ -36,7 +38,10 @@
|*洁|wechat|¥10.00|赞助你肥宅快乐水
|R*s|wechat|¥18.88| 谢谢GF辛苦了
|粟*e|wechat|¥50.00|
|[李超](https://github.com/effortlee)|wechat|¥124.00|
|张炳贤|wechat+qq|¥600.00|
|[王哈哈](https://gitee.com/develop1024)|wechat|¥6.66| 希望gf越来越好
|夕景|alipay+qq|¥9.96+3.57|
<img src="https://goframe.org/images/donate.png"/>

View File

@ -625,6 +625,35 @@ func (a *Array) CountValues() map[interface{}]int {
return m
}
// Iterator is alias of IteratorAsc.
func (a *Array) Iterator(f func(k int, v interface{}) bool) {
a.IteratorAsc(f)
}
// IteratorAsc iterates the array in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (a *Array) IteratorAsc(f func(k int, v interface{}) bool) {
a.mu.RLock()
defer a.mu.RUnlock()
for k, v := range a.array {
if !f(k, v) {
break
}
}
}
// IteratorDesc iterates the array in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (a *Array) IteratorDesc(f func(k int, v interface{}) bool) {
a.mu.RLock()
defer a.mu.RUnlock()
for i := len(a.array) - 1; i >= 0; i-- {
if !f(i, a.array[i]) {
break
}
}
}
// String returns current array as a string, which implements like json.Marshal does.
func (a *Array) String() string {
a.mu.RLock()

View File

@ -637,6 +637,35 @@ func (a *IntArray) CountValues() map[int]int {
return m
}
// Iterator is alias of IteratorAsc.
func (a *IntArray) Iterator(f func(k int, v int) bool) {
a.IteratorAsc(f)
}
// IteratorAsc iterates the array in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (a *IntArray) IteratorAsc(f func(k int, v int) bool) {
a.mu.RLock()
defer a.mu.RUnlock()
for k, v := range a.array {
if !f(k, v) {
break
}
}
}
// IteratorDesc iterates the array in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (a *IntArray) IteratorDesc(f func(k int, v int) bool) {
a.mu.RLock()
defer a.mu.RUnlock()
for i := len(a.array) - 1; i >= 0; i-- {
if !f(i, a.array[i]) {
break
}
}
}
// String returns current array as a string, which implements like json.Marshal does.
func (a *IntArray) String() string {
return "[" + a.Join(",") + "]"

View File

@ -622,6 +622,35 @@ func (a *StrArray) CountValues() map[string]int {
return m
}
// Iterator is alias of IteratorAsc.
func (a *StrArray) Iterator(f func(k int, v string) bool) {
a.IteratorAsc(f)
}
// IteratorAsc iterates the array in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (a *StrArray) IteratorAsc(f func(k int, v string) bool) {
a.mu.RLock()
defer a.mu.RUnlock()
for k, v := range a.array {
if !f(k, v) {
break
}
}
}
// IteratorDesc iterates the array in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (a *StrArray) IteratorDesc(f func(k int, v string) bool) {
a.mu.RLock()
defer a.mu.RUnlock()
for i := len(a.array) - 1; i >= 0; i-- {
if !f(i, a.array[i]) {
break
}
}
}
// String returns current array as a string, which implements like json.Marshal does.
func (a *StrArray) String() string {
a.mu.RLock()

View File

@ -570,6 +570,35 @@ func (a *SortedArray) CountValues() map[interface{}]int {
return m
}
// Iterator is alias of IteratorAsc.
func (a *SortedArray) Iterator(f func(k int, v interface{}) bool) {
a.IteratorAsc(f)
}
// IteratorAsc iterates the array in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (a *SortedArray) IteratorAsc(f func(k int, v interface{}) bool) {
a.mu.RLock()
defer a.mu.RUnlock()
for k, v := range a.array {
if !f(k, v) {
break
}
}
}
// IteratorDesc iterates the array in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (a *SortedArray) IteratorDesc(f func(k int, v interface{}) bool) {
a.mu.RLock()
defer a.mu.RUnlock()
for i := len(a.array) - 1; i >= 0; i-- {
if !f(i, a.array[i]) {
break
}
}
}
// String returns current array as a string, which implements like json.Marshal does.
func (a *SortedArray) String() string {
a.mu.RLock()

View File

@ -562,6 +562,35 @@ func (a *SortedIntArray) CountValues() map[int]int {
return m
}
// Iterator is alias of IteratorAsc.
func (a *SortedIntArray) Iterator(f func(k int, v int) bool) {
a.IteratorAsc(f)
}
// IteratorAsc iterates the array in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (a *SortedIntArray) IteratorAsc(f func(k int, v int) bool) {
a.mu.RLock()
defer a.mu.RUnlock()
for k, v := range a.array {
if !f(k, v) {
break
}
}
}
// IteratorDesc iterates the array in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (a *SortedIntArray) IteratorDesc(f func(k int, v int) bool) {
a.mu.RLock()
defer a.mu.RUnlock()
for i := len(a.array) - 1; i >= 0; i-- {
if !f(i, a.array[i]) {
break
}
}
}
// String returns current array as a string, which implements like json.Marshal does.
func (a *SortedIntArray) String() string {
return "[" + a.Join(",") + "]"

View File

@ -547,6 +547,35 @@ func (a *SortedStrArray) CountValues() map[string]int {
return m
}
// Iterator is alias of IteratorAsc.
func (a *SortedStrArray) Iterator(f func(k int, v string) bool) {
a.IteratorAsc(f)
}
// IteratorAsc iterates the array in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (a *SortedStrArray) IteratorAsc(f func(k int, v string) bool) {
a.mu.RLock()
defer a.mu.RUnlock()
for k, v := range a.array {
if !f(k, v) {
break
}
}
}
// IteratorDesc iterates the array in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (a *SortedStrArray) IteratorDesc(f func(k int, v string) bool) {
a.mu.RLock()
defer a.mu.RUnlock()
for i := len(a.array) - 1; i >= 0; i-- {
if !f(i, a.array[i]) {
break
}
}
}
// String returns current array as a string, which implements like json.Marshal does.
func (a *SortedStrArray) String() string {
a.mu.RLock()

View File

@ -450,3 +450,50 @@ func TestArray_Json(t *testing.T) {
gtest.Assert(user.Scores, data["Scores"])
})
}
func TestArray_Iterator(t *testing.T) {
slice := g.Slice{"a", "b", "d", "c"}
array := garray.NewArrayFrom(slice)
gtest.Case(t, func() {
array.Iterator(func(k int, v interface{}) bool {
gtest.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
array.IteratorAsc(func(k int, v interface{}) bool {
gtest.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
array.IteratorDesc(func(k int, v interface{}) bool {
gtest.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
index := 0
array.Iterator(func(k int, v interface{}) bool {
index++
return false
})
gtest.Assert(index, 1)
})
gtest.Case(t, func() {
index := 0
array.IteratorAsc(func(k int, v interface{}) bool {
index++
return false
})
gtest.Assert(index, 1)
})
gtest.Case(t, func() {
index := 0
array.IteratorDesc(func(k int, v interface{}) bool {
index++
return false
})
gtest.Assert(index, 1)
})
}

View File

@ -484,3 +484,50 @@ func TestIntArray_Json(t *testing.T) {
gtest.Assert(user.Scores, data["Scores"])
})
}
func TestIntArray_Iterator(t *testing.T) {
slice := g.SliceInt{10, 20, 30, 40}
array := garray.NewIntArrayFrom(slice)
gtest.Case(t, func() {
array.Iterator(func(k int, v int) bool {
gtest.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
array.IteratorAsc(func(k int, v int) bool {
gtest.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
array.IteratorDesc(func(k int, v int) bool {
gtest.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
index := 0
array.Iterator(func(k int, v int) bool {
index++
return false
})
gtest.Assert(index, 1)
})
gtest.Case(t, func() {
index := 0
array.IteratorAsc(func(k int, v int) bool {
index++
return false
})
gtest.Assert(index, 1)
})
gtest.Case(t, func() {
index := 0
array.IteratorDesc(func(k int, v int) bool {
index++
return false
})
gtest.Assert(index, 1)
})
}

View File

@ -488,3 +488,50 @@ func TestStrArray_Json(t *testing.T) {
gtest.Assert(user.Scores, data["Scores"])
})
}
func TestStrArray_Iterator(t *testing.T) {
slice := g.SliceStr{"a", "b", "d", "c"}
array := garray.NewStrArrayFrom(slice)
gtest.Case(t, func() {
array.Iterator(func(k int, v string) bool {
gtest.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
array.IteratorAsc(func(k int, v string) bool {
gtest.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
array.IteratorDesc(func(k int, v string) bool {
gtest.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
index := 0
array.Iterator(func(k int, v string) bool {
index++
return false
})
gtest.Assert(index, 1)
})
gtest.Case(t, func() {
index := 0
array.IteratorAsc(func(k int, v string) bool {
index++
return false
})
gtest.Assert(index, 1)
})
gtest.Case(t, func() {
index := 0
array.IteratorDesc(func(k int, v string) bool {
index++
return false
})
gtest.Assert(index, 1)
})
}

View File

@ -587,3 +587,50 @@ func TestSortedArray_Json(t *testing.T) {
gtest.AssertIN(user.Scores.PopLeft(), data["Scores"])
})
}
func TestSortedArray_Iterator(t *testing.T) {
slice := g.Slice{"a", "b", "d", "c"}
array := garray.NewSortedArrayFrom(slice, gutil.ComparatorString)
gtest.Case(t, func() {
array.Iterator(func(k int, v interface{}) bool {
gtest.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
array.IteratorAsc(func(k int, v interface{}) bool {
gtest.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
array.IteratorDesc(func(k int, v interface{}) bool {
gtest.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
index := 0
array.Iterator(func(k int, v interface{}) bool {
index++
return false
})
gtest.Assert(index, 1)
})
gtest.Case(t, func() {
index := 0
array.IteratorAsc(func(k int, v interface{}) bool {
index++
return false
})
gtest.Assert(index, 1)
})
gtest.Case(t, func() {
index := 0
array.IteratorDesc(func(k int, v interface{}) bool {
index++
return false
})
gtest.Assert(index, 1)
})
}

View File

@ -466,3 +466,50 @@ func TestSortedIntArray_Json(t *testing.T) {
gtest.Assert(user.Scores, []int{98, 99, 100})
})
}
func TestSortedIntArray_Iterator(t *testing.T) {
slice := g.SliceInt{10, 20, 30, 40}
array := garray.NewSortedIntArrayFrom(slice)
gtest.Case(t, func() {
array.Iterator(func(k int, v int) bool {
gtest.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
array.IteratorAsc(func(k int, v int) bool {
gtest.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
array.IteratorDesc(func(k int, v int) bool {
gtest.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
index := 0
array.Iterator(func(k int, v int) bool {
index++
return false
})
gtest.Assert(index, 1)
})
gtest.Case(t, func() {
index := 0
array.IteratorAsc(func(k int, v int) bool {
index++
return false
})
gtest.Assert(index, 1)
})
gtest.Case(t, func() {
index := 0
array.IteratorDesc(func(k int, v int) bool {
index++
return false
})
gtest.Assert(index, 1)
})
}

View File

@ -476,3 +476,50 @@ func TestSortedStrArray_Json(t *testing.T) {
gtest.Assert(user.Scores, []string{"A", "A", "A+"})
})
}
func TestSortedStrArray_Iterator(t *testing.T) {
slice := g.SliceStr{"a", "b", "d", "c"}
array := garray.NewSortedStrArrayFrom(slice)
gtest.Case(t, func() {
array.Iterator(func(k int, v string) bool {
gtest.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
array.IteratorAsc(func(k int, v string) bool {
gtest.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
array.IteratorDesc(func(k int, v string) bool {
gtest.Assert(v, slice[k])
return true
})
})
gtest.Case(t, func() {
index := 0
array.Iterator(func(k int, v string) bool {
index++
return false
})
gtest.Assert(index, 1)
})
gtest.Case(t, func() {
index := 0
array.IteratorAsc(func(k int, v string) bool {
index++
return false
})
gtest.Assert(index, 1)
})
gtest.Case(t, func() {
index := 0
array.IteratorDesc(func(k int, v string) bool {
index++
return false
})
gtest.Assert(index, 1)
})
}

View File

@ -364,6 +364,7 @@ func (l *List) Iterator(f func(e *Element) bool) {
// If <f> returns true, then it continues iterating; or false to stop.
func (l *List) IteratorAsc(f func(e *Element) bool) {
l.mu.RLock()
defer l.mu.RUnlock()
length := l.list.Len()
if length > 0 {
for i, e := 0, l.list.Front(); i < length; i, e = i+1, e.Next() {
@ -372,13 +373,13 @@ func (l *List) IteratorAsc(f func(e *Element) bool) {
}
}
}
l.mu.RUnlock()
}
// IteratorDesc iterates the list in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (l *List) IteratorDesc(f func(e *Element) bool) {
l.mu.RLock()
defer l.mu.RUnlock()
length := l.list.Len()
if length > 0 {
for i, e := 0, l.list.Back(); i < length; i, e = i+1, e.Prev() {
@ -387,7 +388,6 @@ func (l *List) IteratorDesc(f func(e *Element) bool) {
}
}
}
l.mu.RUnlock()
}
// Join joins list elements with a string <glue>.

View File

@ -12,6 +12,7 @@ import (
"sync/atomic"
)
// Bool is a struct for concurrent-safe operation for type bool.
type Bool struct {
value int32
}
@ -21,7 +22,7 @@ var (
bytesFalse = []byte("false")
)
// NewBool returns a concurrent-safe object for bool type,
// NewBool creates and returns a concurrent-safe object for bool type,
// with given initial value <value>.
func NewBool(value ...bool) *Bool {
t := &Bool{}
@ -50,7 +51,7 @@ func (v *Bool) Set(value bool) (old bool) {
return
}
// Val atomically loads t.valueue.
// Val atomically loads and returns t.valueue.
func (v *Bool) Val() bool {
return atomic.LoadInt32(&v.value) > 0
}

View File

@ -12,11 +12,12 @@ import (
"sync/atomic"
)
// Byte is a struct for concurrent-safe operation for type byte.
type Byte struct {
value int32
}
// NewByte returns a concurrent-safe object for byte type,
// NewByte creates and returns a concurrent-safe object for byte type,
// with given initial value <value>.
func NewByte(value ...byte) *Byte {
if len(value) > 0 {
@ -37,7 +38,7 @@ func (v *Byte) Set(value byte) (old byte) {
return byte(atomic.SwapInt32(&v.value, int32(value)))
}
// Val atomically loads t.value.
// Val atomically loads and returns t.value.
func (v *Byte) Val() byte {
return byte(atomic.LoadInt32(&v.value))
}

View File

@ -13,11 +13,12 @@ import (
"sync/atomic"
)
// Bytes is a struct for concurrent-safe operation for type []byte.
type Bytes struct {
value atomic.Value
}
// NewBytes returns a concurrent-safe object for []byte type,
// NewBytes creates and returns a concurrent-safe object for []byte type,
// with given initial value <value>.
func NewBytes(value ...[]byte) *Bytes {
t := &Bytes{}
@ -40,7 +41,7 @@ func (v *Bytes) Set(value []byte) (old []byte) {
return
}
// Val atomically loads t.value.
// Val atomically loads and returns t.value.
func (v *Bytes) Val() []byte {
if s := v.value.Load(); s != nil {
return s.([]byte)

View File

@ -14,11 +14,12 @@ import (
"unsafe"
)
// Float32 is a struct for concurrent-safe operation for type float32.
type Float32 struct {
value uint32
}
// NewFloat32 returns a concurrent-safe object for float32 type,
// NewFloat32 creates and returns a concurrent-safe object for float32 type,
// with given initial value <value>.
func NewFloat32(value ...float32) *Float32 {
if len(value) > 0 {
@ -39,7 +40,7 @@ func (v *Float32) Set(value float32) (old float32) {
return math.Float32frombits(atomic.SwapUint32(&v.value, math.Float32bits(value)))
}
// Val atomically loads t.value.
// Val atomically loads and returns t.value.
func (v *Float32) Val() float32 {
return math.Float32frombits(atomic.LoadUint32(&v.value))
}

View File

@ -14,11 +14,12 @@ import (
"unsafe"
)
// Float64 is a struct for concurrent-safe operation for type float64.
type Float64 struct {
value uint64
}
// NewFloat64 returns a concurrent-safe object for float64 type,
// NewFloat64 creates and returns a concurrent-safe object for float64 type,
// with given initial value <value>.
func NewFloat64(value ...float64) *Float64 {
if len(value) > 0 {
@ -39,7 +40,7 @@ func (v *Float64) Set(value float64) (old float64) {
return math.Float64frombits(atomic.SwapUint64(&v.value, math.Float64bits(value)))
}
// Val atomically loads t.value.
// Val atomically loads and returns t.value.
func (v *Float64) Val() float64 {
return math.Float64frombits(atomic.LoadUint64(&v.value))
}

View File

@ -12,11 +12,12 @@ import (
"sync/atomic"
)
// Int is a struct for concurrent-safe operation for type int.
type Int struct {
value int64
}
// NewInt returns a concurrent-safe object for int type,
// NewInt creates and returns a concurrent-safe object for int type,
// with given initial value <value>.
func NewInt(value ...int) *Int {
if len(value) > 0 {
@ -37,7 +38,7 @@ func (v *Int) Set(value int) (old int) {
return int(atomic.SwapInt64(&v.value, int64(value)))
}
// Val atomically loads t.value.
// Val atomically loads and returns t.value.
func (v *Int) Val() int {
return int(atomic.LoadInt64(&v.value))
}

View File

@ -12,11 +12,12 @@ import (
"sync/atomic"
)
// Int32 is a struct for concurrent-safe operation for type int32.
type Int32 struct {
value int32
}
// NewInt32 returns a concurrent-safe object for int32 type,
// NewInt32 creates and returns a concurrent-safe object for int32 type,
// with given initial value <value>.
func NewInt32(value ...int32) *Int32 {
if len(value) > 0 {
@ -37,7 +38,7 @@ func (v *Int32) Set(value int32) (old int32) {
return atomic.SwapInt32(&v.value, value)
}
// Val atomically loads t.value.
// Val atomically loads and returns t.value.
func (v *Int32) Val() int32 {
return atomic.LoadInt32(&v.value)
}

View File

@ -12,11 +12,12 @@ import (
"sync/atomic"
)
// Int64 is a struct for concurrent-safe operation for type int64.
type Int64 struct {
value int64
}
// NewInt64 returns a concurrent-safe object for int64 type,
// NewInt64 creates and returns a concurrent-safe object for int64 type,
// with given initial value <value>.
func NewInt64(value ...int64) *Int64 {
if len(value) > 0 {
@ -37,7 +38,7 @@ func (v *Int64) Set(value int64) (old int64) {
return atomic.SwapInt64(&v.value, value)
}
// Val atomically loads t.value.
// Val atomically loads and returns t.value.
func (v *Int64) Val() int64 {
return atomic.LoadInt64(&v.value)
}

View File

@ -12,11 +12,12 @@ import (
"sync/atomic"
)
// Interface is a struct for concurrent-safe operation for type interface{}.
type Interface struct {
value atomic.Value
}
// NewInterface returns a concurrent-safe object for interface{} type,
// NewInterface creates and returns a concurrent-safe object for interface{} type,
// with given initial value <value>.
func NewInterface(value ...interface{}) *Interface {
t := &Interface{}
@ -39,7 +40,7 @@ func (v *Interface) Set(value interface{}) (old interface{}) {
return
}
// Val atomically loads t.value.
// Val atomically loads and returns t.value.
func (v *Interface) Val() interface{} {
return v.value.Load()
}

View File

@ -12,11 +12,12 @@ import (
"sync/atomic"
)
// String is a struct for concurrent-safe operation for type string.
type String struct {
value atomic.Value
}
// NewString returns a concurrent-safe object for string type,
// NewString creates and returns a concurrent-safe object for string type,
// with given initial value <value>.
func NewString(value ...string) *String {
t := &String{}
@ -38,7 +39,7 @@ func (v *String) Set(value string) (old string) {
return
}
// Val atomically loads t.value.
// Val atomically loads and returns t.value.
func (v *String) Val() string {
s := v.value.Load()
if s != nil {

View File

@ -12,11 +12,12 @@ import (
"sync/atomic"
)
// Uint is a struct for concurrent-safe operation for type uint.
type Uint struct {
value uint64
}
// NewUint returns a concurrent-safe object for uint type,
// NewUint creates and returns a concurrent-safe object for uint type,
// with given initial value <value>.
func NewUint(value ...uint) *Uint {
if len(value) > 0 {
@ -37,7 +38,7 @@ func (v *Uint) Set(value uint) (old uint) {
return uint(atomic.SwapUint64(&v.value, uint64(value)))
}
// Val atomically loads t.value.
// Val atomically loads and returns t.value.
func (v *Uint) Val() uint {
return uint(atomic.LoadUint64(&v.value))
}

View File

@ -12,11 +12,12 @@ import (
"sync/atomic"
)
// Uint32 is a struct for concurrent-safe operation for type uint32.
type Uint32 struct {
value uint32
}
// NewUint32 returns a concurrent-safe object for uint32 type,
// NewUint32 creates and returns a concurrent-safe object for uint32 type,
// with given initial value <value>.
func NewUint32(value ...uint32) *Uint32 {
if len(value) > 0 {
@ -37,7 +38,7 @@ func (v *Uint32) Set(value uint32) (old uint32) {
return atomic.SwapUint32(&v.value, value)
}
// Val atomically loads t.value.
// Val atomically loads and returns t.value.
func (v *Uint32) Val() uint32 {
return atomic.LoadUint32(&v.value)
}

View File

@ -12,11 +12,12 @@ import (
"sync/atomic"
)
// Uint64 is a struct for concurrent-safe operation for type uint64.
type Uint64 struct {
value uint64
}
// NewUint64 returns a concurrent-safe object for uint64 type,
// NewUint64 creates and returns a concurrent-safe object for uint64 type,
// with given initial value <value>.
func NewUint64(value ...uint64) *Uint64 {
if len(value) > 0 {
@ -37,7 +38,7 @@ func (v *Uint64) Set(value uint64) (old uint64) {
return atomic.SwapUint64(&v.value, value)
}
// Val atomically loads t.value.
// Val atomically loads and returns t.value.
func (v *Uint64) Val() uint64 {
return atomic.LoadUint64(&v.value)
}

View File

@ -22,17 +22,18 @@ import (
"github.com/gogf/gf/util/grand"
)
// 数据库操作接口
// DB is the interface for ORM operations.
type DB interface {
// 建立数据库连接方法(开发者一般不需要直接调用)
// Open creates a raw connection object for database with given node configuration.
// Note that it is not recommended using the this function manually.
Open(config *ConfigNode) (*sql.DB, error)
// SQL操作方法 API
// Query APIs.
Query(query string, args ...interface{}) (*sql.Rows, error)
Exec(sql string, args ...interface{}) (sql.Result, error)
Prepare(sql string, execOnMaster ...bool) (*sql.Stmt, error)
// 内部实现API的方法(不同数据库可覆盖这些方法实现自定义的操作)
// Internal APIs for CURD, which can be overwrote for custom CURD implements.
doQuery(link dbLink, query string, args ...interface{}) (rows *sql.Rows, err error)
doGetAll(link dbLink, query string, args ...interface{}) (result Result, err error)
doExec(link dbLink, query string, args ...interface{}) (result sql.Result, err error)
@ -42,7 +43,7 @@ type DB interface {
doUpdate(link dbLink, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error)
doDelete(link dbLink, table string, condition string, args ...interface{}) (result sql.Result, err error)
// 数据库查询
// Query APIs for convenience purpose.
GetAll(query string, args ...interface{}) (Result, error)
GetOne(query string, args ...interface{}) (Record, error)
GetValue(query string, args ...interface{}) (Value, error)
@ -51,36 +52,33 @@ type DB interface {
GetStructs(objPointerSlice interface{}, query string, args ...interface{}) error
GetScan(objPointer interface{}, query string, args ...interface{}) error
// 创建底层数据库master/slave链接对象
// Master/Slave support.
Master() (*sql.DB, error)
Slave() (*sql.DB, error)
// Ping
// Ping.
PingMaster() error
PingSlave() error
// 开启事务操作
// Transaction.
Begin() (*TX, error)
// 数据表插入/更新/保存操作
Insert(table string, data interface{}, batch ...int) (sql.Result, error)
Replace(table string, data interface{}, batch ...int) (sql.Result, error)
Save(table string, data interface{}, batch ...int) (sql.Result, error)
// 数据表插入/更新/保存操作(批量)
BatchInsert(table string, list interface{}, batch ...int) (sql.Result, error)
BatchReplace(table string, list interface{}, batch ...int) (sql.Result, error)
BatchSave(table string, list interface{}, batch ...int) (sql.Result, error)
// 数据修改/删除
Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error)
Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error)
// 创建链式操作对象
// Create model.
From(tables string) *Model
Table(tables string) *Model
// 设置管理
// Configuration methods.
SetDebug(debug bool)
SetSchema(schema string)
SetLogger(logger *glog.Logger)
@ -88,53 +86,54 @@ type DB interface {
SetMaxIdleConnCount(n int)
SetMaxOpenConnCount(n int)
SetMaxConnLifetime(d time.Duration)
Tables() (tables []string, err error)
TableFields(table string) (map[string]*TableField, error)
Tables(schema ...string) (tables []string, err error)
TableFields(table string, schema ...string) (map[string]*TableField, error)
// 内部方法接口
// Internal methods.
getCache() *gcache.Cache
getChars() (charLeft string, charRight string)
getDebug() bool
getPrefix() string
getMaster(schema ...string) (*sql.DB, error)
getSlave(schema ...string) (*sql.DB, error)
quoteWord(s string) string
quoteString(s string) string
doSetSchema(sqlDb *sql.DB, schema string) error
filterFields(table string, data map[string]interface{}) map[string]interface{}
handleTableName(table string) string
filterFields(schema, table string, data map[string]interface{}) map[string]interface{}
convertValue(fieldValue []byte, fieldType string) interface{}
rowsToResult(rows *sql.Rows) (Result, error)
handleSqlBeforeExec(sql string) string
}
// 执行底层数据库操作的核心接口
// dbLink is a common database function wrapper interface for internal usage.
type dbLink interface {
Query(query string, args ...interface{}) (*sql.Rows, error)
Exec(sql string, args ...interface{}) (sql.Result, error)
Prepare(sql string) (*sql.Stmt, error)
}
// 数据库链接对象
// dbBase is the base struct for database management.
type dbBase struct {
db DB // 数据库对象
group string // 配置分组名称
debug *gtype.Bool // (默认关闭)是否开启调试模式,当开启时会启用一些调试特性
cache *gcache.Cache // 数据库缓存,包括底层连接池对象缓存及查询缓存;需要注意的是,事务查询不支持查询缓存
schema *gtype.String // 手动切换的数据库名称
prefix string // 表名前缀
tables map[string]map[string]string // 数据库表结构
logger *glog.Logger // 日志管理对象
maxIdleConnCount int // 连接池最大限制的连接数
maxOpenConnCount int // 连接池最大打开的连接数
maxConnLifetime time.Duration // 连接对象可重复使用的时间长度
db DB // DB interface object.
group string // Configuration group name.
debug *gtype.Bool // Enable debug mode for the database.
cache *gcache.Cache // Cache manager.
schema *gtype.String // Custom schema for this object.
prefix string // Table prefix.
logger *glog.Logger // Logger.
maxIdleConnCount int // Max idle connection count.
maxOpenConnCount int // Max open connection count.
maxConnLifetime time.Duration // Max TTL for a connection.
}
// 执行的SQL对象
// Sql is the sql recording struct.
type Sql struct {
Sql string // SQL语句(可能带有预处理占位符)
Args []interface{} // 预处理参数值列表
Format string // 格式化后的SQL语句仅供参考
Error error // 执行结果(nil为成功)
Start int64 // 执行开始时间(毫秒)
End int64 // 执行结束时间(毫秒)
Sql string // SQL string(may contain reserved char '?').
Args []interface{} // Arguments for this sql.
Format string // Formatted sql which contains arguments in the sql.
Error error // Execution result.
Start int64 // Start execution timestamp in milliseconds.
End int64 // End execution timestamp in milliseconds.
}
// 表字段结构信息
@ -313,25 +312,36 @@ func getConfigNodeByWeight(cg ConfigGroup) *ConfigNode {
return nil
}
// 获得底层数据库链接对象
func (bs *dbBase) getSqlDb(master bool) (sqlDb *sql.DB, err error) {
// 负载均衡
// getSqlDb retrieves and returns a underlying database connection object.
// The parameter <master> specifies whether retrieves master node connection if
// master-slave nodes are configured.
func (bs *dbBase) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error) {
// Load balance.
node, err := getConfigNodeByGroup(bs.group, master)
if err != nil {
return nil, err
}
// 默认值设定
// Default value checks.
if node.Charset == "" {
node.Charset = "utf8"
}
// 缓存连接对象(该对象其实是一个连接池对象)
// Changes the schema.
nodeSchema := bs.schema.Val()
if len(schema) > 0 && schema[0] != "" {
nodeSchema = schema[0]
}
if nodeSchema != "" {
// Value copy.
n := *node
n.Name = nodeSchema
node = &n
}
// Cache the underlying connection object by node.
v := bs.cache.GetOrSetFuncLock(node.String(), func() interface{} {
sqlDb, err = bs.db.Open(node)
if err != nil {
return nil
}
// 接口对象可能会覆盖这些连接参数,所以这里优先判断有误设置连接池属性。
// 若无设置则使用配置节点的连接池参数
if bs.maxIdleConnCount > 0 {
sqlDb.SetMaxIdleConns(bs.maxIdleConnCount)
} else if node.MaxIdleConnCount > 0 {
@ -354,30 +364,41 @@ func (bs *dbBase) getSqlDb(master bool) (sqlDb *sql.DB, err error) {
if v != nil && sqlDb == nil {
sqlDb = v.(*sql.DB)
}
// 是否开启调试模式
if node.Debug {
bs.db.SetDebug(node.Debug)
}
// 是否手动选择数据库
if v := bs.schema.Val(); v != "" {
if e := bs.db.doSetSchema(sqlDb, v); e != nil {
err = e
}
}
return
}
// 切换当前数据库对象操作的数据库。
// SetSchema changes the schema for this database connection object.
// Importantly note that when schema configuration changed for the database,
// it affects all operations on the database object in the future.
func (bs *dbBase) SetSchema(schema string) {
bs.schema.Set(schema)
}
// 创建底层数据库master链接对象。
// Master creates and returns a connection from master node if master-slave configured.
// It returns the default connection if master-slave not configured.
func (bs *dbBase) Master() (*sql.DB, error) {
return bs.getSqlDb(true)
return bs.getSqlDb(true, bs.schema.Val())
}
// 创建底层数据库slave链接对象。
// Slave creates and returns a connection from slave node if master-slave configured.
// It returns the default connection if master-slave not configured.
func (bs *dbBase) Slave() (*sql.DB, error) {
return bs.getSqlDb(false)
return bs.getSqlDb(false, bs.schema.Val())
}
// getMaster acts like function Master but with additional <schema> parameter specifying
// the schema for the connection. It is defined for internal usage.
// Also see Master.
func (bs *dbBase) getMaster(schema ...string) (*sql.DB, error) {
return bs.getSqlDb(true, schema...)
}
// getSlave acts like function Slave but with additional <schema> parameter specifying
// the schema for the connection. It is defined for internal usage.
// Also see Slave.
func (bs *dbBase) getSlave(schema ...string) (*sql.DB, error) {
return bs.getSqlDb(false, schema...)
}

View File

@ -31,17 +31,6 @@ var (
lastOperatorReg = regexp.MustCompile(`[<>=]+\s*$`)
)
// 打印SQL对象(仅在debug=true时有效)
func (bs *dbBase) printSql(v *Sql) {
s := fmt.Sprintf("[%d ms] %s", v.End-v.Start, v.Format)
if v.Error != nil {
s += "\nError: " + v.Error.Error()
bs.logger.StackWithFilter(gPATH_FILTER_KEY).Error(s)
} else {
bs.logger.StackWithFilter(gPATH_FILTER_KEY).Debug(s)
}
}
// 数据库sql查询操作主要执行查询
func (bs *dbBase) Query(query string, args ...interface{}) (rows *sql.Rows, err error) {
link, err := bs.db.Slave()
@ -56,9 +45,9 @@ func (bs *dbBase) doQuery(link dbLink, query string, args ...interface{}) (rows
query, args = formatQuery(query, args)
query = bs.db.handleSqlBeforeExec(query)
if bs.db.getDebug() {
mTime1 := gtime.Millisecond()
mTime1 := gtime.TimestampMilli()
rows, err = link.Query(query, args...)
mTime2 := gtime.Millisecond()
mTime2 := gtime.TimestampMilli()
s := &Sql{
Sql: query,
Args: args,
@ -302,7 +291,7 @@ func (bs *dbBase) doInsert(link dbLink, table string, data interface{}, option i
var values []string
var params []interface{}
var dataMap Map
table = bs.db.quoteWord(table)
table = bs.db.handleTableName(table)
// 使用反射判断data数据类型如果为slice类型那么自动转为批量操作
rv := reflect.ValueOf(data)
kind := rv.Kind()
@ -371,7 +360,7 @@ func (bs *dbBase) BatchSave(table string, list interface{}, batch ...int) (sql.R
func (bs *dbBase) doBatchInsert(link dbLink, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) {
var keys, values []string
var params []interface{}
table = bs.db.quoteWord(table)
table = bs.db.handleTableName(table)
listMap := (List)(nil)
switch v := list.(type) {
case Result:
@ -491,7 +480,7 @@ func (bs *dbBase) Update(table string, data interface{}, condition interface{},
// CURD操作:数据更新统一采用sql预处理。
// data参数支持string/map/struct/*struct类型类型。
func (bs *dbBase) doUpdate(link dbLink, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) {
table = bs.db.quoteWord(table)
table = bs.db.handleTableName(table)
updates := ""
// 使用反射进行类型判断
rv := reflect.ValueOf(data)
@ -543,7 +532,7 @@ func (bs *dbBase) doDelete(link dbLink, table string, condition string, args ...
return nil, err
}
}
table = bs.db.quoteWord(table)
table = bs.db.handleTableName(table)
return bs.db.doExec(link, fmt.Sprintf("DELETE FROM %s%s", table, condition), args...)
}
@ -605,6 +594,17 @@ func (bs *dbBase) rowsToResult(rows *sql.Rows) (Result, error) {
return records, nil
}
// handleTableName adds prefix string and quote chars for the table. It handles table string like:
// "user", "user u", "user,user_detail", "user u, user_detail ut", "user as u, user_detail as ut".
//
// Note that, this will automatically checks the table prefix whether already added, if true it does
// nothing to the table name, or else adds the prefix to the table name.
func (bs *dbBase) handleTableName(table string) string {
charLeft, charRight := bs.db.getChars()
prefix := bs.db.getPrefix()
return doHandleTableName(table, prefix, charLeft, charRight)
}
// quoteWord checks given string <s> a word, if true quotes it with security chars of the database
// and returns the quoted string; or else return <s> without any change.
func (bs *dbBase) quoteWord(s string) string {
@ -619,8 +619,13 @@ func (bs *dbBase) quoteString(s string) string {
return doQuoteString(s, charLeft, charRight)
}
// 动态切换数据库
func (bs *dbBase) doSetSchema(sqlDb *sql.DB, schema string) error {
_, err := sqlDb.Exec("USE " + schema)
return err
// 打印SQL对象(仅在debug=true时有效)
func (bs *dbBase) printSql(v *Sql) {
s := fmt.Sprintf("[%d ms] %s", v.End-v.Start, v.Format)
if v.Error != nil {
s += "\nError: " + v.Error.Error()
bs.logger.StackWithFilter(gPATH_FILTER_KEY).Error(s)
} else {
bs.logger.StackWithFilter(gPATH_FILTER_KEY).Debug(s)
}
}

View File

@ -51,8 +51,31 @@ var (
quoteWordReg = regexp.MustCompile(`^[a-zA-Z0-9\-_]+$`)
)
// handleTableName adds prefix string and quote chars for the table. It handles table string like:
// "user", "user u", "user,user_detail", "user u, user_detail ut", "user as u, user_detail as ut".
//
// Note that, this will automatically checks the table prefix whether already added, if true it does
// nothing to the table name, or else adds the prefix to the table name.
func doHandleTableName(table, prefix, charLeft, charRight string) string {
array1 := gstr.SplitAndTrim(table, ",")
for k1, v1 := range array1 {
array2 := gstr.SplitAndTrim(v1, " ")
// Trim the security chars.
array2[0] = gstr.TrimLeftStr(array2[0], charLeft)
array2[0] = gstr.TrimRightStr(array2[0], charRight)
// If the table name already has the prefix, skips the prefix adding.
if len(array2[0]) <= len(prefix) || array2[0][:len(prefix)] != prefix {
array2[0] = prefix + array2[0]
}
// Add the security chars.
array2[0] = doQuoteWord(array2[0], charLeft, charRight)
array1[k1] = gstr.Join(array2, " ")
}
return gstr.Join(array1, ",")
}
// doQuoteWord checks given string <s> a word, if true quotes it with <charLeft> and <charRight>
// and returns the quoted string; or else return <s> without any change.
// and returns the quoted string; or else returns <s> without any change.
func doQuoteWord(s, charLeft, charRight string) string {
if quoteWordReg.MatchString(s) && !gstr.ContainsAny(s, charLeft+charRight) {
return charLeft + s + charRight
@ -78,23 +101,6 @@ func doQuoteString(s, charLeft, charRight string) string {
return gstr.Join(array1, ",")
}
// addTablePrefix adds prefix string to the table. It handles table string like:
// "user", "user u", "user,user_detail", "user u, user_detail ut", "user as u, user_detail as ut".
//
// Note that, this should be used before any quoting function calls.
func addTablePrefix(table, prefix string) string {
if prefix == "" {
return table
}
array1 := gstr.SplitAndTrim(table, ",")
for k1, v1 := range array1 {
array2 := gstr.SplitAndTrim(v1, " ")
array2[0] = prefix + array2[0]
array1[k1] = gstr.Join(array2, " ")
}
return gstr.Join(array1, ",")
}
// GetWhereConditionOfStruct returns the where condition sql and arguments by given struct pointer.
// This function automatically retrieves primary or unique field and its attribute value as condition.
func GetWhereConditionOfStruct(pointer interface{}) (where string, args []interface{}) {

View File

@ -24,6 +24,7 @@ import (
type Model struct {
db DB // Underlying DB interface.
tx *TX // Underlying TX interface.
schema string // Custom database schema.
linkType int // Mark for operation on master or slave.
tablesInit string // Table names when model initialization.
tables string // Operation table names, which can be more than one table names and aliases, like: "user", "user u", "user u, user_detail ud".
@ -67,8 +68,7 @@ const (
// The parameter <tables> can be more than one table names, like :
// "user", "user u", "user, user_detail", "user u, user_detail ud"
func (bs *dbBase) Table(table string) *Model {
table = addTablePrefix(table, bs.db.getPrefix())
table = bs.db.quoteString(table)
table = bs.db.handleTableName(table)
return &Model{
db: bs.db,
tablesInit: table,
@ -81,8 +81,15 @@ func (bs *dbBase) Table(table string) *Model {
}
}
// Model is alias of dbBase.Table.
// See dbBase.Table.
func (bs *dbBase) Model(tables string) *Model {
return bs.db.Table(tables)
}
// From is alias of dbBase.Table.
// See dbBase.Table.
// Deprecated.
func (bs *dbBase) From(tables string) *Model {
return bs.db.Table(tables)
}
@ -90,8 +97,7 @@ func (bs *dbBase) From(tables string) *Model {
// Table acts like dbBase.Table except it operates on transaction.
// See dbBase.Table.
func (tx *TX) Table(table string) *Model {
table = addTablePrefix(table, tx.db.getPrefix())
table = tx.db.quoteString(table)
table = tx.db.handleTableName(table)
return &Model{
db: tx.db,
tx: tx,
@ -105,8 +111,15 @@ func (tx *TX) Table(table string) *Model {
}
}
// Model is alias of tx.Table.
// See tx.Table.
func (tx *TX) Model(tables string) *Model {
return tx.Table(tables)
}
// From is alias of tx.Table.
// See tx.Table.
// Deprecated.
func (tx *TX) From(tables string) *Model {
return tx.Table(tables)
}
@ -125,6 +138,13 @@ func (m *Model) TX(tx *TX) *Model {
return model
}
// Schema sets the schema for current operation.
func (m *Model) Schema(schema string) *Model {
model := m.getModel()
model.schema = schema
return model
}
// Clone creates and returns a new model which is a clone of current model.
// Note that it uses deep-copy for the clone.
func (m *Model) Clone() *Model {
@ -186,27 +206,21 @@ func (m *Model) getModel() *Model {
// LeftJoin does "LEFT JOIN ... ON ..." statement on the model.
func (m *Model) LeftJoin(table string, on string) *Model {
model := m.getModel()
table = addTablePrefix(table, m.db.getPrefix())
table = m.db.quoteString(table)
model.tables += fmt.Sprintf(" LEFT JOIN %s ON (%s)", table, on)
model.tables += fmt.Sprintf(" LEFT JOIN %s ON (%s)", m.db.handleTableName(table), on)
return model
}
// RightJoin does "RIGHT JOIN ... ON ..." statement on the model.
func (m *Model) RightJoin(table string, on string) *Model {
model := m.getModel()
table = addTablePrefix(table, m.db.getPrefix())
table = m.db.quoteString(table)
model.tables += fmt.Sprintf(" RIGHT JOIN %s ON (%s)", table, on)
model.tables += fmt.Sprintf(" RIGHT JOIN %s ON (%s)", m.db.handleTableName(table), on)
return model
}
// InnerJoin does "INNER JOIN ... ON ..." statement on the model.
func (m *Model) InnerJoin(table string, on string) *Model {
model := m.getModel()
table = addTablePrefix(table, m.db.getPrefix())
table = m.db.quoteString(table)
model.tables += fmt.Sprintf(" INNER JOIN %s ON (%s)", table, on)
model.tables += fmt.Sprintf(" INNER JOIN %s ON (%s)", m.db.handleTableName(table), on)
return model
}
@ -501,7 +515,7 @@ func (m *Model) Insert(data ...interface{}) (result sql.Result, err error) {
batch = m.batch
}
return m.db.doBatchInsert(
m.getLink(),
m.getLink(true),
m.tables,
m.filterDataForInsertOrUpdate(list),
gINSERT_OPTION_DEFAULT,
@ -510,7 +524,7 @@ func (m *Model) Insert(data ...interface{}) (result sql.Result, err error) {
} else if data, ok := m.data.(Map); ok {
// Single insert.
return m.db.doInsert(
m.getLink(),
m.getLink(true),
m.tables,
m.filterDataForInsertOrUpdate(data),
gINSERT_OPTION_DEFAULT,
@ -541,7 +555,7 @@ func (m *Model) Replace(data ...interface{}) (result sql.Result, err error) {
batch = m.batch
}
return m.db.doBatchInsert(
m.getLink(),
m.getLink(true),
m.tables,
m.filterDataForInsertOrUpdate(list),
gINSERT_OPTION_REPLACE,
@ -550,7 +564,7 @@ func (m *Model) Replace(data ...interface{}) (result sql.Result, err error) {
} else if data, ok := m.data.(Map); ok {
// Single insert.
return m.db.doInsert(
m.getLink(),
m.getLink(true),
m.tables,
m.filterDataForInsertOrUpdate(data),
gINSERT_OPTION_REPLACE,
@ -584,7 +598,7 @@ func (m *Model) Save(data ...interface{}) (result sql.Result, err error) {
batch = m.batch
}
return m.db.doBatchInsert(
m.getLink(),
m.getLink(true),
m.tables,
m.filterDataForInsertOrUpdate(list),
gINSERT_OPTION_SAVE,
@ -593,7 +607,7 @@ func (m *Model) Save(data ...interface{}) (result sql.Result, err error) {
} else if data, ok := m.data.(Map); ok {
// Single save.
return m.db.doInsert(
m.getLink(),
m.getLink(true),
m.tables,
m.filterDataForInsertOrUpdate(data),
gINSERT_OPTION_SAVE,
@ -627,7 +641,7 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro
}
condition, conditionArgs := m.formatCondition(false)
return m.db.doUpdate(
m.getLink(),
m.getLink(true),
m.tables,
m.filterDataForInsertOrUpdate(m.data),
condition,
@ -648,7 +662,7 @@ func (m *Model) Delete(where ...interface{}) (result sql.Result, err error) {
}
}()
condition, conditionArgs := m.formatCondition(false)
return m.db.doDelete(m.getLink(), m.tables, condition, conditionArgs...)
return m.db.doDelete(m.getLink(true), m.tables, condition, conditionArgs...)
}
// Select is alias of Model.All.
@ -881,6 +895,15 @@ func (m *Model) FindCount(where ...interface{}) (int, error) {
return m.Count()
}
// FindScan retrieves and returns the record/records by Model.WherePri and Model.Scan.
// Also see Model.WherePri and Model.Scan.
func (m *Model) FindScan(pointer interface{}, where ...interface{}) error {
if len(where) > 0 {
return m.WherePri(where[0], where[1:]...).Scan(pointer)
}
return m.Scan(pointer)
}
// Chunk iterates the table with given size and callback function.
func (m *Model) Chunk(limit int, callback func(result Result, err error) bool) {
page := m.start
@ -926,7 +949,7 @@ func (m *Model) filterDataForInsertOrUpdate(data interface{}) interface{} {
// Note that, it does not filter list item, which is also type of map, for "omit empty" feature.
func (m *Model) doFilterDataMapForInsertOrUpdate(data Map, allowOmitEmpty bool) Map {
if m.filter {
data = m.db.filterFields(m.tables, data)
data = m.db.filterFields(m.schema, m.tables, data)
}
// Remove key-value pairs of which the value is empty.
if allowOmitEmpty && m.option&OPTION_OMITEMPTY > 0 {
@ -953,16 +976,25 @@ func (m *Model) doFilterDataMapForInsertOrUpdate(data Map, allowOmitEmpty bool)
}
// getLink returns the underlying database link object with configured <linkType> attribute.
func (m *Model) getLink() dbLink {
// The parameter <master> specifies whether using the master node if master-slave configured.
func (m *Model) getLink(master bool) dbLink {
if m.tx != nil {
return m.tx.tx
}
switch m.linkType {
linkType := m.linkType
if linkType == 0 {
if master {
linkType = gLINK_TYPE_MASTER
} else {
linkType = gLINK_TYPE_SLAVE
}
}
switch linkType {
case gLINK_TYPE_MASTER:
link, _ := m.db.Master()
link, _ := m.db.getMaster(m.schema)
return link
case gLINK_TYPE_SLAVE:
link, _ := m.db.Slave()
link, _ := m.db.getSlave(m.schema)
return link
}
return nil
@ -981,7 +1013,7 @@ func (m *Model) getAll(query string, args ...interface{}) (result Result, err er
return v.(Result), nil
}
}
result, err = m.db.doGetAll(m.getLink(), query, args...)
result, err = m.db.doGetAll(m.getLink(false), query, args...)
// Cache the result.
if len(cacheKey) > 0 && err == nil {
if m.cacheDuration < 0 {

View File

@ -3,11 +3,11 @@
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// 说明:
// 1.需要导入sqlserver驱动 github.com/denisenkom/go-mssqldb
// 2.不支持save/replace方法
// 3.不支持LastInsertId方法
//
// Note:
// 1. It needs manually import: _ "github.com/lib/pq"
// 2. It does not support Save/Replace features.
// 3. It does not support LastInsertId.
package gdb
@ -20,19 +20,19 @@ import (
"github.com/gogf/gf/text/gregex"
)
// 数据库链接对象
type dbMssql struct {
*dbBase
}
// 创建SQL操作对象
func (db *dbMssql) Open(config *ConfigNode) (*sql.DB, error) {
source := ""
if config.LinkInfo != "" {
source = config.LinkInfo
} else {
source = fmt.Sprintf("user id=%s;password=%s;server=%s;port=%s;database=%s;encrypt=disable",
config.User, config.Pass, config.Host, config.Port, config.Name)
source = fmt.Sprintf(
"user id=%s;password=%s;server=%s;port=%s;database=%s;encrypt=disable",
config.User, config.Pass, config.Host, config.Port, config.Name,
)
}
if db, err := sql.Open("sqlserver", source); err == nil {
return db, nil
@ -41,76 +41,68 @@ func (db *dbMssql) Open(config *ConfigNode) (*sql.DB, error) {
}
}
// 获得关键字操作符
func (db *dbMssql) getChars() (charLeft string, charRight string) {
return "\"", "\""
}
// 在执行sql之前对sql进行进一步处理
func (db *dbMssql) handleSqlBeforeExec(query string) string {
index := 0
str, _ := gregex.ReplaceStringFunc("\\?", query, func(s string) string {
index++
return fmt.Sprintf("@p%d", index)
})
str, _ = gregex.ReplaceString("\"", "", str)
return db.parseSql(str)
}
//将MYSQL的SQL语法转换为MSSQL的语法
//1.由于mssql不支持limit写法所以需要对mysql中的limit用法做转换
func (db *dbMssql) parseSql(sql string) string {
//下面的正则表达式匹配出SELECT和INSERT的关键字后分别做不同的处理如有LIMIT则将LIMIT的关键字也匹配出
// SELECT * FROM USER WHERE ID=1 LIMIT 1
if m, _ := gregex.MatchString(`^SELECT(.+)LIMIT 1$`, sql); len(m) > 1 {
return fmt.Sprintf(`SELECT TOP 1 %s`, m[1])
}
// SELECT * FROM USER WHERE AGE>18 ORDER BY ID DESC LIMIT 100, 200
patten := `^\s*(?i)(SELECT)|(LIMIT\s*(\d+)\s*,\s*(\d+))`
if gregex.IsMatchString(patten, sql) == false {
//fmt.Println("not matched..")
return sql
}
res, err := gregex.MatchAllString(patten, sql)
if err != nil {
//fmt.Println("MatchString error.", err)
return ""
}
index := 0
keyword := strings.TrimSpace(res[index][0])
keyword = strings.ToUpper(keyword)
index++
switch keyword {
case "SELECT":
//不含LIMIT关键字则不处理
if len(res) < 2 || (strings.HasPrefix(res[index][0], "LIMIT") == false && strings.HasPrefix(res[index][0], "limit") == false) {
// 不含LIMIT关键字则不处理
if len(res) < 2 ||
(strings.HasPrefix(res[index][0], "LIMIT") == false &&
strings.HasPrefix(res[index][0], "limit") == false) {
break
}
//不含LIMIT则不处理
// 不含LIMIT则不处理
if gregex.IsMatchString("((?i)SELECT)(.+)((?i)LIMIT)", sql) == false {
break
}
//判断SQL中是否含有order by
// 判断SQL中是否含有order by
selectStr := ""
orderbyStr := ""
haveOrderby := gregex.IsMatchString("((?i)SELECT)(.+)((?i)ORDER BY)", sql)
if haveOrderby {
//取order by 前面的字符串
orderStr := ""
haveOrder := gregex.IsMatchString("((?i)SELECT)(.+)((?i)ORDER BY)", sql)
if haveOrder {
// 取order by 前面的字符串
queryExpr, _ := gregex.MatchString("((?i)SELECT)(.+)((?i)ORDER BY)", sql)
if len(queryExpr) != 4 || strings.EqualFold(queryExpr[1], "SELECT") == false || strings.EqualFold(queryExpr[3], "ORDER BY") == false {
break
}
selectStr = queryExpr[2]
//取order by表达式的值
orderbyExpr, _ := gregex.MatchString("((?i)ORDER BY)(.+)((?i)LIMIT)", sql)
if len(orderbyExpr) != 4 || strings.EqualFold(orderbyExpr[1], "ORDER BY") == false || strings.EqualFold(orderbyExpr[3], "LIMIT") == false {
// 取order by表达式的值
orderExpr, _ := gregex.MatchString("((?i)ORDER BY)(.+)((?i)LIMIT)", sql)
if len(orderExpr) != 4 || strings.EqualFold(orderExpr[1], "ORDER BY") == false || strings.EqualFold(orderExpr[3], "LIMIT") == false {
break
}
orderbyStr = orderbyExpr[2]
orderStr = orderExpr[2]
} else {
queryExpr, _ := gregex.MatchString("((?i)SELECT)(.+)((?i)LIMIT)", sql)
if len(queryExpr) != 4 || strings.EqualFold(queryExpr[1], "SELECT") == false || strings.EqualFold(queryExpr[3], "LIMIT") == false {
@ -119,7 +111,7 @@ func (db *dbMssql) parseSql(sql string) string {
selectStr = queryExpr[2]
}
//取limit后面的取值范围
// 取limit后面的取值范围
first, limit := 0, 0
for i := 1; i < len(res[index]); i++ {
if len(strings.TrimSpace(res[index][i])) == 0 {
@ -133,53 +125,69 @@ func (db *dbMssql) parseSql(sql string) string {
}
}
if haveOrderby {
sql = fmt.Sprintf("SELECT * FROM (SELECT ROW_NUMBER() OVER (ORDER BY %s) as ROWNUMBER_, %s ) as TMP_ WHERE TMP_.ROWNUMBER_ > %d AND TMP_.ROWNUMBER_ <= %d", orderbyStr, selectStr, first, limit)
if haveOrder {
sql = fmt.Sprintf(
"SELECT * FROM "+
"(SELECT ROW_NUMBER() OVER (ORDER BY %s) as ROWNUMBER_, %s ) as TMP_ "+
"WHERE TMP_.ROWNUMBER_ > %d AND TMP_.ROWNUMBER_ <= %d",
orderStr, selectStr, first, limit,
)
} else {
if first == 0 {
first = limit
} else {
first = limit - first
}
sql = fmt.Sprintf("SELECT * FROM (SELECT TOP %d * FROM (SELECT TOP %d %s) as TMP1_ ) as TMP2_ ", first, limit, selectStr)
sql = fmt.Sprintf(
"SELECT * FROM (SELECT TOP %d * FROM (SELECT TOP %d %s) as TMP1_ ) as TMP2_ ",
first, limit, selectStr,
)
}
default:
}
return sql
}
// 返回当前数据库所有的数据表名称
// TODO
func (bs *dbMssql) Tables() (tables []string, err error) {
func (db *dbMssql) Tables(schema ...string) (tables []string, err error) {
return
}
// 获得指定表表的数据结构构造成map哈希表返回其中键名为表字段名称键值暂无用途(默认为字段数据类型).
func (db *dbMssql) TableFields(table string) (fields map[string]*TableField, err error) {
// 缓存不存在时会查询数据表结构,缓存后不过期,直至程序重启(重新部署)
v := db.cache.GetOrSetFunc("mssql_table_fields_"+table, func() interface{} {
result := (Result)(nil)
result, err = db.GetAll(fmt.Sprintf(`
SELECT c.name as FIELD, CASE t.name
WHEN 'numeric' THEN t.name + '(' + convert(varchar(20),c.xprec) + ',' + convert(varchar(20),c.xscale) + ')'
WHEN 'char' THEN t.name + '(' + convert(varchar(20),c.length)+ ')'
WHEN 'varchar' THEN t.name + '(' + convert(varchar(20),c.length)+ ')'
ELSE t.name + '(' + convert(varchar(20),c.length)+ ')' END as TYPE
FROM systypes t,syscolumns c WHERE t.xtype=c.xtype AND c.id = (SELECT id FROM sysobjects WHERE name='%s') ORDER BY c.colid`, strings.ToUpper(table)))
if err != nil {
return nil
}
fields = make(map[string]*TableField)
for i, m := range result {
// SQLServer返回的field为大写的需要转为小写的
fields[strings.ToLower(m["FIELD"].String())] = &TableField{
Index: i,
Name: strings.ToLower(m["FIELD"].String()),
Type: strings.ToLower(m["TYPE"].String()),
func (db *dbMssql) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
checkSchema := db.schema.Val()
if len(schema) > 0 && schema[0] != "" {
checkSchema = schema[0]
}
v := db.cache.GetOrSetFunc(
fmt.Sprintf(`mssql_table_fields_%s_%s`, table, checkSchema), func() interface{} {
var result Result
var link *sql.DB
link, err = db.getSlave(checkSchema)
if err != nil {
return nil
}
}
return fields
}, 0)
result, err = db.doGetAll(link, fmt.Sprintf(`
SELECT c.name as FIELD, CASE t.name
WHEN 'numeric' THEN t.name + '(' + convert(varchar(20),c.xprec) + ',' + convert(varchar(20),c.xscale) + ')'
WHEN 'char' THEN t.name + '(' + convert(varchar(20),c.length)+ ')'
WHEN 'varchar' THEN t.name + '(' + convert(varchar(20),c.length)+ ')'
ELSE t.name + '(' + convert(varchar(20),c.length)+ ')' END as TYPE
FROM systypes t,syscolumns c WHERE t.xtype=c.xtype
AND c.id = (SELECT id FROM sysobjects WHERE name='%s')
ORDER BY c.colid`, strings.ToUpper(table)))
if err != nil {
return nil
}
fields = make(map[string]*TableField)
for i, m := range result {
fields[strings.ToLower(m["FIELD"].String())] = &TableField{
Index: i,
Name: strings.ToLower(m["FIELD"].String()),
Type: strings.ToLower(m["TYPE"].String()),
}
}
return fields
}, 0)
if err == nil {
fields = v.(map[string]*TableField)
}

View File

@ -13,19 +13,20 @@ import (
_ "github.com/gf-third/mysql"
)
// 数据库链接对象
type dbMysql struct {
*dbBase
}
// 创建SQL操作对象内部采用了lazy link处理
// Open creates and returns a underlying database connection with given configuration.
func (db *dbMysql) Open(config *ConfigNode) (*sql.DB, error) {
var source string
if config.LinkInfo != "" {
source = config.LinkInfo
} else {
source = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&multiStatements=true&parseTime=true&loc=Local",
config.User, config.Pass, config.Host, config.Port, config.Name, config.Charset)
source = fmt.Sprintf(
"%s:%s@tcp(%s:%s)/%s?charset=%s&multiStatements=true&parseTime=true&loc=Local",
config.User, config.Pass, config.Host, config.Port, config.Name, config.Charset,
)
}
if db, err := sql.Open("gf-mysql", source); err == nil {
return db, nil
@ -34,12 +35,12 @@ func (db *dbMysql) Open(config *ConfigNode) (*sql.DB, error) {
}
}
// 获得关键字操作符
// getChars returns the quote chars for field.
func (db *dbMysql) getChars() (charLeft string, charRight string) {
return "`", "`"
}
// 在执行sql之前对sql进行进一步处理
func (db *dbMysql) handleSqlBeforeExec(query string) string {
return query
// handleSqlBeforeExec handles the sql before posts it to database.
func (db *dbMysql) handleSqlBeforeExec(sql string) string {
return sql
}

View File

@ -3,10 +3,11 @@
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// 说明:
// 1.需要导入oracle驱动 github.com/mattn/go-oci8
// 2.不支持save/replace方法可以调用这2个方法估计会报错还没测试过,(应该是可以通过oracle的merge来实现这2个功能的还没仔细研究)
// 3.不支持LastInsertId方法
//
// Note:
// 1. It needs manually import: _ "github.com/mattn/go-oci8"
// 2. It does not support Save/Replace features.
// 3. It does not support LastInsertId.
package gdb
@ -21,7 +22,6 @@ import (
"github.com/gogf/gf/text/gregex"
)
// 数据库链接对象
type dbOracle struct {
*dbBase
}
@ -31,7 +31,6 @@ const (
tableAlias2 = "GFORM2"
)
// 创建SQL操作对象
func (db *dbOracle) Open(config *ConfigNode) (*sql.DB, error) {
var source string
if config.LinkInfo != "" {
@ -46,36 +45,28 @@ func (db *dbOracle) Open(config *ConfigNode) (*sql.DB, error) {
}
}
// 获得关键字操作符
func (db *dbOracle) getChars() (charLeft string, charRight string) {
return "\"", "\""
}
// 在执行sql之前对sql进行进一步处理
func (db *dbOracle) handleSqlBeforeExec(query string) string {
index := 0
str, _ := gregex.ReplaceStringFunc("\\?", query, func(s string) string {
index++
return fmt.Sprintf(":%d", index)
})
str, _ = gregex.ReplaceString("\"", "", str)
return db.parseSql(str)
}
//由于ORACLE中对LIMIT和批量插入的语法与MYSQL不一致所以这里需要对LIMIT和批量插入做语法上的转换
func (db *dbOracle) parseSql(sql string) string {
//下面的正则表达式匹配出SELECT和INSERT的关键字后分别做不同的处理如有LIMIT则将LIMIT的关键字也匹配出
patten := `^\s*(?i)(SELECT)|(LIMIT\s*(\d+)\s*,\s*(\d+))`
if gregex.IsMatchString(patten, sql) == false {
//fmt.Println("not matched..")
return sql
}
res, err := gregex.MatchAllString(patten, sql)
if err != nil {
//fmt.Println("MatchString error.", err)
return ""
}
@ -86,12 +77,12 @@ func (db *dbOracle) parseSql(sql string) string {
index++
switch keyword {
case "SELECT":
//不含LIMIT关键字则不处理
// 不含LIMIT关键字则不处理
if len(res) < 2 || (strings.HasPrefix(res[index][0], "LIMIT") == false && strings.HasPrefix(res[index][0], "limit") == false) {
break
}
//取limit前面的字符串
// 取limit前面的字符串
if gregex.IsMatchString("((?i)SELECT)(.+)((?i)LIMIT)", sql) == false {
break
}
@ -101,7 +92,7 @@ func (db *dbOracle) parseSql(sql string) string {
break
}
//取limit后面的取值范围
// 取limit后面的取值范围
first, limit := 0, 0
for i := 1; i < len(res[index]); i++ {
if len(strings.TrimSpace(res[index][i])) == 0 {
@ -121,38 +112,39 @@ func (db *dbOracle) parseSql(sql string) string {
return sql
}
// 返回当前数据库所有的数据表名称
// TODO
func (bs *dbOracle) Tables() (tables []string, err error) {
func (db *dbOracle) Tables(schema ...string) (tables []string, err error) {
return
}
// 获得指定表表的数据结构构造成map哈希表返回其中键名为表字段名称键值为字段数据结构.
func (db *dbOracle) TableFields(table string) (fields map[string]*TableField, err error) {
// 缓存不存在时会查询数据表结构,缓存后不过期,直至程序重启(重新部署)
v := db.cache.GetOrSetFunc("oracle_table_fields_"+table, func() interface{} {
result := (Result)(nil)
result, err = db.GetAll(fmt.Sprintf(`
SELECT COLUMN_NAME AS FIELD, CASE DATA_TYPE
WHEN 'NUMBER' THEN DATA_TYPE||'('||DATA_PRECISION||','||DATA_SCALE||')'
WHEN 'FLOAT' THEN DATA_TYPE||'('||DATA_PRECISION||','||DATA_SCALE||')'
ELSE DATA_TYPE||'('||DATA_LENGTH||')' END AS TYPE
FROM USER_TAB_COLUMNS WHERE TABLE_NAME = '%s' ORDER BY COLUMN_ID`, strings.ToUpper(table)))
if err != nil {
return nil
}
fields = make(map[string]*TableField)
for i, m := range result {
// ORACLE返回的值默认都是大写的需要转为小写
fields[strings.ToLower(m["FIELD"].String())] = &TableField{
Index: i,
Name: strings.ToLower(m["FIELD"].String()),
Type: strings.ToLower(m["TYPE"].String()),
func (db *dbOracle) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
checkSchema := db.schema.Val()
if len(schema) > 0 && schema[0] != "" {
checkSchema = schema[0]
}
v := db.cache.GetOrSetFunc(
fmt.Sprintf(`oracle_table_fields_%s_%s`, table, checkSchema),
func() interface{} {
result := (Result)(nil)
result, err = db.GetAll(fmt.Sprintf(`
SELECT COLUMN_NAME AS FIELD, CASE DATA_TYPE
WHEN 'NUMBER' THEN DATA_TYPE||'('||DATA_PRECISION||','||DATA_SCALE||')'
WHEN 'FLOAT' THEN DATA_TYPE||'('||DATA_PRECISION||','||DATA_SCALE||')'
ELSE DATA_TYPE||'('||DATA_LENGTH||')' END AS TYPE
FROM USER_TAB_COLUMNS WHERE TABLE_NAME = '%s' ORDER BY COLUMN_ID`, strings.ToUpper(table)))
if err != nil {
return nil
}
}
return fields
}, 0)
fields = make(map[string]*TableField)
for i, m := range result {
fields[strings.ToLower(m["FIELD"].String())] = &TableField{
Index: i,
Name: strings.ToLower(m["FIELD"].String()),
Type: strings.ToLower(m["TYPE"].String()),
}
}
return fields
}, 0)
if err == nil {
fields = v.(map[string]*TableField)
}

View File

@ -3,6 +3,11 @@
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
//
// Note:
// 1. It needs manually import: _ "github.com/lib/pq"
// 2. It does not support Save/Replace features.
// 3. It does not support LastInsertId.
package gdb
@ -14,26 +19,19 @@ import (
"github.com/gogf/gf/text/gregex"
)
// PostgreSQL的适配.
//
// 使用时需要import:
//
// _ "github.com/lib/pq"
//
// @todo 需要完善replace和save的操作覆盖
// 数据库链接对象
type dbPgsql struct {
*dbBase
}
// 创建SQL操作对象内部采用了lazy link处理
func (db *dbPgsql) Open(config *ConfigNode) (*sql.DB, error) {
var source string
if config.LinkInfo != "" {
source = config.LinkInfo
} else {
source = fmt.Sprintf("user=%s password=%s host=%s port=%s dbname=%s sslmode=disable", config.User, config.Pass, config.Host, config.Port, config.Name)
source = fmt.Sprintf(
"user=%s password=%s host=%s port=%s dbname=%s sslmode=disable",
config.User, config.Pass, config.Host, config.Port, config.Name,
)
}
if db, err := sql.Open("postgres", source); err == nil {
return db, nil
@ -42,59 +40,58 @@ func (db *dbPgsql) Open(config *ConfigNode) (*sql.DB, error) {
}
}
// 动态切换数据库
func (db *dbPgsql) setSchema(sqlDb *sql.DB, schema string) error {
_, err := sqlDb.Exec("SET search_path TO " + schema)
return err
}
// 获得关键字操作符
func (db *dbPgsql) getChars() (charLeft string, charRight string) {
return "\"", "\""
}
// 在执行sql之前对sql进行进一步处理
func (db *dbPgsql) handleSqlBeforeExec(query string) string {
func (db *dbPgsql) handleSqlBeforeExec(sql string) string {
index := 0
query, _ = gregex.ReplaceStringFunc("\\?", query, func(s string) string {
sql, _ = gregex.ReplaceStringFunc("\\?", sql, func(s string) string {
index++
return fmt.Sprintf("$%d", index)
})
// 分页语法替换
query, _ = gregex.ReplaceString(` LIMIT (\d+),\s*(\d+)`, ` LIMIT $1 OFFSET $2`, query)
return query
sql, _ = gregex.ReplaceString(` LIMIT (\d+),\s*(\d+)`, ` LIMIT $1 OFFSET $2`, sql)
return sql
}
// 返回当前数据库所有的数据表名称
// TODO
func (bs *dbPgsql) Tables() (tables []string, err error) {
func (db *dbPgsql) Tables(schema ...string) (tables []string, err error) {
return
}
// 获得指定表表的数据结构构造成map哈希表返回其中键名为表字段名称键值为字段数据结构.
func (db *dbPgsql) TableFields(table string) (fields map[string]*TableField, err error) {
// 缓存不存在时会查询数据表结构,缓存后不过期,直至程序重启(重新部署)
func (db *dbPgsql) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
table, _ = gregex.ReplaceString("\"", "", table)
v := db.cache.GetOrSetFunc("pgsql_table_fields_"+table, func() interface{} {
result := (Result)(nil)
result, err = db.GetAll(fmt.Sprintf(`
SELECT a.attname AS field, t.typname AS type FROM pg_class c, pg_attribute a
LEFT OUTER JOIN pg_description b ON a.attrelid=b.objoid AND a.attnum = b.objsubid,pg_type t
WHERE c.relname = '%s' and a.attnum > 0 and a.attrelid = c.oid and a.atttypid = t.oid ORDER BY a.attnum`, strings.ToLower(table)))
if err != nil {
return nil
}
fields = make(map[string]*TableField)
for i, m := range result {
fields[m["field"].String()] = &TableField{
Index: i,
Name: m["field"].String(),
Type: m["type"].String(),
checkSchema := db.schema.Val()
if len(schema) > 0 && schema[0] != "" {
checkSchema = schema[0]
}
v := db.cache.GetOrSetFunc(
fmt.Sprintf(`pgsql_table_fields_%s_%s`, table, checkSchema), func() interface{} {
var result Result
var link *sql.DB
link, err = db.getSlave(checkSchema)
if err != nil {
return nil
}
}
return fields
}, 0)
result, err = db.doGetAll(link, fmt.Sprintf(`
SELECT a.attname AS field, t.typname AS type FROM pg_class c, pg_attribute a
LEFT OUTER JOIN pg_description b ON a.attrelid=b.objoid AND a.attnum = b.objsubid,pg_type t
WHERE c.relname = '%s' and a.attnum > 0 and a.attrelid = c.oid and a.atttypid = t.oid
ORDER BY a.attnum`, strings.ToLower(table)))
if err != nil {
return nil
}
fields = make(map[string]*TableField)
for i, m := range result {
fields[m["field"].String()] = &TableField{
Index: i,
Name: m["field"].String(),
Type: m["type"].String(),
}
}
return fields
}, 0)
if err == nil {
fields = v.(map[string]*TableField)
}

Some files were not shown because too many files have changed in this diff Show More