table.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. 'use strict'
  2. const TYPES = require('./datatypes').TYPES
  3. const declareType = require('./datatypes').declare
  4. const objectHasProperty = require('./utils').objectHasProperty
  5. const MAX = 65535 // (1 << 16) - 1
  6. const JSON_COLUMN_ID = 'JSON_F52E2B61-18A1-11d1-B105-00805F49916B'
  7. function Table (name) {
  8. if (name) {
  9. const parsed = Table.parseName(name)
  10. this.name = parsed.name
  11. this.schema = parsed.schema
  12. this.database = parsed.database
  13. this.path = (this.database ? `[${this.database}].` : '') + (this.schema ? `[${this.schema}].` : '') + `[${this.name}]`
  14. this.temporary = this.name.charAt(0) === '#'
  15. }
  16. this.columns = []
  17. this.rows = []
  18. Object.defineProperty(this.columns, 'add', {
  19. value (name, column, options) {
  20. if (column == null) {
  21. throw new Error('Column data type is not defined.')
  22. }
  23. if (column instanceof Function) {
  24. column = column()
  25. }
  26. options = options || {}
  27. column.name = name
  28. column.nullable = options.nullable
  29. column.primary = options.primary
  30. if (objectHasProperty(options, 'length')) {
  31. column.length = options.length
  32. }
  33. return this.push(column)
  34. }
  35. }
  36. )
  37. Object.defineProperty(this.rows, 'add', {
  38. value () {
  39. return this.push(Array.prototype.slice.call(arguments))
  40. }
  41. }
  42. )
  43. }
  44. /*
  45. @private
  46. */
  47. Table.prototype._makeBulk = function _makeBulk () {
  48. for (let i = 0; i < this.columns.length; i++) {
  49. const col = this.columns[i]
  50. switch (col.type) {
  51. case TYPES.DateTime:
  52. case TYPES.DateTime2:
  53. for (let j = 0; j < this.rows.length; j++) {
  54. const dateValue = this.rows[j][i]
  55. if (typeof dateValue === 'string' || typeof dateValue === 'number') {
  56. const date = new Date(dateValue)
  57. if (isNaN(date.getDate())) {
  58. throw new TypeError('Invalid date value passed to bulk rows')
  59. }
  60. this.rows[j][i] = date
  61. }
  62. }
  63. break
  64. case TYPES.Xml:
  65. col.type = TYPES.NVarChar(MAX).type
  66. break
  67. case TYPES.UDT:
  68. case TYPES.Geography:
  69. case TYPES.Geometry:
  70. col.type = TYPES.VarBinary(MAX).type
  71. break
  72. default:
  73. break
  74. }
  75. }
  76. return this
  77. }
  78. Table.prototype.declare = function declare () {
  79. const pkey = this.columns.filter(col => col.primary === true).map(col => col.name)
  80. const cols = this.columns.map(col => {
  81. const def = [`[${col.name}] ${declareType(col.type, col)}`]
  82. if (col.nullable === true) {
  83. def.push('null')
  84. } else if (col.nullable === false) {
  85. def.push('not null')
  86. }
  87. if (col.primary === true && pkey.length === 1) {
  88. def.push('primary key')
  89. }
  90. return def.join(' ')
  91. })
  92. const constraint = pkey.length > 1 ? `, constraint PK_${this.temporary ? this.name.substr(1) : this.name} primary key (${pkey.join(', ')})` : ''
  93. return `create table ${this.path} (${cols.join(', ')}${constraint})`
  94. }
  95. Table.fromRecordset = function fromRecordset (recordset, name) {
  96. const t = new this(name)
  97. for (const colName in recordset.columns) {
  98. if (objectHasProperty(recordset.columns, colName)) {
  99. const col = recordset.columns[colName]
  100. t.columns.add(colName, {
  101. type: col.type,
  102. length: col.length,
  103. scale: col.scale,
  104. precision: col.precision
  105. }, {
  106. nullable: col.nullable
  107. })
  108. }
  109. }
  110. if (t.columns.length === 1 && t.columns[0].name === JSON_COLUMN_ID) {
  111. for (let i = 0; i < recordset.length; i++) {
  112. t.rows.add(JSON.stringify(recordset[i]))
  113. }
  114. } else {
  115. for (let i = 0; i < recordset.length; i++) {
  116. t.rows.add.apply(t.rows, t.columns.map(col => recordset[i][col.name]))
  117. }
  118. }
  119. return t
  120. }
  121. Table.parseName = function parseName (name) {
  122. const length = name.length
  123. let cursor = -1
  124. let buffer = ''
  125. let escaped = false
  126. const path = []
  127. while (++cursor < length) {
  128. const char = name.charAt(cursor)
  129. if (char === '[') {
  130. if (escaped) {
  131. buffer += char
  132. } else {
  133. escaped = true
  134. }
  135. } else if (char === ']') {
  136. if (escaped) {
  137. escaped = false
  138. } else {
  139. throw new Error('Invalid table name.')
  140. }
  141. } else if (char === '.') {
  142. if (escaped) {
  143. buffer += char
  144. } else {
  145. path.push(buffer)
  146. buffer = ''
  147. }
  148. } else {
  149. buffer += char
  150. }
  151. }
  152. if (buffer) {
  153. path.push(buffer)
  154. }
  155. switch (path.length) {
  156. case 1:
  157. return {
  158. name: path[0],
  159. schema: null,
  160. database: null
  161. }
  162. case 2:
  163. return {
  164. name: path[1],
  165. schema: path[0],
  166. database: null
  167. }
  168. case 3:
  169. return {
  170. name: path[2],
  171. schema: path[1],
  172. database: path[0]
  173. }
  174. default:
  175. throw new Error('Invalid table name.')
  176. }
  177. }
  178. module.exports = Table