column.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  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 mysql
  15. import (
  16. "bytes"
  17. "database/sql"
  18. "fmt"
  19. "reflect"
  20. "strconv"
  21. "strings"
  22. "time"
  23. "yunion.io/x/log"
  24. "yunion.io/x/pkg/gotypes"
  25. "yunion.io/x/pkg/tristate"
  26. "yunion.io/x/pkg/utils"
  27. "yunion.io/x/sqlchemy"
  28. )
  29. func columnDefinitionBuffer(c sqlchemy.IColumnSpec) bytes.Buffer {
  30. var buf bytes.Buffer
  31. buf.WriteByte('`')
  32. buf.WriteString(c.Name())
  33. buf.WriteByte('`')
  34. buf.WriteByte(' ')
  35. buf.WriteString(c.ColType())
  36. extra := c.ExtraDefs()
  37. if len(extra) > 0 {
  38. buf.WriteString(" ")
  39. buf.WriteString(extra)
  40. }
  41. if !c.IsNullable() {
  42. buf.WriteString(" NOT NULL")
  43. }
  44. def := c.Default()
  45. defOk := c.IsSupportDefault()
  46. if def != "" {
  47. if !defOk {
  48. panic(fmt.Errorf("column %q type %q does not support having default value: %q",
  49. c.Name(), c.ColType(), def,
  50. ))
  51. }
  52. def = sqlchemy.GetStringValue(c.ConvertFromString(def))
  53. buf.WriteString(" DEFAULT ")
  54. if c.IsText() {
  55. buf.WriteByte('\'')
  56. }
  57. buf.WriteString(def)
  58. if c.IsText() {
  59. buf.WriteByte('\'')
  60. }
  61. }
  62. return buf
  63. }
  64. // SBooleanColumn represents a boolean type column, which is a int(1) for mysql, with value of true or false
  65. type SBooleanColumn struct {
  66. sqlchemy.SBaseWidthColumn
  67. }
  68. // DefinitionString implementation of SBooleanColumn for IColumnSpec
  69. func (c *SBooleanColumn) DefinitionString() string {
  70. buf := columnDefinitionBuffer(c)
  71. return buf.String()
  72. }
  73. // ConvertFromString implementation of SBooleanColumn for IColumnSpec
  74. func (c *SBooleanColumn) ConvertFromString(str string) interface{} {
  75. switch sqlchemy.ConvertValueToBool(str) {
  76. case true:
  77. return 1
  78. default:
  79. return 0
  80. }
  81. }
  82. // ConvertFromValue implementation of STristateColumn for IColumnSpec
  83. func (c *SBooleanColumn) ConvertFromValue(val interface{}) interface{} {
  84. switch sqlchemy.ConvertValueToBool(val) {
  85. case true:
  86. return 1
  87. default:
  88. return 0
  89. }
  90. }
  91. // IsZero implementation of SBooleanColumn for IColumnSpec
  92. func (c *SBooleanColumn) IsZero(val interface{}) bool {
  93. if c.IsPointer() {
  94. bVal := val.(*bool)
  95. return bVal == nil
  96. }
  97. bVal := val.(bool)
  98. return bVal == false
  99. }
  100. // NewBooleanColumn return an instance of SBooleanColumn
  101. func NewBooleanColumn(name string, tagmap map[string]string, isPointer bool) SBooleanColumn {
  102. bc := SBooleanColumn{SBaseWidthColumn: sqlchemy.NewBaseWidthColumn(name, "TINYINT", tagmap, isPointer)}
  103. if !bc.IsPointer() && len(bc.Default()) > 0 && bc.ConvertFromString(bc.Default()) == 1 {
  104. msg := fmt.Sprintf("Non-pointer boolean column should not default true: %s(%s)", name, tagmap)
  105. panic(msg)
  106. }
  107. return bc
  108. }
  109. // STristateColumn represents a tristate type column, with value of true, false or none
  110. type STristateColumn struct {
  111. sqlchemy.SBaseWidthColumn
  112. }
  113. // DefinitionString implementation of STristateColumn for IColumnSpec
  114. func (c *STristateColumn) DefinitionString() string {
  115. buf := columnDefinitionBuffer(c)
  116. return buf.String()
  117. }
  118. // ConvertFromString implementation of STristateColumn for IColumnSpec
  119. func (c *STristateColumn) ConvertFromString(str string) interface{} {
  120. switch sqlchemy.ConvertValueToTriState(str) {
  121. case tristate.True:
  122. return 1
  123. case tristate.False:
  124. return 0
  125. default:
  126. return sql.NullInt32{}
  127. }
  128. }
  129. // ConvertFromValue implementation of STristateColumn for IColumnSpec
  130. func (c *STristateColumn) ConvertFromValue(val interface{}) interface{} {
  131. switch sqlchemy.ConvertValueToTriState(val) {
  132. case tristate.True:
  133. return 1
  134. case tristate.False:
  135. return 0
  136. default:
  137. return sql.NullInt32{}
  138. }
  139. }
  140. // IsZero implementation of STristateColumn for IColumnSpec
  141. func (c *STristateColumn) IsZero(val interface{}) bool {
  142. if c.IsPointer() {
  143. bVal := val.(*tristate.TriState)
  144. return bVal == nil
  145. }
  146. bVal := val.(tristate.TriState)
  147. return bVal == tristate.None
  148. }
  149. // NewTristateColumn return an instance of STristateColumn
  150. func NewTristateColumn(table, name string, tagmap map[string]string, isPointer bool) STristateColumn {
  151. if _, ok := tagmap[sqlchemy.TAG_NULLABLE]; ok {
  152. // simply warning, for backward compatiblity reason
  153. // tristate always nullable
  154. // delete(tagmap, sqlchemy.TAG_NULLABLE)
  155. log.Warningf("%s TristateColumn %s should have no nullable tag", table, name)
  156. }
  157. bc := STristateColumn{SBaseWidthColumn: sqlchemy.NewBaseWidthColumn(name, "TINYINT", tagmap, isPointer)}
  158. return bc
  159. }
  160. // SIntegerColumn represents an integer type of column, with value of integer
  161. type SIntegerColumn struct {
  162. sqlchemy.SBaseWidthColumn
  163. // Is this column an autoincrement colmn
  164. isAutoIncrement bool
  165. // Is this column is a version column for this records
  166. isAutoVersion bool
  167. // Is this column a unsigned integer?
  168. isUnsigned bool
  169. // If this column is an autoincrement column, AutoIncrementOffset records the initial offset
  170. autoIncrementOffset int64
  171. }
  172. // IsNumeric implementation of SIntegerColumn for IColumnSpec
  173. func (c *SIntegerColumn) IsNumeric() bool {
  174. return true
  175. }
  176. // ExtraDefs implementation of SIntegerColumn for IColumnSpec
  177. func (c *SIntegerColumn) ExtraDefs() string {
  178. if c.isAutoIncrement {
  179. return "AUTO_INCREMENT"
  180. }
  181. return ""
  182. }
  183. // DefinitionString implementation of SIntegerColumn for IColumnSpec
  184. func (c *SIntegerColumn) DefinitionString() string {
  185. buf := columnDefinitionBuffer(c)
  186. return buf.String()
  187. }
  188. // IsZero implementation of SIntegerColumn for IColumnSpec
  189. func (c *SIntegerColumn) IsZero(val interface{}) bool {
  190. if val == nil || (c.IsPointer() && reflect.ValueOf(val).IsNil()) {
  191. return true
  192. }
  193. switch intVal := val.(type) {
  194. case int8, int16, int32, int64, int, uint, uint8, uint16, uint32, uint64:
  195. return intVal == 0
  196. }
  197. return true
  198. }
  199. func (c *SIntegerColumn) int2int(intval int64) interface{} {
  200. colType := c.ColType()
  201. parts := strings.Split(colType, "(")
  202. switch parts[0] {
  203. case "TINYINT":
  204. return int8(intval)
  205. case "SMALLINT":
  206. return int16(intval)
  207. case "INT":
  208. return int(intval)
  209. case "BIGINT":
  210. return int64(intval)
  211. case "UNSIGNED TINYINT":
  212. return uint8(intval)
  213. case "UNSIGNED SMALLINT":
  214. return uint16(intval)
  215. case "UNSIGNED INT":
  216. return uint(intval)
  217. case "UNSIGNED BIGINT":
  218. return uint64(intval)
  219. }
  220. panic(fmt.Sprintf("unsupported type %s", c.ColType()))
  221. }
  222. // ConvertFromString implementation of SBooleanColumn for IColumnSpec
  223. func (c *SIntegerColumn) ConvertFromString(str string) interface{} {
  224. intval := sqlchemy.ConvertValueToInteger(str)
  225. return c.int2int(intval)
  226. }
  227. // ColType implementation of SIntegerColumn for IColumnSpec
  228. func (c *SIntegerColumn) ColType() string {
  229. str := (&c.SBaseWidthColumn).ColType()
  230. if c.isUnsigned {
  231. str += " UNSIGNED"
  232. }
  233. return str
  234. }
  235. func (c *SIntegerColumn) IsAutoVersion() bool {
  236. return c.isAutoVersion
  237. }
  238. func (c *SIntegerColumn) IsAutoIncrement() bool {
  239. return c.isAutoIncrement
  240. }
  241. func (c *SIntegerColumn) AutoIncrementOffset() int64 {
  242. return c.autoIncrementOffset
  243. }
  244. func (c *SIntegerColumn) SetAutoIncrement(on bool) {
  245. c.isAutoIncrement = on
  246. }
  247. func (c *SIntegerColumn) SetAutoIncrementOffset(offset int64) {
  248. c.autoIncrementOffset = offset
  249. }
  250. // NewIntegerColumn return an instance of SIntegerColumn
  251. func NewIntegerColumn(name string, sqltype string, unsigned bool, tagmap map[string]string, isPointer bool) SIntegerColumn {
  252. autoinc := false
  253. autoincBase := int64(0)
  254. tagmap, v, ok := utils.TagPop(tagmap, sqlchemy.TAG_AUTOINCREMENT)
  255. if ok {
  256. base, err := strconv.ParseInt(v, 10, 64)
  257. if err == nil && base > 0 {
  258. autoinc = true
  259. autoincBase = base
  260. } else {
  261. autoinc = utils.ToBool(v)
  262. }
  263. }
  264. autover := false
  265. tagmap, v, ok = utils.TagPop(tagmap, sqlchemy.TAG_AUTOVERSION)
  266. if ok {
  267. autover = utils.ToBool(v)
  268. }
  269. c := SIntegerColumn{
  270. SBaseWidthColumn: sqlchemy.NewBaseWidthColumn(name, sqltype, tagmap, isPointer),
  271. isAutoIncrement: autoinc,
  272. autoIncrementOffset: autoincBase,
  273. isAutoVersion: autover,
  274. isUnsigned: unsigned,
  275. }
  276. if autoinc {
  277. c.SetPrimary(true) // autoincrement column must be primary key
  278. c.SetNullable(false)
  279. c.isAutoVersion = false
  280. } else if autover {
  281. c.SetPrimary(false)
  282. c.SetNullable(false)
  283. if len(c.Default()) == 0 {
  284. c.SetDefault("0")
  285. }
  286. }
  287. return c
  288. }
  289. // SFloatColumn represents a float type column, e.g. float32 or float64
  290. type SFloatColumn struct {
  291. sqlchemy.SBaseColumn
  292. }
  293. // IsNumeric implementation of SFloatColumn for IColumnSpec
  294. func (c *SFloatColumn) IsNumeric() bool {
  295. return true
  296. }
  297. // DefinitionString implementation of SFloatColumn for IColumnSpec
  298. func (c *SFloatColumn) DefinitionString() string {
  299. buf := columnDefinitionBuffer(c)
  300. return buf.String()
  301. }
  302. // IsZero implementation of SFloatColumn for IColumnSpec
  303. func (c *SFloatColumn) IsZero(val interface{}) bool {
  304. if c.IsPointer() {
  305. switch val.(type) {
  306. case *float32:
  307. return val.(*float32) == nil
  308. case *float64:
  309. return val.(*float64) == nil
  310. }
  311. } else {
  312. switch val.(type) {
  313. case float32:
  314. return val.(float32) == 0.0
  315. case float64:
  316. return val.(float64) == 0.0
  317. }
  318. }
  319. return true
  320. }
  321. // ConvertFromString implementation of SBooleanColumn for IColumnSpec
  322. func (c *SFloatColumn) ConvertFromString(str string) interface{} {
  323. return sqlchemy.ConvertValueToFloat(str)
  324. }
  325. // NewFloatColumn returns an instance of SFloatColumn
  326. func NewFloatColumn(name string, sqlType string, tagmap map[string]string, isPointer bool) SFloatColumn {
  327. return SFloatColumn{SBaseColumn: sqlchemy.NewBaseColumn(name, sqlType, tagmap, isPointer)}
  328. }
  329. // SDecimalColumn represents a DECIMAL type of column, i.e. a float with fixed width of digits
  330. type SDecimalColumn struct {
  331. sqlchemy.SBaseWidthColumn
  332. Precision int
  333. }
  334. // ColType implementation of SDecimalColumn for IColumnSpec
  335. func (c *SDecimalColumn) ColType() string {
  336. str := c.SBaseWidthColumn.ColType()
  337. if str[len(str)-1] == ')' {
  338. str = str[:len(str)-1]
  339. }
  340. return fmt.Sprintf("%s, %d)", str, c.Precision)
  341. }
  342. // IsNumeric implementation of SDecimalColumn for IColumnSpec
  343. func (c *SDecimalColumn) IsNumeric() bool {
  344. return true
  345. }
  346. // DefinitionString implementation of SDecimalColumn for IColumnSpec
  347. func (c *SDecimalColumn) DefinitionString() string {
  348. buf := columnDefinitionBuffer(c)
  349. return buf.String()
  350. }
  351. // IsZero implementation of SDecimalColumn for IColumnSpec
  352. func (c *SDecimalColumn) IsZero(val interface{}) bool {
  353. if c.IsPointer() {
  354. switch val.(type) {
  355. case *float32:
  356. return val.(*float32) == nil
  357. case *float64:
  358. return val.(*float64) == nil
  359. }
  360. } else {
  361. switch val.(type) {
  362. case float32:
  363. return val.(float32) == 0.0
  364. case float64:
  365. return val.(float64) == 0.0
  366. }
  367. }
  368. return true
  369. }
  370. // ConvertFromString implementation of SBooleanColumn for IColumnSpec
  371. func (c *SDecimalColumn) ConvertFromString(str string) interface{} {
  372. return sqlchemy.ConvertValueToFloat(str)
  373. }
  374. // NewDecimalColumn returns an instance of SDecimalColumn
  375. func NewDecimalColumn(name string, tagmap map[string]string, isPointer bool) SDecimalColumn {
  376. tagmap, v, ok := utils.TagPop(tagmap, sqlchemy.TAG_PRECISION)
  377. if !ok {
  378. panic(fmt.Sprintf("Field %q of float misses precision tag", name))
  379. }
  380. prec, err := strconv.Atoi(v)
  381. if err != nil {
  382. panic(fmt.Sprintf("Field precision of %q shoud be integer (%q)", name, v))
  383. }
  384. return SDecimalColumn{
  385. SBaseWidthColumn: sqlchemy.NewBaseWidthColumn(name, "DECIMAL", tagmap, isPointer),
  386. Precision: prec,
  387. }
  388. }
  389. // STextColumn represents a text type of column
  390. type STextColumn struct {
  391. sqlchemy.SBaseWidthColumn
  392. Charset string
  393. }
  394. // IsSupportDefault implementation of STextColumn for IColumnSpec
  395. func (c *STextColumn) IsSupportDefault() bool {
  396. // https://stackoverflow.com/questions/3466872/why-cant-a-text-column-have-a-default-value-in-mysql
  397. // MySQL does not support default for TEXT/BLOB
  398. if c.SBaseColumn.ColType() == "VARCHAR" {
  399. return true
  400. }
  401. return false
  402. }
  403. // ColType implementation of STextColumn for IColumnSpec
  404. func (c *STextColumn) ColType() string {
  405. var charset string
  406. var collate string
  407. switch c.Charset {
  408. case "ascii":
  409. charset = "ascii"
  410. collate = "ascii_general_ci"
  411. case "utf8":
  412. charset = "utf8mb4"
  413. collate = "utf8mb4_unicode_ci"
  414. }
  415. return fmt.Sprintf("%s CHARACTER SET '%s' COLLATE '%s'", c.SBaseWidthColumn.ColType(), charset, collate)
  416. }
  417. // IsText implementation of STextColumn for IColumnSpec
  418. func (c *STextColumn) IsText() bool {
  419. return true
  420. }
  421. // IsSearchable implementation of STextColumn for IColumnSpec
  422. func (c *STextColumn) IsSearchable() bool {
  423. return true
  424. }
  425. // IsAscii implementation of STextColumn for IColumnSpec
  426. func (c *STextColumn) IsAscii() bool {
  427. if c.Charset == "ascii" {
  428. return true
  429. }
  430. return false
  431. }
  432. // DefinitionString implementation of STextColumn for IColumnSpec
  433. func (c *STextColumn) DefinitionString() string {
  434. buf := columnDefinitionBuffer(c)
  435. return buf.String()
  436. }
  437. // IsZero implementation of STextColumn for IColumnSpec
  438. func (c *STextColumn) IsZero(val interface{}) bool {
  439. if c.IsPointer() {
  440. return gotypes.IsNil(val)
  441. }
  442. return reflect.ValueOf(val).Len() == 0
  443. }
  444. // ConvertFromString implementation of SBooleanColumn for IColumnSpec
  445. func (c *STextColumn) ConvertFromString(str string) interface{} {
  446. return str
  447. }
  448. func (c *STextColumn) IsString() bool {
  449. return true
  450. }
  451. // NewTextColumn return an instance of STextColumn
  452. func NewTextColumn(name string, sqlType string, tagmap map[string]string, isPointer bool) STextColumn {
  453. tagmap, charset, _ := utils.TagPop(tagmap, sqlchemy.TAG_CHARSET)
  454. if len(charset) == 0 {
  455. charset = "utf8"
  456. } else if charset != "utf8" && charset != "ascii" {
  457. panic(fmt.Sprintf("Unsupported charset %s for %s", charset, name))
  458. }
  459. return STextColumn{
  460. SBaseWidthColumn: sqlchemy.NewBaseWidthColumn(name, sqlType, tagmap, isPointer),
  461. Charset: charset,
  462. }
  463. }
  464. // STimeTypeColumn represents a Detetime type of column, e.g. DateTime
  465. type STimeTypeColumn struct {
  466. sqlchemy.SBaseColumn
  467. }
  468. // IsText implementation of STimeTypeColumn for IColumnSpec
  469. func (c *STimeTypeColumn) IsText() bool {
  470. return true
  471. }
  472. // DefinitionString implementation of STimeTypeColumn for IColumnSpec
  473. func (c *STimeTypeColumn) DefinitionString() string {
  474. buf := columnDefinitionBuffer(c)
  475. return buf.String()
  476. }
  477. // IsZero implementation of STimeTypeColumn for IColumnSpec
  478. func (c *STimeTypeColumn) IsZero(val interface{}) bool {
  479. if c.IsPointer() {
  480. bVal := val.(*time.Time)
  481. return bVal == nil
  482. }
  483. bVal := val.(time.Time)
  484. return bVal.IsZero()
  485. }
  486. // ConvertFromString implementation of SBooleanColumn for IColumnSpec
  487. func (c *STimeTypeColumn) ConvertFromString(str string) interface{} {
  488. return sqlchemy.ConvertValueToTime(str)
  489. }
  490. // ConvertFromValue implementation of STimeTypeColumn for IColumnSpec
  491. func (c *STimeTypeColumn) ConvertFromValue(val interface{}) interface{} {
  492. return sqlchemy.ConvertValueToTime(val)
  493. }
  494. // NewTimeTypeColumn return an instance of STimeTypeColumn
  495. func NewTimeTypeColumn(name string, typeStr string, tagmap map[string]string, isPointer bool) STimeTypeColumn {
  496. dc := STimeTypeColumn{
  497. sqlchemy.NewBaseColumn(name, typeStr, tagmap, isPointer),
  498. }
  499. return dc
  500. }
  501. // SDateTimeColumn represents a DateTime type of column
  502. type SDateTimeColumn struct {
  503. STimeTypeColumn
  504. // Is this column a 'created_at' field, whichi records the time of create this record
  505. isCreatedAt bool
  506. // Is this column a 'updated_at' field, whichi records the time when this record was updated
  507. isUpdatedAt bool
  508. }
  509. // DefinitionString implementation of SDateTimeColumn for IColumnSpec
  510. func (c *SDateTimeColumn) DefinitionString() string {
  511. buf := columnDefinitionBuffer(c)
  512. return buf.String()
  513. }
  514. func (c *SDateTimeColumn) IsCreatedAt() bool {
  515. return c.isCreatedAt
  516. }
  517. func (c *SDateTimeColumn) IsUpdatedAt() bool {
  518. return c.isUpdatedAt
  519. }
  520. func (c *SDateTimeColumn) IsDateTime() bool {
  521. return true
  522. }
  523. // NewDateTimeColumn returns an instance of DateTime column
  524. func NewDateTimeColumn(name string, tagmap map[string]string, isPointer bool) SDateTimeColumn {
  525. createdAt := false
  526. updatedAt := false
  527. tagmap, v, ok := utils.TagPop(tagmap, sqlchemy.TAG_CREATE_TIMESTAMP)
  528. if ok {
  529. createdAt = utils.ToBool(v)
  530. }
  531. tagmap, v, ok = utils.TagPop(tagmap, sqlchemy.TAG_UPDATE_TIMESTAMP)
  532. if ok {
  533. updatedAt = utils.ToBool(v)
  534. }
  535. dtc := SDateTimeColumn{
  536. NewTimeTypeColumn(name, "DATETIME", tagmap, isPointer),
  537. createdAt, updatedAt,
  538. }
  539. return dtc
  540. }
  541. // CompoundColumn represents a column of compound tye, e.g. a JSON, an Array, or a struct
  542. type CompoundColumn struct {
  543. STextColumn
  544. sqlchemy.SBaseCompoundColumn
  545. }
  546. // DefinitionString implementation of CompoundColumn for IColumnSpec
  547. func (c *CompoundColumn) DefinitionString() string {
  548. buf := columnDefinitionBuffer(c)
  549. return buf.String()
  550. }
  551. // IsZero implementation of CompoundColumn for IColumnSpec
  552. func (c *CompoundColumn) IsZero(val interface{}) bool {
  553. if val == nil {
  554. return true
  555. }
  556. if c.IsPointer() && reflect.ValueOf(val).IsNil() {
  557. return true
  558. }
  559. return false
  560. }
  561. // ConvertFromString implementation of CompoundColumn for IColumnSpec
  562. func (c *CompoundColumn) ConvertFromString(str string) interface{} {
  563. return c.SBaseCompoundColumn.ConvertFromString(str)
  564. }
  565. // ConvertFromValue implementation of CompoundColumn for IColumnSpec
  566. func (c *CompoundColumn) ConvertFromValue(val interface{}) interface{} {
  567. return c.SBaseCompoundColumn.ConvertFromValue(val)
  568. }
  569. // NewCompoundColumn returns an instance of CompoundColumn
  570. func NewCompoundColumn(name string, sqlType string, tagmap map[string]string, isPointer bool) CompoundColumn {
  571. dtc := CompoundColumn{STextColumn: NewTextColumn(name, sqlType, tagmap, isPointer)}
  572. return dtc
  573. }