column.go 17 KB

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