table.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  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 sqlchemy
  15. import (
  16. "fmt"
  17. "reflect"
  18. "sort"
  19. "sync"
  20. "yunion.io/x/log"
  21. "yunion.io/x/pkg/errors"
  22. "yunion.io/x/pkg/utils"
  23. )
  24. // ITableSpec is the interface represents a table
  25. type ITableSpec interface {
  26. // Insert performs an insert operation that insert one record at a time
  27. Insert(dt interface{}) error
  28. // InsertOrUpdate performs an atomic insert or update operation that insert a new record to update the record with current value
  29. InsertOrUpdate(dt interface{}) error
  30. // Update performs an update operation
  31. Update(dt interface{}, onUpdate func() error) (UpdateDiffs, error)
  32. // Increment performs a special update that do an atomic incremental update of the numeric fields
  33. Increment(diff, target interface{}) error
  34. // Decrement performs a special update that do an atomic decremental update of the numeric fields
  35. Decrement(diff, target interface{}) error
  36. // DataType returns the data type corresponding to the table
  37. DataType() reflect.Type
  38. // ColumnSpec returns the column definition of a spcific column
  39. ColumnSpec(name string) IColumnSpec
  40. // Name returns the name of the table
  41. Name() string
  42. // Columns returns the array of columns definitions
  43. Columns() []IColumnSpec
  44. // PrimaryColumns returns the array of columns of primary keys
  45. PrimaryColumns() []IColumnSpec
  46. // Indexes
  47. Indexes() []STableIndex
  48. // Expression returns expression of the table
  49. Expression() string
  50. // Instance returns an instance of STable for this spec
  51. Instance() *STable
  52. // DropForeignKeySQL returns the SQL statements to drop foreignkeys for this table
  53. DropForeignKeySQL() []string
  54. // AddIndex adds index to table
  55. AddIndex(unique bool, cols ...string) bool
  56. // SyncSQL returns SQL strings to synchronize the data and model definition of the table
  57. SyncSQL() []string
  58. // Sync forces synchronize the data and model definition of the table
  59. Sync() error
  60. // Fetch query a struct
  61. Fetch(dt interface{}) error
  62. // Database returns the database of this table
  63. Database() *SDatabase
  64. // Drop drops table
  65. Drop() error
  66. // getter of Extra Options
  67. GetExtraOptions() TableExtraOptions
  68. // setter of Extra Options
  69. SetExtraOptions(opts TableExtraOptions)
  70. }
  71. // STableSpec defines the table specification, which implements ITableSpec
  72. type STableSpec struct {
  73. structType reflect.Type
  74. name string
  75. _columns []IColumnSpec
  76. _indexes []STableIndex
  77. _contraints []STableConstraint
  78. extraOptions TableExtraOptions
  79. sDBReferer
  80. IsLinked bool
  81. syncedIndex bool
  82. syncIndexLock *sync.Mutex
  83. }
  84. // STable is an instance of table for query, system will automatically give a alias to this table
  85. type STable struct {
  86. spec ITableSpec
  87. alias string
  88. }
  89. // STableField represents a field in a table, implements IQueryField
  90. type STableField struct {
  91. table *STable
  92. spec IColumnSpec
  93. alias string
  94. }
  95. // NewTableSpecFromStruct generates STableSpec based on the information of a struct model
  96. func NewTableSpecFromStruct(s interface{}, name string) *STableSpec {
  97. return NewTableSpecFromStructWithDBName(s, name, DefaultDB)
  98. }
  99. func NewTableSpecFromStructWithDBName(s interface{}, name string, dbName DBName) *STableSpec {
  100. val := reflect.Indirect(reflect.ValueOf(s))
  101. st := val.Type()
  102. if st.Kind() != reflect.Struct {
  103. panic("expect Struct kind")
  104. }
  105. table := &STableSpec{
  106. name: name,
  107. structType: st,
  108. sDBReferer: sDBReferer{
  109. dbName: dbName,
  110. },
  111. syncedIndex: false,
  112. syncIndexLock: &sync.Mutex{},
  113. }
  114. return table
  115. }
  116. func NewTableSpecFromISpecWithDBName(spec ITableSpec, name string, dbName DBName, extraOpts TableExtraOptions) *STableSpec {
  117. table := &STableSpec{
  118. name: name,
  119. structType: spec.DataType(),
  120. sDBReferer: sDBReferer{
  121. dbName: dbName,
  122. },
  123. extraOptions: extraOpts,
  124. IsLinked: true,
  125. syncedIndex: false,
  126. syncIndexLock: &sync.Mutex{},
  127. }
  128. return table
  129. }
  130. // Name implementation of STableSpec for ITableSpec
  131. func (ts *STableSpec) Name() string {
  132. return ts.name
  133. }
  134. // Expression implementation of STableSpec for ITableSpec
  135. func (ts *STableSpec) Expression() string {
  136. qChar := ts.Database().backend.QuoteChar()
  137. return fmt.Sprintf("%s%s%s", qChar, ts.name, qChar)
  138. }
  139. func (ts *STableSpec) SyncColumnIndexes() error {
  140. if !ts.Exists() {
  141. return errors.Wrap(errors.ErrNotFound, "table not exists")
  142. }
  143. ts.syncIndexLock.Lock()
  144. defer ts.syncIndexLock.Unlock()
  145. if ts.syncedIndex {
  146. return nil
  147. }
  148. cols, err := ts.Database().backend.FetchTableColumnSpecs(ts)
  149. if err != nil {
  150. return errors.Wrap(err, "FetchTableColumnSpecs")
  151. }
  152. if len(cols) != len(ts._columns) {
  153. colsName := map[string]bool{}
  154. for _, col := range ts._columns {
  155. colsName[col.Name()] = true
  156. }
  157. removed := []string{}
  158. for _, col := range cols {
  159. if _, ok := colsName[col.Name()]; !ok {
  160. removed = append(removed, col.Name())
  161. }
  162. }
  163. return errors.Wrapf(errors.ErrInvalidStatus, "ts %s col %d != actual col %d need remove columns %s", ts.Name(), len(ts._columns), len(cols), removed)
  164. }
  165. for i := range cols {
  166. cols[i].SetColIndex(i)
  167. }
  168. // sort colums
  169. sort.Slice(cols, func(i, j int) bool {
  170. return compareColumnSpec(cols[i], cols[j]) < 0
  171. })
  172. sort.Slice(ts._columns, func(i, j int) bool {
  173. return compareColumnSpec(ts._columns[i], ts._columns[j]) < 0
  174. })
  175. // compare columns and assign colindex
  176. for i := range ts._columns {
  177. comp := compareColumnSpec(cols[i], ts._columns[i])
  178. if comp != 0 {
  179. return errors.Wrapf(errors.ErrInvalidStatus, "colname %s != %s", cols[i].Name(), ts._columns[i].Name())
  180. }
  181. ts._columns[i].SetColIndex(cols[i].GetColIndex())
  182. }
  183. // sort columns according to colindex
  184. sort.Slice(ts._columns, func(i, j int) bool {
  185. return compareColumnIndex(ts._columns[i], ts._columns[j]) < 0
  186. })
  187. ts.syncedIndex = true
  188. return nil
  189. }
  190. // Clone makes a clone of a table, so we may create a new table of the same schema
  191. func (ts *STableSpec) Clone(name string, autoIncOffset int64) *STableSpec {
  192. nts, _ := ts.CloneWithSyncColumnOrder(name, autoIncOffset, false)
  193. return nts
  194. }
  195. // Clone makes a clone of a table, so we may create a new table of the same schema
  196. func (ts *STableSpec) CloneWithSyncColumnOrder(name string, autoIncOffset int64, syncColOrder bool) (*STableSpec, error) {
  197. if ts.Exists() && syncColOrder {
  198. // if table exists, sync column index
  199. err := ts.SyncColumnIndexes()
  200. if err != nil {
  201. return nil, errors.Wrap(err, "SyncColumnIndexes")
  202. }
  203. }
  204. columns := ts.Columns()
  205. newCols := make([]IColumnSpec, len(columns))
  206. for i := range newCols {
  207. col := columns[i]
  208. if col.IsAutoIncrement() {
  209. colValue := reflect.Indirect(reflect.ValueOf(col))
  210. newColValue := reflect.Indirect(reflect.New(colValue.Type()))
  211. newColValue.Set(colValue)
  212. newCol := newColValue.Addr().Interface().(IColumnSpec)
  213. newCol.SetAutoIncrementOffset(autoIncOffset)
  214. newCols[i] = newCol
  215. } else {
  216. newCols[i] = col
  217. }
  218. }
  219. nts := &STableSpec{
  220. structType: ts.structType,
  221. name: name,
  222. _columns: newCols,
  223. _contraints: ts._contraints,
  224. sDBReferer: ts.sDBReferer,
  225. syncedIndex: false,
  226. syncIndexLock: &sync.Mutex{},
  227. }
  228. newIndexes := make([]STableIndex, len(ts._indexes))
  229. for i := range ts._indexes {
  230. newIndexes[i] = ts._indexes[i].clone(nts)
  231. }
  232. nts._indexes = newIndexes
  233. return nts, nil
  234. }
  235. // Columns implementation of STableSpec for ITableSpec
  236. func (ts *STableSpec) Columns() []IColumnSpec {
  237. ts.syncIndexLock.Lock()
  238. defer ts.syncIndexLock.Unlock()
  239. if ts._columns == nil {
  240. val := reflect.Indirect(reflect.New(ts.structType))
  241. ts.struct2TableSpec(val)
  242. }
  243. return ts._columns
  244. }
  245. // PrimaryColumns implementation of STableSpec for ITableSpec
  246. func (ts *STableSpec) PrimaryColumns() []IColumnSpec {
  247. ret := make([]IColumnSpec, 0)
  248. columns := ts.Columns()
  249. for i := range columns {
  250. if columns[i].IsPrimary() {
  251. ret = append(ret, columns[i])
  252. }
  253. }
  254. return ret
  255. }
  256. // Indexes implementation of STableSpec for ITableSpec
  257. func (ts *STableSpec) Indexes() []STableIndex {
  258. return ts._indexes
  259. }
  260. // DataType implementation of STableSpec for ITableSpec
  261. func (ts *STableSpec) DataType() reflect.Type {
  262. return ts.structType
  263. }
  264. // CreateSQL returns the SQL for creating this table
  265. func (ts *STableSpec) CreateSQLs() []string {
  266. return ts.Database().backend.GetCreateSQLs(ts)
  267. }
  268. // NewTableInstance return an new table instance from an ITableSpec
  269. func NewTableInstance(ts ITableSpec) *STable {
  270. table := STable{spec: ts, alias: getTableAliasName()}
  271. return &table
  272. }
  273. // Instance return an new table instance from an instance of STableSpec
  274. func (ts *STableSpec) Instance() *STable {
  275. return NewTableInstance(ts)
  276. }
  277. // ColumnSpec implementation of STableSpec for ITableSpec
  278. func (ts *STableSpec) ColumnSpec(name string) IColumnSpec {
  279. for _, c := range ts.Columns() {
  280. if c.Name() == name {
  281. return c
  282. }
  283. }
  284. return nil
  285. }
  286. // Field implementation of STableSpec for IQuerySource
  287. func (tbl *STable) Field(name string, alias ...string) IQueryField {
  288. // name = reflectutils.StructFieldName(name)
  289. name = utils.CamelSplit(name, "_")
  290. spec := tbl.spec.ColumnSpec(name)
  291. if spec == nil {
  292. log.Warningf("column %s not found in table %s", name, tbl.spec.Name())
  293. return nil
  294. }
  295. col := STableField{table: tbl, spec: spec}
  296. if len(alias) > 0 {
  297. return col.Label(alias[0])
  298. }
  299. return &col
  300. }
  301. // Fields implementation of STable for IQuerySource
  302. func (tbl *STable) Fields() []IQueryField {
  303. ret := make([]IQueryField, 0)
  304. for _, c := range tbl.spec.Columns() {
  305. ret = append(ret, tbl.Field(c.Name()))
  306. }
  307. return ret
  308. }
  309. // Database implementaion of STable for IQuerySource
  310. func (tbl *STable) database() *SDatabase {
  311. return tbl.spec.Database()
  312. }
  313. // Expression implementation of STable for IQuerySource
  314. func (tbl *STable) Expression() string {
  315. return tbl.spec.Expression()
  316. }
  317. // Alias implementation of STable for IQuerySource
  318. func (tbl *STable) Alias() string {
  319. return tbl.alias
  320. }
  321. // Variables implementation of STable for IQuerySource
  322. func (tbl *STable) Variables() []interface{} {
  323. return []interface{}{}
  324. }
  325. // Expression implementation of STableField for IQueryField
  326. func (c *STableField) Expression() string {
  327. qChar := c.database().backend.QuoteChar()
  328. return fmt.Sprintf("%s%s%s.%s%s%s", qChar, c.table.Alias(), qChar, qChar, c.spec.Name(), qChar)
  329. }
  330. // Name implementation of STableField for IQueryField
  331. func (c *STableField) Name() string {
  332. if len(c.alias) > 0 {
  333. return c.alias
  334. }
  335. return c.spec.Name()
  336. }
  337. // Reference implementation of STableField for IQueryField
  338. func (c *STableField) Reference() string {
  339. qChar := c.database().backend.QuoteChar()
  340. return fmt.Sprintf("%s%s%s.%s%s%s", qChar, c.table.Alias(), qChar, qChar, c.Name(), qChar)
  341. }
  342. // Label implementation of STableField for IQueryField
  343. func (c *STableField) Label(label string) IQueryField {
  344. if len(label) > 0 {
  345. // label make a copy of the field
  346. nc := *c
  347. nc.alias = label
  348. return &nc
  349. } else {
  350. return c
  351. }
  352. }
  353. // Variables implementation of STableField for IQueryField
  354. func (c *STableField) Variables() []interface{} {
  355. return nil
  356. }
  357. // ConvertFromValue implementation of STableField for IQueryField
  358. func (c *STableField) ConvertFromValue(val interface{}) interface{} {
  359. return c.spec.ConvertFromValue(val)
  360. }
  361. // database implementation of STableField for IQueryField
  362. func (c *STableField) database() *SDatabase {
  363. return c.table.database()
  364. }