session.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. import { once } from "node:events";
  2. import { NoopCache } from "../cache/core/index.js";
  3. import { Column } from "../column.js";
  4. import { entityKind, is } from "../entity.js";
  5. import { NoopLogger } from "../logger.js";
  6. import {
  7. MySqlPreparedQuery,
  8. MySqlSession,
  9. MySqlTransaction
  10. } from "../mysql-core/session.js";
  11. import { fillPlaceholders, sql } from "../sql/sql.js";
  12. import { mapResultRow } from "../utils.js";
  13. class MySql2PreparedQuery extends MySqlPreparedQuery {
  14. constructor(client, queryString, params, logger, cache, queryMetadata, cacheConfig, fields, customResultMapper, generatedIds, returningIds) {
  15. super(cache, queryMetadata, cacheConfig);
  16. this.client = client;
  17. this.params = params;
  18. this.logger = logger;
  19. this.fields = fields;
  20. this.customResultMapper = customResultMapper;
  21. this.generatedIds = generatedIds;
  22. this.returningIds = returningIds;
  23. this.rawQuery = {
  24. sql: queryString,
  25. // rowsAsArray: true,
  26. typeCast: function(field, next) {
  27. if (field.type === "TIMESTAMP" || field.type === "DATETIME" || field.type === "DATE") {
  28. return field.string();
  29. }
  30. return next();
  31. }
  32. };
  33. this.query = {
  34. sql: queryString,
  35. rowsAsArray: true,
  36. typeCast: function(field, next) {
  37. if (field.type === "TIMESTAMP" || field.type === "DATETIME" || field.type === "DATE") {
  38. return field.string();
  39. }
  40. return next();
  41. }
  42. };
  43. }
  44. static [entityKind] = "MySql2PreparedQuery";
  45. rawQuery;
  46. query;
  47. async execute(placeholderValues = {}) {
  48. const params = fillPlaceholders(this.params, placeholderValues);
  49. this.logger.logQuery(this.rawQuery.sql, params);
  50. const { fields, client, rawQuery, query, joinsNotNullableMap, customResultMapper, returningIds, generatedIds } = this;
  51. if (!fields && !customResultMapper) {
  52. const res = await this.queryWithCache(rawQuery.sql, params, async () => {
  53. return await client.query(rawQuery, params);
  54. });
  55. const insertId = res[0].insertId;
  56. const affectedRows = res[0].affectedRows;
  57. if (returningIds) {
  58. const returningResponse = [];
  59. let j = 0;
  60. for (let i = insertId; i < insertId + affectedRows; i++) {
  61. for (const column of returningIds) {
  62. const key = returningIds[0].path[0];
  63. if (is(column.field, Column)) {
  64. if (column.field.primary && column.field.autoIncrement) {
  65. returningResponse.push({ [key]: i });
  66. }
  67. if (column.field.defaultFn && generatedIds) {
  68. returningResponse.push({ [key]: generatedIds[j][key] });
  69. }
  70. }
  71. }
  72. j++;
  73. }
  74. return returningResponse;
  75. }
  76. return res;
  77. }
  78. const result = await this.queryWithCache(query.sql, params, async () => {
  79. return await client.query(query, params);
  80. });
  81. const rows = result[0];
  82. if (customResultMapper) {
  83. return customResultMapper(rows);
  84. }
  85. return rows.map((row) => mapResultRow(fields, row, joinsNotNullableMap));
  86. }
  87. async *iterator(placeholderValues = {}) {
  88. const params = fillPlaceholders(this.params, placeholderValues);
  89. const conn = (isPool(this.client) ? await this.client.getConnection() : this.client).connection;
  90. const { fields, query, rawQuery, joinsNotNullableMap, client, customResultMapper } = this;
  91. const hasRowsMapper = Boolean(fields || customResultMapper);
  92. const driverQuery = hasRowsMapper ? conn.query(query, params) : conn.query(rawQuery, params);
  93. const stream = driverQuery.stream();
  94. function dataListener() {
  95. stream.pause();
  96. }
  97. stream.on("data", dataListener);
  98. try {
  99. const onEnd = once(stream, "end");
  100. const onError = once(stream, "error");
  101. while (true) {
  102. stream.resume();
  103. const row = await Promise.race([onEnd, onError, new Promise((resolve) => stream.once("data", resolve))]);
  104. if (row === void 0 || Array.isArray(row) && row.length === 0) {
  105. break;
  106. } else if (row instanceof Error) {
  107. throw row;
  108. } else {
  109. if (hasRowsMapper) {
  110. if (customResultMapper) {
  111. const mappedRow = customResultMapper([row]);
  112. yield Array.isArray(mappedRow) ? mappedRow[0] : mappedRow;
  113. } else {
  114. yield mapResultRow(fields, row, joinsNotNullableMap);
  115. }
  116. } else {
  117. yield row;
  118. }
  119. }
  120. }
  121. } finally {
  122. stream.off("data", dataListener);
  123. if (isPool(client)) {
  124. conn.end();
  125. }
  126. }
  127. }
  128. }
  129. class MySql2Session extends MySqlSession {
  130. constructor(client, dialect, schema, options) {
  131. super(dialect);
  132. this.client = client;
  133. this.schema = schema;
  134. this.options = options;
  135. this.logger = options.logger ?? new NoopLogger();
  136. this.cache = options.cache ?? new NoopCache();
  137. this.mode = options.mode;
  138. }
  139. static [entityKind] = "MySql2Session";
  140. logger;
  141. mode;
  142. cache;
  143. prepareQuery(query, fields, customResultMapper, generatedIds, returningIds, queryMetadata, cacheConfig) {
  144. return new MySql2PreparedQuery(
  145. this.client,
  146. query.sql,
  147. query.params,
  148. this.logger,
  149. this.cache,
  150. queryMetadata,
  151. cacheConfig,
  152. fields,
  153. customResultMapper,
  154. generatedIds,
  155. returningIds
  156. );
  157. }
  158. /**
  159. * @internal
  160. * What is its purpose?
  161. */
  162. async query(query, params) {
  163. this.logger.logQuery(query, params);
  164. const result = await this.client.query({
  165. sql: query,
  166. values: params,
  167. rowsAsArray: true,
  168. typeCast: function(field, next) {
  169. if (field.type === "TIMESTAMP" || field.type === "DATETIME" || field.type === "DATE") {
  170. return field.string();
  171. }
  172. return next();
  173. }
  174. });
  175. return result;
  176. }
  177. all(query) {
  178. const querySql = this.dialect.sqlToQuery(query);
  179. this.logger.logQuery(querySql.sql, querySql.params);
  180. return this.client.execute(querySql.sql, querySql.params).then((result) => result[0]);
  181. }
  182. async transaction(transaction, config) {
  183. const session = isPool(this.client) ? new MySql2Session(
  184. await this.client.getConnection(),
  185. this.dialect,
  186. this.schema,
  187. this.options
  188. ) : this;
  189. const tx = new MySql2Transaction(
  190. this.dialect,
  191. session,
  192. this.schema,
  193. 0,
  194. this.mode
  195. );
  196. if (config) {
  197. const setTransactionConfigSql = this.getSetTransactionSQL(config);
  198. if (setTransactionConfigSql) {
  199. await tx.execute(setTransactionConfigSql);
  200. }
  201. const startTransactionSql = this.getStartTransactionSQL(config);
  202. await (startTransactionSql ? tx.execute(startTransactionSql) : tx.execute(sql`begin`));
  203. } else {
  204. await tx.execute(sql`begin`);
  205. }
  206. try {
  207. const result = await transaction(tx);
  208. await tx.execute(sql`commit`);
  209. return result;
  210. } catch (err) {
  211. await tx.execute(sql`rollback`);
  212. throw err;
  213. } finally {
  214. if (isPool(this.client)) {
  215. session.client.release();
  216. }
  217. }
  218. }
  219. }
  220. class MySql2Transaction extends MySqlTransaction {
  221. static [entityKind] = "MySql2Transaction";
  222. async transaction(transaction) {
  223. const savepointName = `sp${this.nestedIndex + 1}`;
  224. const tx = new MySql2Transaction(
  225. this.dialect,
  226. this.session,
  227. this.schema,
  228. this.nestedIndex + 1,
  229. this.mode
  230. );
  231. await tx.execute(sql.raw(`savepoint ${savepointName}`));
  232. try {
  233. const result = await transaction(tx);
  234. await tx.execute(sql.raw(`release savepoint ${savepointName}`));
  235. return result;
  236. } catch (err) {
  237. await tx.execute(sql.raw(`rollback to savepoint ${savepointName}`));
  238. throw err;
  239. }
  240. }
  241. }
  242. function isPool(client) {
  243. return "getConnection" in client;
  244. }
  245. export {
  246. MySql2PreparedQuery,
  247. MySql2Session,
  248. MySql2Transaction
  249. };
  250. //# sourceMappingURL=session.js.map