column.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package sqlite
  15. import (
  16. "bytes"
  17. "database/sql"
  18. "fmt"
  19. "reflect"
  20. "strconv"
  21. "time"
  22. "yunion.io/x/pkg/gotypes"
  23. "yunion.io/x/pkg/tristate"
  24. "yunion.io/x/pkg/utils"
  25. "yunion.io/x/sqlchemy"
  26. )
  27. func columnDefinitionBuffer(c sqlchemy.IColumnSpec) bytes.Buffer {
  28. var buf bytes.Buffer
  29. buf.WriteByte('`')
  30. buf.WriteString(c.Name())
  31. buf.WriteByte('`')
  32. buf.WriteByte(' ')
  33. if c.IsAutoIncrement() {
  34. // https://www.sqlite.org/autoinc.html
  35. buf.WriteString("INTEGER PRIMARY KEY")
  36. } else {
  37. buf.WriteString(c.ColType())
  38. }
  39. if !c.IsNullable() {
  40. buf.WriteString(" NOT NULL")
  41. }
  42. def := c.Default()
  43. defOk := c.IsSupportDefault()
  44. if def != "" {
  45. if !defOk {
  46. panic(fmt.Errorf("column %q type %q does not support having default value: %q",
  47. c.Name(), c.ColType(), def,
  48. ))
  49. }
  50. def = sqlchemy.GetStringValue(c.ConvertFromString(def))
  51. buf.WriteString(" DEFAULT ")
  52. if c.IsText() {
  53. buf.WriteByte('\'')
  54. }
  55. buf.WriteString(def)
  56. if c.IsText() {
  57. buf.WriteByte('\'')
  58. }
  59. }
  60. if c.IsText() {
  61. buf.WriteString(" COLLATE NOCASE")
  62. }
  63. return buf
  64. }
  65. // SBooleanColumn represents a boolean type column, which is a int(1) for mysql, with value of true or false
  66. type SBooleanColumn struct {
  67. sqlchemy.SBaseColumn
  68. }
  69. // DefinitionString implementation of SBooleanColumn for IColumnSpec
  70. func (c *SBooleanColumn) DefinitionString() string {
  71. buf := columnDefinitionBuffer(c)
  72. return buf.String()
  73. }
  74. // ConvertFromString implementation of SBooleanColumn for IColumnSpec
  75. func (c *SBooleanColumn) ConvertFromString(str string) interface{} {
  76. if sqlchemy.ConvertValueToBool(str) {
  77. return 1
  78. }
  79. return 0
  80. }
  81. // ConvertFromValue implementation of STristateColumn for IColumnSpec
  82. func (c *SBooleanColumn) ConvertFromValue(val interface{}) interface{} {
  83. if sqlchemy.ConvertValueToBool(val) {
  84. return 1
  85. }
  86. return 0
  87. }
  88. // IsZero implementation of SBooleanColumn for IColumnSpec
  89. func (c *SBooleanColumn) IsZero(val interface{}) bool {
  90. if c.IsPointer() {
  91. bVal := val.(*bool)
  92. return bVal == nil
  93. }
  94. bVal := val.(bool)
  95. return bVal == false
  96. }
  97. // NewBooleanColumn return an instance of SBooleanColumn
  98. func NewBooleanColumn(name string, tagmap map[string]string, isPointer bool) SBooleanColumn {
  99. bc := SBooleanColumn{SBaseColumn: sqlchemy.NewBaseColumn(name, "INTEGER", tagmap, isPointer)}
  100. if !bc.IsPointer() && len(bc.Default()) > 0 && bc.ConvertFromString(bc.Default()) == "1" {
  101. msg := fmt.Sprintf("Non-pointer boolean column should not default true: %s(%s)", name, tagmap)
  102. panic(msg)
  103. }
  104. return bc
  105. }
  106. // STristateColumn represents a tristate type column, with value of true, false or none
  107. type STristateColumn struct {
  108. sqlchemy.SBaseColumn
  109. }
  110. // DefinitionString implementation of STristateColumn for IColumnSpec
  111. func (c *STristateColumn) DefinitionString() string {
  112. buf := columnDefinitionBuffer(c)
  113. return buf.String()
  114. }
  115. // ConvertFromString implementation of STristateColumn for IColumnSpec
  116. func (c *STristateColumn) ConvertFromString(str string) interface{} {
  117. switch sqlchemy.ConvertValueToTriState(str) {
  118. case tristate.True:
  119. return 1
  120. case tristate.None:
  121. return sql.NullInt32{}
  122. default:
  123. return 0
  124. }
  125. }
  126. // ConvertFromValue implementation of STristateColumn for IColumnSpec
  127. func (c *STristateColumn) ConvertFromValue(val interface{}) interface{} {
  128. switch sqlchemy.ConvertValueToTriState(val) {
  129. case tristate.True:
  130. return 1
  131. case tristate.None:
  132. return sql.NullInt32{}
  133. default:
  134. return 0
  135. }
  136. }
  137. // IsZero implementation of STristateColumn for IColumnSpec
  138. func (c *STristateColumn) IsZero(val interface{}) bool {
  139. if c.IsPointer() {
  140. bVal := val.(*tristate.TriState)
  141. return bVal == nil
  142. }
  143. bVal := val.(tristate.TriState)
  144. return bVal == tristate.None
  145. }
  146. // NewTristateColumn return an instance of STristateColumn
  147. func NewTristateColumn(table, name string, tagmap map[string]string, isPointer bool) STristateColumn {
  148. if _, ok := tagmap[sqlchemy.TAG_NULLABLE]; ok {
  149. // tristate always nullable
  150. delete(tagmap, sqlchemy.TAG_NULLABLE)
  151. }
  152. bc := STristateColumn{SBaseColumn: sqlchemy.NewBaseColumn(name, "INTEGER", tagmap, isPointer)}
  153. return bc
  154. }
  155. // SIntegerColumn represents an integer type of column, with value of integer
  156. type SIntegerColumn struct {
  157. sqlchemy.SBaseColumn
  158. // Is this column an autoincrement colmn
  159. isAutoIncrement bool
  160. // Is this column is a version column for this records
  161. isAutoVersion bool
  162. // If this column is an autoincrement column, AutoIncrementOffset records the initial offset
  163. autoIncrementOffset int64
  164. }
  165. // IsNumeric implementation of SIntegerColumn for IColumnSpec
  166. func (c *SIntegerColumn) IsNumeric() bool {
  167. return true
  168. }
  169. // DefinitionString implementation of SIntegerColumn for IColumnSpec
  170. func (c *SIntegerColumn) DefinitionString() string {
  171. buf := columnDefinitionBuffer(c)
  172. return buf.String()
  173. }
  174. // IsZero implementation of SIntegerColumn for IColumnSpec
  175. func (c *SIntegerColumn) IsZero(val interface{}) bool {
  176. if val == nil || (c.IsPointer() && reflect.ValueOf(val).IsNil()) {
  177. return true
  178. }
  179. switch intVal := val.(type) {
  180. case int8, int16, int32, int64, int, uint, uint8, uint16, uint32, uint64:
  181. return intVal == 0
  182. }
  183. return true
  184. }
  185. // ConvertFromString implementation of SBooleanColumn for IColumnSpec
  186. func (c *SIntegerColumn) ConvertFromString(str string) interface{} {
  187. return sqlchemy.ConvertValueToInteger(str)
  188. }
  189. func (c *SIntegerColumn) IsAutoVersion() bool {
  190. return c.isAutoVersion
  191. }
  192. func (c *SIntegerColumn) IsAutoIncrement() bool {
  193. return c.isAutoIncrement
  194. }
  195. func (c *SIntegerColumn) SetAutoIncrement(on bool) {
  196. c.isAutoIncrement = on
  197. }
  198. func (c *SIntegerColumn) SetAutoIncrementOffset(offset int64) {
  199. c.autoIncrementOffset = offset
  200. }
  201. // NewIntegerColumn return an instance of SIntegerColumn
  202. func NewIntegerColumn(name string, tagmap map[string]string, isPointer bool) SIntegerColumn {
  203. autoinc := false
  204. autoincBase := int64(0)
  205. tagmap, v, ok := utils.TagPop(tagmap, sqlchemy.TAG_AUTOINCREMENT)
  206. if ok {
  207. base, err := strconv.ParseInt(v, 10, 64)
  208. if err == nil && base > 0 {
  209. autoinc = true
  210. autoincBase = base
  211. } else {
  212. autoinc = utils.ToBool(v)
  213. }
  214. }
  215. autover := false
  216. tagmap, v, ok = utils.TagPop(tagmap, sqlchemy.TAG_AUTOVERSION)
  217. if ok {
  218. autover = utils.ToBool(v)
  219. }
  220. c := SIntegerColumn{
  221. SBaseColumn: sqlchemy.NewBaseColumn(name, "INTEGER", tagmap, isPointer),
  222. isAutoIncrement: autoinc,
  223. autoIncrementOffset: autoincBase,
  224. isAutoVersion: autover,
  225. }
  226. if autoinc {
  227. c.SetPrimary(true) // autoincrement column must be primary key
  228. c.SetNullable(false)
  229. c.isAutoVersion = false
  230. } else if autover {
  231. c.SetPrimary(false)
  232. c.SetNullable(false)
  233. if len(c.Default()) == 0 {
  234. c.SetDefault("0")
  235. }
  236. }
  237. return c
  238. }
  239. // SFloatColumn represents a float type column, e.g. float32 or float64
  240. type SFloatColumn struct {
  241. sqlchemy.SBaseColumn
  242. }
  243. // IsNumeric implementation of SFloatColumn for IColumnSpec
  244. func (c *SFloatColumn) IsNumeric() bool {
  245. return true
  246. }
  247. // DefinitionString implementation of SFloatColumn for IColumnSpec
  248. func (c *SFloatColumn) DefinitionString() string {
  249. buf := columnDefinitionBuffer(c)
  250. return buf.String()
  251. }
  252. // IsZero implementation of SFloatColumn for IColumnSpec
  253. func (c *SFloatColumn) IsZero(val interface{}) bool {
  254. if c.IsPointer() {
  255. switch val.(type) {
  256. case *float32:
  257. return val.(*float32) == nil
  258. case *float64:
  259. return val.(*float64) == nil
  260. }
  261. } else {
  262. switch val.(type) {
  263. case float32:
  264. return val.(float32) == 0.0
  265. case float64:
  266. return val.(float64) == 0.0
  267. }
  268. }
  269. return true
  270. }
  271. // ConvertFromString implementation of SBooleanColumn for IColumnSpec
  272. func (c *SFloatColumn) ConvertFromString(str string) interface{} {
  273. return sqlchemy.ConvertValueToFloat(str)
  274. }
  275. // NewFloatColumn returns an instance of SFloatColumn
  276. func NewFloatColumn(name string, tagmap map[string]string, isPointer bool) SFloatColumn {
  277. return SFloatColumn{SBaseColumn: sqlchemy.NewBaseColumn(name, "REAL", tagmap, isPointer)}
  278. }
  279. // STextColumn represents a text type of column
  280. type STextColumn struct {
  281. sqlchemy.SBaseColumn
  282. }
  283. // IsText implementation of STextColumn for IColumnSpec
  284. func (c *STextColumn) IsText() bool {
  285. return true
  286. }
  287. // IsSearchable implementation of STextColumn for IColumnSpec
  288. func (c *STextColumn) IsSearchable() bool {
  289. return true
  290. }
  291. // IsAscii implementation of STextColumn for IColumnSpec
  292. func (c *STextColumn) IsAscii() bool {
  293. return false
  294. }
  295. // DefinitionString implementation of STextColumn for IColumnSpec
  296. func (c *STextColumn) DefinitionString() string {
  297. buf := columnDefinitionBuffer(c)
  298. return buf.String()
  299. }
  300. // IsZero implementation of STextColumn for IColumnSpec
  301. func (c *STextColumn) IsZero(val interface{}) bool {
  302. if c.IsPointer() {
  303. return gotypes.IsNil(val)
  304. }
  305. return reflect.ValueOf(val).Len() == 0
  306. }
  307. // ConvertFromString implementation of SBooleanColumn for IColumnSpec
  308. func (c *STextColumn) ConvertFromString(str string) interface{} {
  309. return str
  310. }
  311. func (c *STextColumn) IsString() bool {
  312. return true
  313. }
  314. // NewTextColumn return an instance of STextColumn
  315. func NewTextColumn(name string, tagmap map[string]string, isPointer bool) STextColumn {
  316. return STextColumn{
  317. SBaseColumn: sqlchemy.NewBaseColumn(name, "TEXT", tagmap, isPointer),
  318. }
  319. }
  320. // STimeTypeColumn represents a Detetime type of column, e.g. DateTime
  321. type STimeTypeColumn struct {
  322. sqlchemy.SBaseColumn
  323. }
  324. // IsText implementation of STimeTypeColumn for IColumnSpec
  325. func (c *STimeTypeColumn) IsText() bool {
  326. return true
  327. }
  328. // DefinitionString implementation of STimeTypeColumn for IColumnSpec
  329. func (c *STimeTypeColumn) DefinitionString() string {
  330. buf := columnDefinitionBuffer(c)
  331. return buf.String()
  332. }
  333. // IsZero implementation of STimeTypeColumn for IColumnSpec
  334. func (c *STimeTypeColumn) IsZero(val interface{}) bool {
  335. if c.IsPointer() {
  336. bVal := val.(*time.Time)
  337. return bVal == nil
  338. }
  339. bVal := val.(time.Time)
  340. return bVal.IsZero()
  341. }
  342. // ConvertFromString implementation of SBooleanColumn for IColumnSpec
  343. func (c *STimeTypeColumn) ConvertFromString(str string) interface{} {
  344. return sqlchemy.ConvertValueToTime(str)
  345. }
  346. // ConvertFromValue implementation of STimeTypeColumn for IColumnSpec
  347. func (c *STimeTypeColumn) ConvertFromValue(val interface{}) interface{} {
  348. return sqlchemy.ConvertValueToTime(val)
  349. }
  350. // NewTimeTypeColumn return an instance of STimeTypeColumn
  351. func NewTimeTypeColumn(name string, tagmap map[string]string, isPointer bool) STimeTypeColumn {
  352. dc := STimeTypeColumn{
  353. SBaseColumn: sqlchemy.NewBaseColumn(name, "TEXT", tagmap, isPointer),
  354. }
  355. return dc
  356. }
  357. // SDateTimeColumn represents a DateTime type of column
  358. type SDateTimeColumn struct {
  359. STimeTypeColumn
  360. // Is this column a 'created_at' field, whichi records the time of create this record
  361. isCreatedAt bool
  362. // Is this column a 'updated_at' field, whichi records the time when this record was updated
  363. isUpdatedAt bool
  364. }
  365. // DefinitionString implementation of SDateTimeColumn for IColumnSpec
  366. func (c *SDateTimeColumn) DefinitionString() string {
  367. buf := columnDefinitionBuffer(c)
  368. return buf.String()
  369. }
  370. func (c *SDateTimeColumn) IsCreatedAt() bool {
  371. return c.isCreatedAt
  372. }
  373. func (c *SDateTimeColumn) IsUpdatedAt() bool {
  374. return c.isUpdatedAt
  375. }
  376. func (c *SDateTimeColumn) IsDateTime() bool {
  377. return true
  378. }
  379. // NewDateTimeColumn returns an instance of DateTime column
  380. func NewDateTimeColumn(name string, tagmap map[string]string, isPointer bool) SDateTimeColumn {
  381. createdAt := false
  382. updatedAt := false
  383. tagmap, v, ok := utils.TagPop(tagmap, sqlchemy.TAG_CREATE_TIMESTAMP)
  384. if ok {
  385. createdAt = utils.ToBool(v)
  386. }
  387. tagmap, v, ok = utils.TagPop(tagmap, sqlchemy.TAG_UPDATE_TIMESTAMP)
  388. if ok {
  389. updatedAt = utils.ToBool(v)
  390. }
  391. dtc := SDateTimeColumn{
  392. STimeTypeColumn: NewTimeTypeColumn(name, tagmap, isPointer),
  393. isCreatedAt: createdAt,
  394. isUpdatedAt: updatedAt,
  395. }
  396. return dtc
  397. }
  398. // CompoundColumn represents a column of compound tye, e.g. a JSON, an Array, or a struct
  399. type CompoundColumn struct {
  400. STextColumn
  401. sqlchemy.SBaseCompoundColumn
  402. }
  403. // DefinitionString implementation of CompoundColumn for IColumnSpec
  404. func (c *CompoundColumn) DefinitionString() string {
  405. buf := columnDefinitionBuffer(c)
  406. return buf.String()
  407. }
  408. // IsZero implementation of CompoundColumn for IColumnSpec
  409. func (c *CompoundColumn) IsZero(val interface{}) bool {
  410. if val == nil {
  411. return true
  412. }
  413. if c.IsPointer() && reflect.ValueOf(val).IsNil() {
  414. return true
  415. }
  416. return false
  417. }
  418. // ConvertFromString implementation of CompoundColumn for IColumnSpec
  419. func (c *CompoundColumn) ConvertFromString(str string) interface{} {
  420. return c.SBaseCompoundColumn.ConvertFromString(str)
  421. }
  422. // ConvertFromValue implementation of CompoundColumn for IColumnSpec
  423. func (c *CompoundColumn) ConvertFromValue(val interface{}) interface{} {
  424. return c.SBaseCompoundColumn.ConvertFromValue(val)
  425. }
  426. // NewCompoundColumn returns an instance of CompoundColumn
  427. func NewCompoundColumn(name string, tagmap map[string]string, isPointer bool) CompoundColumn {
  428. dtc := CompoundColumn{STextColumn: NewTextColumn(name, tagmap, isPointer)}
  429. return dtc
  430. }