insert.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. /*
  2. * Copyright (c) 2015-present, Vitaly Tomilov
  3. *
  4. * See the LICENSE file at the top-level directory of this distribution
  5. * for licensing information.
  6. *
  7. * Removal or modification of this copyright notice is prohibited.
  8. */
  9. const {TableName} = require('../table-name');
  10. const {ColumnSet} = require('../column-set');
  11. const npm = {
  12. formatting: require('../../formatting'),
  13. utils: require('../../utils')
  14. };
  15. /**
  16. * @method helpers.insert
  17. * @description
  18. * Generates an `INSERT` query for either one object or an array of objects.
  19. *
  20. * @param {object|object[]} data
  21. * An insert object with properties for insert values, or an array of such objects.
  22. *
  23. * When `data` is not a non-null object and not an array, it will throw {@link external:TypeError TypeError} = `Invalid parameter 'data' specified.`
  24. *
  25. * When `data` is an empty array, it will throw {@link external:TypeError TypeError} = `Cannot generate an INSERT from an empty array.`
  26. *
  27. * When `data` is an array that contains a non-object value, the method will throw {@link external:Error Error} =
  28. * `Invalid insert object at index N.`
  29. *
  30. * @param {array|helpers.Column|helpers.ColumnSet} [columns]
  31. * Set of columns to be inserted.
  32. *
  33. * It is optional when `data` is a single object, and required when `data` is an array of objects. If not specified for an array
  34. * of objects, the method will throw {@link external:TypeError TypeError} = `Parameter 'columns' is required when inserting multiple records.`
  35. *
  36. * When `columns` is not a {@link helpers.ColumnSet ColumnSet} object, a temporary {@link helpers.ColumnSet ColumnSet}
  37. * is created - from the value of `columns` (if it was specified), or from the value of `data` (if it is not an array).
  38. *
  39. * When the final {@link helpers.ColumnSet ColumnSet} is empty (no columns in it), the method will throw
  40. * {@link external:Error Error} = `Cannot generate an INSERT without any columns.`
  41. *
  42. * @param {helpers.TableName|Table|string} [table]
  43. * Destination table.
  44. *
  45. * It is normally a required parameter. But when `columns` is passed in as a {@link helpers.ColumnSet ColumnSet} object
  46. * with `table` set in it, that will be used when this parameter isn't specified. When neither is available, the method
  47. * will throw {@link external:Error Error} = `Table name is unknown.`
  48. *
  49. * @returns {string}
  50. * An `INSERT` query string.
  51. *
  52. * @see
  53. * {@link helpers.Column Column},
  54. * {@link helpers.ColumnSet ColumnSet},
  55. * {@link helpers.TableName TableName}
  56. *
  57. * @example
  58. *
  59. * const pgp = require('pg-promise')({
  60. * capSQL: true // if you want all generated SQL capitalized
  61. * });
  62. * const {insert} = pgp.helpers;
  63. *
  64. * const dataSingle = {val: 123, msg: 'hello'};
  65. * const dataMulti = [{val: 123, msg: 'hello'}, {val: 456, msg: 'world!'}];
  66. *
  67. * // Column details can be taken from the data object:
  68. *
  69. * insert(dataSingle, null, 'my-table');
  70. * //=> INSERT INTO "my-table"("val","msg") VALUES(123,'hello')
  71. *
  72. * @example
  73. *
  74. * // Column details are required for a multi-row `INSERT`:
  75. * const {insert} = pgp.helpers;
  76. *
  77. * insert(dataMulti, ['val', 'msg'], 'my-table');
  78. * //=> INSERT INTO "my-table"("val","msg") VALUES(123,'hello'),(456,'world!')
  79. *
  80. * @example
  81. *
  82. * // Column details from a reusable ColumnSet (recommended for performance):
  83. * const {ColumnSet, insert} = pgp.helpers;
  84. *
  85. * const cs = new ColumnSet(['val', 'msg'], {table: 'my-table'});
  86. *
  87. * insert(dataMulti, cs);
  88. * //=> INSERT INTO "my-table"("val","msg") VALUES(123,'hello'),(456,'world!')
  89. *
  90. */
  91. function insert(data, columns, table, capSQL) {
  92. if (!data || typeof data !== 'object') {
  93. throw new TypeError('Invalid parameter \'data\' specified.');
  94. }
  95. const isArray = Array.isArray(data);
  96. if (isArray && !data.length) {
  97. throw new TypeError('Cannot generate an INSERT from an empty array.');
  98. }
  99. if (columns instanceof ColumnSet) {
  100. if (npm.utils.isNull(table)) {
  101. table = columns.table;
  102. }
  103. } else {
  104. if (isArray && npm.utils.isNull(columns)) {
  105. throw new TypeError('Parameter \'columns\' is required when inserting multiple records.');
  106. }
  107. columns = new ColumnSet(columns || data);
  108. }
  109. if (!columns.columns.length) {
  110. throw new Error('Cannot generate an INSERT without any columns.');
  111. }
  112. if (!table) {
  113. throw new Error('Table name is unknown.');
  114. }
  115. if (!(table instanceof TableName)) {
  116. table = new TableName(table);
  117. }
  118. let query = capSQL ? sql.capCase : sql.lowCase;
  119. const fmOptions = {capSQL};
  120. const format = npm.formatting.as.format;
  121. query = format(query, [table.name, columns.names], fmOptions);
  122. if (isArray) {
  123. return query + data.map((d, index) => {
  124. if (!d || typeof d !== 'object') {
  125. throw new Error(`Invalid insert object at index ${index}.`);
  126. }
  127. return '(' + format(columns.variables, columns.prepare(d), fmOptions) + ')';
  128. }).join();
  129. }
  130. return query + '(' + format(columns.variables, columns.prepare(data), fmOptions) + ')';
  131. }
  132. const sql = {
  133. lowCase: 'insert into $1^($2^) values',
  134. capCase: 'INSERT INTO $1^($2^) VALUES'
  135. };
  136. module.exports = {insert};