UNPKG

d1-sql-tag

Version:

A template literal for working with Cloudflare D1 database

147 lines (146 loc) 4.68 kB
// src/sql-tag.ts var batchId = 0; var rowTypeSymbol = Symbol("rowType"); function createD1SqlTag(db, options) { const sqlTag = (strings, ...values) => { const fragment = { build() { return buildPreparedStatement(db, options, strings, values); }, all() { return buildPreparedStatement(db, options, strings, values).all(); }, run() { return buildPreparedStatement(db, options, strings, values).run(); }, templateStrings: strings, templateValues: values }; return fragment; }; sqlTag.batch = async (statements) => { const queries = statements.map((it) => it.query); const id = makeBatchId(); options?.beforeQuery?.(id, queries); const start = Date.now(); const result = await db.batch(statements.map((it) => makeNativeStatement(db, it))); const duration = Date.now() - start; options?.afterQuery?.(id, queries, result, duration); for (let i = 0; i < result.length; i++) { const statement = statements[i]; const statementResult = result[i]; if ("mapper" in statement) { statementResult.results = statementResult.results.map(statement.mapper); } } return result; }; return sqlTag; } function buildPreparedStatement(db, options, templateStrings, templateValues) { const { query, values } = expandTemplate(templateStrings, templateValues); const statement = { all() { return executeAll(db, options, statement, null); }, run() { return executeRun(db, options, statement); }, map(mapper) { const mappedStatement = { all() { return executeAll(db, options, mappedStatement, mapper); }, run() { return executeRun(db, options, mappedStatement); }, query, values, mapper, [rowTypeSymbol]: null }; return mappedStatement; }, query, values, [rowTypeSymbol]: null }; return statement; } function expandTemplate(rootTemplateStrings, rootTemplateValues) { let query = ""; const values = []; function expand(templateStrings, templateValues) { for (let i = 0; i < templateStrings.length; i++) { if (i > 0) { const value = templateValues[i - 1]; const valueIsFragment = value && typeof value === "object" && "templateStrings" in value && "templateValues" in value; if (valueIsFragment) { expand(value.templateStrings, value.templateValues); } else { let valueIndex = values.indexOf(value); if (valueIndex === -1) { valueIndex = values.push(value) - 1; } query += `?${valueIndex + 1}`; } } query += templateStrings[i]; } } expand(rootTemplateStrings, rootTemplateValues); return { query, values }; } async function executeAll(db, options, statement, mapper) { const batchId2 = makeBatchId(); options?.beforeQuery?.(batchId2, [statement.query]); const start = Date.now(); const result = await makeNativeStatement(db, statement).all(); const duration = Date.now() - start; options?.afterQuery?.(batchId2, [statement.query], [result], duration); if (mapper) { result.results = result.results.map(mapper); } return result; } async function executeRun(db, options, statement) { const batchId2 = makeBatchId(); options?.beforeQuery?.(batchId2, [statement.query]); const start = Date.now(); const result = await makeNativeStatement(db, statement).run(); const duration = Date.now() - start; options?.afterQuery?.(batchId2, [statement.query], [result], duration); return result; } function makeNativeStatement(db, statement) { let stmt = db.prepare(statement.query); if (statement.values.length > 0) { stmt = stmt.bind(...statement.values); } return stmt; } function makeBatchId() { batchId += 1; return batchId; } // src/logger.ts function logQueryResults(queries, results, duration) { console.log( `D1 batch: ${typeof duration === "number" ? `${duration}ms \xB7 ` : ""}${queries.length} queries` ); for (let i = 0; i < queries.length; i++) { const query = queries[i]; const result = results[i]; console.log(`${i + 1}: ${cleanupSqlQuery(query)}`); const logSuffix = "rows_read" in result.meta ? ` \xB7 ${result.meta.rows_read} read \xB7 ${result.meta.rows_written} written` : ""; console.log(` \u21B3 ${result.meta.duration}ms \xB7 ${result.meta.changes} changed` + logSuffix); } } function cleanupSqlQuery(query) { return query.replace(/\n/g, " ").replace(/\s+/g, " "); } export { createD1SqlTag, logQueryResults }; //# sourceMappingURL=index.js.map