sql.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. import { entityKind, is } from "../entity.js";
  2. import { isPgEnum } from "../pg-core/columns/enum.js";
  3. import { Subquery } from "../subquery.js";
  4. import { tracer } from "../tracing.js";
  5. import { ViewBaseConfig } from "../view-common.js";
  6. import { Column } from "../column.js";
  7. import { IsAlias, Table } from "../table.js";
  8. class FakePrimitiveParam {
  9. static [entityKind] = "FakePrimitiveParam";
  10. }
  11. function isSQLWrapper(value) {
  12. return value !== null && value !== void 0 && typeof value.getSQL === "function";
  13. }
  14. function mergeQueries(queries) {
  15. const result = { sql: "", params: [] };
  16. for (const query of queries) {
  17. result.sql += query.sql;
  18. result.params.push(...query.params);
  19. if (query.typings?.length) {
  20. if (!result.typings) {
  21. result.typings = [];
  22. }
  23. result.typings.push(...query.typings);
  24. }
  25. }
  26. return result;
  27. }
  28. class StringChunk {
  29. static [entityKind] = "StringChunk";
  30. value;
  31. constructor(value) {
  32. this.value = Array.isArray(value) ? value : [value];
  33. }
  34. getSQL() {
  35. return new SQL([this]);
  36. }
  37. }
  38. class SQL {
  39. constructor(queryChunks) {
  40. this.queryChunks = queryChunks;
  41. for (const chunk of queryChunks) {
  42. if (is(chunk, Table)) {
  43. const schemaName = chunk[Table.Symbol.Schema];
  44. this.usedTables.push(
  45. schemaName === void 0 ? chunk[Table.Symbol.Name] : schemaName + "." + chunk[Table.Symbol.Name]
  46. );
  47. }
  48. }
  49. }
  50. static [entityKind] = "SQL";
  51. /** @internal */
  52. decoder = noopDecoder;
  53. shouldInlineParams = false;
  54. /** @internal */
  55. usedTables = [];
  56. append(query) {
  57. this.queryChunks.push(...query.queryChunks);
  58. return this;
  59. }
  60. toQuery(config) {
  61. return tracer.startActiveSpan("drizzle.buildSQL", (span) => {
  62. const query = this.buildQueryFromSourceParams(this.queryChunks, config);
  63. span?.setAttributes({
  64. "drizzle.query.text": query.sql,
  65. "drizzle.query.params": JSON.stringify(query.params)
  66. });
  67. return query;
  68. });
  69. }
  70. buildQueryFromSourceParams(chunks, _config) {
  71. const config = Object.assign({}, _config, {
  72. inlineParams: _config.inlineParams || this.shouldInlineParams,
  73. paramStartIndex: _config.paramStartIndex || { value: 0 }
  74. });
  75. const {
  76. casing,
  77. escapeName,
  78. escapeParam,
  79. prepareTyping,
  80. inlineParams,
  81. paramStartIndex
  82. } = config;
  83. return mergeQueries(chunks.map((chunk) => {
  84. if (is(chunk, StringChunk)) {
  85. return { sql: chunk.value.join(""), params: [] };
  86. }
  87. if (is(chunk, Name)) {
  88. return { sql: escapeName(chunk.value), params: [] };
  89. }
  90. if (chunk === void 0) {
  91. return { sql: "", params: [] };
  92. }
  93. if (Array.isArray(chunk)) {
  94. const result = [new StringChunk("(")];
  95. for (const [i, p] of chunk.entries()) {
  96. result.push(p);
  97. if (i < chunk.length - 1) {
  98. result.push(new StringChunk(", "));
  99. }
  100. }
  101. result.push(new StringChunk(")"));
  102. return this.buildQueryFromSourceParams(result, config);
  103. }
  104. if (is(chunk, SQL)) {
  105. return this.buildQueryFromSourceParams(chunk.queryChunks, {
  106. ...config,
  107. inlineParams: inlineParams || chunk.shouldInlineParams
  108. });
  109. }
  110. if (is(chunk, Table)) {
  111. const schemaName = chunk[Table.Symbol.Schema];
  112. const tableName = chunk[Table.Symbol.Name];
  113. return {
  114. sql: schemaName === void 0 || chunk[IsAlias] ? escapeName(tableName) : escapeName(schemaName) + "." + escapeName(tableName),
  115. params: []
  116. };
  117. }
  118. if (is(chunk, Column)) {
  119. const columnName = casing.getColumnCasing(chunk);
  120. if (_config.invokeSource === "indexes") {
  121. return { sql: escapeName(columnName), params: [] };
  122. }
  123. const schemaName = chunk.table[Table.Symbol.Schema];
  124. return {
  125. sql: chunk.table[IsAlias] || schemaName === void 0 ? escapeName(chunk.table[Table.Symbol.Name]) + "." + escapeName(columnName) : escapeName(schemaName) + "." + escapeName(chunk.table[Table.Symbol.Name]) + "." + escapeName(columnName),
  126. params: []
  127. };
  128. }
  129. if (is(chunk, View)) {
  130. const schemaName = chunk[ViewBaseConfig].schema;
  131. const viewName = chunk[ViewBaseConfig].name;
  132. return {
  133. sql: schemaName === void 0 || chunk[ViewBaseConfig].isAlias ? escapeName(viewName) : escapeName(schemaName) + "." + escapeName(viewName),
  134. params: []
  135. };
  136. }
  137. if (is(chunk, Param)) {
  138. if (is(chunk.value, Placeholder)) {
  139. return { sql: escapeParam(paramStartIndex.value++, chunk), params: [chunk], typings: ["none"] };
  140. }
  141. const mappedValue = chunk.value === null ? null : chunk.encoder.mapToDriverValue(chunk.value);
  142. if (is(mappedValue, SQL)) {
  143. return this.buildQueryFromSourceParams([mappedValue], config);
  144. }
  145. if (inlineParams) {
  146. return { sql: this.mapInlineParam(mappedValue, config), params: [] };
  147. }
  148. let typings = ["none"];
  149. if (prepareTyping) {
  150. typings = [prepareTyping(chunk.encoder)];
  151. }
  152. return { sql: escapeParam(paramStartIndex.value++, mappedValue), params: [mappedValue], typings };
  153. }
  154. if (is(chunk, Placeholder)) {
  155. return { sql: escapeParam(paramStartIndex.value++, chunk), params: [chunk], typings: ["none"] };
  156. }
  157. if (is(chunk, SQL.Aliased) && chunk.fieldAlias !== void 0) {
  158. return { sql: escapeName(chunk.fieldAlias), params: [] };
  159. }
  160. if (is(chunk, Subquery)) {
  161. if (chunk._.isWith) {
  162. return { sql: escapeName(chunk._.alias), params: [] };
  163. }
  164. return this.buildQueryFromSourceParams([
  165. new StringChunk("("),
  166. chunk._.sql,
  167. new StringChunk(") "),
  168. new Name(chunk._.alias)
  169. ], config);
  170. }
  171. if (isPgEnum(chunk)) {
  172. if (chunk.schema) {
  173. return { sql: escapeName(chunk.schema) + "." + escapeName(chunk.enumName), params: [] };
  174. }
  175. return { sql: escapeName(chunk.enumName), params: [] };
  176. }
  177. if (isSQLWrapper(chunk)) {
  178. if (chunk.shouldOmitSQLParens?.()) {
  179. return this.buildQueryFromSourceParams([chunk.getSQL()], config);
  180. }
  181. return this.buildQueryFromSourceParams([
  182. new StringChunk("("),
  183. chunk.getSQL(),
  184. new StringChunk(")")
  185. ], config);
  186. }
  187. if (inlineParams) {
  188. return { sql: this.mapInlineParam(chunk, config), params: [] };
  189. }
  190. return { sql: escapeParam(paramStartIndex.value++, chunk), params: [chunk], typings: ["none"] };
  191. }));
  192. }
  193. mapInlineParam(chunk, { escapeString }) {
  194. if (chunk === null) {
  195. return "null";
  196. }
  197. if (typeof chunk === "number" || typeof chunk === "boolean") {
  198. return chunk.toString();
  199. }
  200. if (typeof chunk === "string") {
  201. return escapeString(chunk);
  202. }
  203. if (typeof chunk === "object") {
  204. const mappedValueAsString = chunk.toString();
  205. if (mappedValueAsString === "[object Object]") {
  206. return escapeString(JSON.stringify(chunk));
  207. }
  208. return escapeString(mappedValueAsString);
  209. }
  210. throw new Error("Unexpected param value: " + chunk);
  211. }
  212. getSQL() {
  213. return this;
  214. }
  215. as(alias) {
  216. if (alias === void 0) {
  217. return this;
  218. }
  219. return new SQL.Aliased(this, alias);
  220. }
  221. mapWith(decoder) {
  222. this.decoder = typeof decoder === "function" ? { mapFromDriverValue: decoder } : decoder;
  223. return this;
  224. }
  225. inlineParams() {
  226. this.shouldInlineParams = true;
  227. return this;
  228. }
  229. /**
  230. * This method is used to conditionally include a part of the query.
  231. *
  232. * @param condition - Condition to check
  233. * @returns itself if the condition is `true`, otherwise `undefined`
  234. */
  235. if(condition) {
  236. return condition ? this : void 0;
  237. }
  238. }
  239. class Name {
  240. constructor(value) {
  241. this.value = value;
  242. }
  243. static [entityKind] = "Name";
  244. brand;
  245. getSQL() {
  246. return new SQL([this]);
  247. }
  248. }
  249. function name(value) {
  250. return new Name(value);
  251. }
  252. function isDriverValueEncoder(value) {
  253. return typeof value === "object" && value !== null && "mapToDriverValue" in value && typeof value.mapToDriverValue === "function";
  254. }
  255. const noopDecoder = {
  256. mapFromDriverValue: (value) => value
  257. };
  258. const noopEncoder = {
  259. mapToDriverValue: (value) => value
  260. };
  261. const noopMapper = {
  262. ...noopDecoder,
  263. ...noopEncoder
  264. };
  265. class Param {
  266. /**
  267. * @param value - Parameter value
  268. * @param encoder - Encoder to convert the value to a driver parameter
  269. */
  270. constructor(value, encoder = noopEncoder) {
  271. this.value = value;
  272. this.encoder = encoder;
  273. }
  274. static [entityKind] = "Param";
  275. brand;
  276. getSQL() {
  277. return new SQL([this]);
  278. }
  279. }
  280. function param(value, encoder) {
  281. return new Param(value, encoder);
  282. }
  283. function sql(strings, ...params) {
  284. const queryChunks = [];
  285. if (params.length > 0 || strings.length > 0 && strings[0] !== "") {
  286. queryChunks.push(new StringChunk(strings[0]));
  287. }
  288. for (const [paramIndex, param2] of params.entries()) {
  289. queryChunks.push(param2, new StringChunk(strings[paramIndex + 1]));
  290. }
  291. return new SQL(queryChunks);
  292. }
  293. ((sql2) => {
  294. function empty() {
  295. return new SQL([]);
  296. }
  297. sql2.empty = empty;
  298. function fromList(list) {
  299. return new SQL(list);
  300. }
  301. sql2.fromList = fromList;
  302. function raw(str) {
  303. return new SQL([new StringChunk(str)]);
  304. }
  305. sql2.raw = raw;
  306. function join(chunks, separator) {
  307. const result = [];
  308. for (const [i, chunk] of chunks.entries()) {
  309. if (i > 0 && separator !== void 0) {
  310. result.push(separator);
  311. }
  312. result.push(chunk);
  313. }
  314. return new SQL(result);
  315. }
  316. sql2.join = join;
  317. function identifier(value) {
  318. return new Name(value);
  319. }
  320. sql2.identifier = identifier;
  321. function placeholder2(name2) {
  322. return new Placeholder(name2);
  323. }
  324. sql2.placeholder = placeholder2;
  325. function param2(value, encoder) {
  326. return new Param(value, encoder);
  327. }
  328. sql2.param = param2;
  329. })(sql || (sql = {}));
  330. ((SQL2) => {
  331. class Aliased {
  332. constructor(sql2, fieldAlias) {
  333. this.sql = sql2;
  334. this.fieldAlias = fieldAlias;
  335. }
  336. static [entityKind] = "SQL.Aliased";
  337. /** @internal */
  338. isSelectionField = false;
  339. getSQL() {
  340. return this.sql;
  341. }
  342. /** @internal */
  343. clone() {
  344. return new Aliased(this.sql, this.fieldAlias);
  345. }
  346. }
  347. SQL2.Aliased = Aliased;
  348. })(SQL || (SQL = {}));
  349. class Placeholder {
  350. constructor(name2) {
  351. this.name = name2;
  352. }
  353. static [entityKind] = "Placeholder";
  354. getSQL() {
  355. return new SQL([this]);
  356. }
  357. }
  358. function placeholder(name2) {
  359. return new Placeholder(name2);
  360. }
  361. function fillPlaceholders(params, values) {
  362. return params.map((p) => {
  363. if (is(p, Placeholder)) {
  364. if (!(p.name in values)) {
  365. throw new Error(`No value for placeholder "${p.name}" was provided`);
  366. }
  367. return values[p.name];
  368. }
  369. if (is(p, Param) && is(p.value, Placeholder)) {
  370. if (!(p.value.name in values)) {
  371. throw new Error(`No value for placeholder "${p.value.name}" was provided`);
  372. }
  373. return p.encoder.mapToDriverValue(values[p.value.name]);
  374. }
  375. return p;
  376. });
  377. }
  378. const IsDrizzleView = Symbol.for("drizzle:IsDrizzleView");
  379. class View {
  380. static [entityKind] = "View";
  381. /** @internal */
  382. [ViewBaseConfig];
  383. /** @internal */
  384. [IsDrizzleView] = true;
  385. constructor({ name: name2, schema, selectedFields, query }) {
  386. this[ViewBaseConfig] = {
  387. name: name2,
  388. originalName: name2,
  389. schema,
  390. selectedFields,
  391. query,
  392. isExisting: !query,
  393. isAlias: false
  394. };
  395. }
  396. getSQL() {
  397. return new SQL([this]);
  398. }
  399. }
  400. function isView(view) {
  401. return typeof view === "object" && view !== null && IsDrizzleView in view;
  402. }
  403. function getViewName(view) {
  404. return view[ViewBaseConfig].name;
  405. }
  406. Column.prototype.getSQL = function() {
  407. return new SQL([this]);
  408. };
  409. Table.prototype.getSQL = function() {
  410. return new SQL([this]);
  411. };
  412. Subquery.prototype.getSQL = function() {
  413. return new SQL([this]);
  414. };
  415. export {
  416. FakePrimitiveParam,
  417. Name,
  418. Param,
  419. Placeholder,
  420. SQL,
  421. StringChunk,
  422. View,
  423. fillPlaceholders,
  424. getViewName,
  425. isDriverValueEncoder,
  426. isSQLWrapper,
  427. isView,
  428. name,
  429. noopDecoder,
  430. noopEncoder,
  431. noopMapper,
  432. param,
  433. placeholder,
  434. sql
  435. };
  436. //# sourceMappingURL=sql.js.map