@evs-chris/ts-pg-dao
Advanced tools
Comparing version 0.8.7 to 0.8.8
{ | ||
"name": "@evs-chris/ts-pg-dao", | ||
"version": "0.8.7", | ||
"version": "0.8.8", | ||
"main": "runtime/main.js", | ||
@@ -5,0 +5,0 @@ "typings": "runtime/main.d.ts", |
@@ -12,4 +12,12 @@ import * as pg from 'pg'; | ||
begin(): Promise<void>; | ||
/** Roll back the current transaction for this connection. */ | ||
rollback(): Promise<void>; | ||
/** Set a savepoint that can be rolled back to any number of times without | ||
* fully aborting the transaction. | ||
*/ | ||
savepoint(): Promise<SavePoint>; | ||
/** Roll back the current transaction for this connection. If a savepoint | ||
* is given and can be rolled back to, the surrounding transaction will | ||
* not be aborted. If the savepoint fails or is not supplied, the whole | ||
* transaction will be aborted. | ||
* */ | ||
rollback(savepoint?: SavePoint): Promise<void>; | ||
/** Complete the current transaction for this connection. */ | ||
@@ -52,2 +60,9 @@ commit(): Promise<void>; | ||
/** | ||
* A savepoint genereated from a transaction. | ||
*/ | ||
export interface SavePoint { | ||
point: string; | ||
} | ||
export { QueryResult } from 'pg'; | ||
/** | ||
* Enhances the given pg.Client with a few convenience methods for managing | ||
@@ -57,3 +72,3 @@ * transactions and a tagged template helper for executing a query with | ||
*/ | ||
export declare function enhance(client: pg.Client): Connection; | ||
export declare function enhance(client: pg.Client | pg.ClientConfig): Connection; | ||
export declare class SQL { | ||
@@ -60,0 +75,0 @@ sql: string; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const pg = require("pg"); | ||
/** | ||
@@ -11,2 +12,4 @@ * Enhances the given pg.Client with a few convenience methods for managing | ||
return client; | ||
if (!('connect' in client)) | ||
client = new pg.Client(client); | ||
const res = client; | ||
@@ -16,2 +19,3 @@ res['ts-pg-dao'] = true; | ||
res.begin = begin; | ||
res.savepoint = savepoint; | ||
res.rollback = rollback; | ||
@@ -32,6 +36,21 @@ res.commit = commit; | ||
this.inTransaction = true; | ||
this.__savepoint = 0; | ||
} | ||
async function rollback() { | ||
async function savepoint() { | ||
if (!this.inTransaction) | ||
throw new Error(`Can't set a savepoint when not in transaction`); | ||
const point = `step${this.__savepoint++}`; | ||
await this.query(`savepoint ${point}`); | ||
return { point }; | ||
} | ||
async function rollback(point) { | ||
if (!this.inTransaction) | ||
throw new Error(`Can't rollback when not in transaction`); | ||
if (point) { | ||
try { | ||
await this.query(`rollback to ${point.point}`); | ||
return; | ||
} | ||
catch (_a) { } | ||
} | ||
await this.query('rollback'); | ||
@@ -45,4 +64,4 @@ const t = this; | ||
} | ||
catch (_a) { } | ||
t.__cruns = []; | ||
catch (_b) { } | ||
t.__rruns = []; | ||
} | ||
@@ -49,0 +68,0 @@ // discard commit callbacks |
@@ -286,3 +286,3 @@ "use strict"; | ||
finally { | ||
await client.release(); | ||
client.release(); | ||
} | ||
@@ -289,0 +289,0 @@ } |
@@ -101,7 +101,7 @@ "use strict"; | ||
const server = path.join(serverPath, (model.file || model.name) + '.ts'); | ||
console.log(`\twriting server ${model.name} to ${server}...`); | ||
console.log(` writing server ${model.name} to ${server}...`); | ||
await fs.writeFile(server, serverModel(config, model)); | ||
if (!pathy) { | ||
const client = path.join(clientPath, (model.file || model.name) + '.ts'); | ||
console.log(`\twriting client ${model.name} to ${client}...`); | ||
console.log(` writing client ${model.name} to ${client}...`); | ||
await fs.writeFile(client, clientModel(config, model)); | ||
@@ -113,3 +113,3 @@ } | ||
const cfg = config.pgconfig; | ||
console.log(`\t - generating schema cache`); | ||
console.log(` - generating schema cache`); | ||
const allCols = (await client.query(main_1.columnQuery)).rows; | ||
@@ -131,3 +131,3 @@ try { | ||
} | ||
console.log(`\t - writing schema cache ${cfg.schemaCacheFile}`); | ||
console.log(` - writing schema cache ${cfg.schemaCacheFile}`); | ||
await fs.writeFile(cfg.schemaCacheFile, JSON.stringify(cache, null, ' '), { encoding: 'utf8' }); | ||
@@ -151,3 +151,3 @@ } | ||
const server = path.join(serverPath, typeof config.index === 'string' ? config.index : 'index.ts'); | ||
console.log(`\twriting server index to ${server}...`); | ||
console.log(` writing server index to ${server}...`); | ||
await fs.writeFile(server, tpl); | ||
@@ -166,3 +166,3 @@ if (!pathy) { | ||
const client = path.join(clientPath, typeof config.index === 'string' ? config.index : 'index.ts'); | ||
console.log(`\twriting client index to ${client}...`); | ||
console.log(` writing client index to ${client}...`); | ||
await fs.writeFile(client, tpl); | ||
@@ -211,6 +211,7 @@ } | ||
let res: dao.QueryResult; | ||
const transact = !con.inTransaction; | ||
if (transact) await con.begin(); | ||
try { | ||
const res = await con.query(sql, params); | ||
res = await con.query(sql, params); | ||
if (res.rowCount < 1) throw new Error('No matching row to update for ${model.name}'); | ||
@@ -224,3 +225,3 @@ if (res.rowCount > 1) throw new Error('Too many matching rows updated for ${model.name}'); | ||
} | ||
${model.fields.find(f => f.optlock) ? `${model.fields.filter(f => f.optlock).map(f => `\n model.${f.alias || f.name} = lock;`).join('')}` : ''} | ||
${model.fields.find(f => f.optlock) ? `${model.fields.filter(f => f.optlock).map(f => `\n model.${f.alias || f.name} = res.rows[0].${f.alias || f.name};`).join('')}` : ''} | ||
} else {${insertMembers(config, model, ' ')}${loadFlag ? ` | ||
@@ -404,5 +405,2 @@ model.${loadFlag} = false;` : ''}${changeFlag ? ` | ||
let res = `\n${prefix}const params = [];\n${prefix}const sets = [];\n${prefix}let sql = 'UPDATE ${model.table} SET ';`; | ||
const lock = model.fields.find(f => f.optlock); | ||
if (lock) | ||
res += `\n${prefix}const lock = new Date()${lock.pgtype === 'date' ? `.toISOString().substr(0, 10)` : ''};`; | ||
model.fields.forEach(f => { | ||
@@ -415,3 +413,3 @@ if (!f.pkey && !f.optlock) { | ||
if (locks.length) { | ||
res += `\n${locks.map(l => `${prefix}params.push(lock);\n${prefix}sets.push('"${l.name}" = $' + params.length);`).join('\n')}`; | ||
res += `\n${locks.map(l => `${prefix}sets.push('"${l.name}" = now()');`).join('\n')}`; | ||
} | ||
@@ -422,3 +420,3 @@ res += `\n\n${prefix}sql += sets.join(', ');\n`; | ||
res += `\n${prefix}params.push(${where.map(f => `model.${f.alias || f.name}`).join(', ')});`; | ||
res += `\n${prefix}sql += \` WHERE ${where.map((f, i) => `${f.optlock ? `date_trunc('millisecond', "${f.name}"${model.cast(config, f)})` : `"${f.name}"`} = $\${count + ${i + 1}}`).join(' AND ')}\`;`; | ||
res += `\n${prefix}sql += \` WHERE ${where.map((f, i) => `${f.optlock ? `date_trunc('millisecond', "${f.name}"${model.cast(config, f)})` : `"${f.name}"`} = $\${count + ${i + 1}}`).join(' AND ')}${locks.length ? ` RETURNING ${locks.map(l => `"${l.name}"${model.cast(config, l)}`).join(', ')}` : ''}\`;`; | ||
return res; | ||
@@ -464,3 +462,3 @@ } | ||
} | ||
const tableAliases = /@"?([a-zA-Z_]+[a-zA-Z0-9_]*)"?(?!\.)\s(?:(?!\s*(?:left|right|cross|inner|outer|on|where)\s)\s*(?:[aA][sS]\s)?\s*"?([a-zA-Z_]+[a-zA-Z0-9_]*)?"?)?/gi; | ||
const tableAliases = /@"?([a-zA-Z_]+[a-zA-Z0-9_]*)"?(?!\.)\b(?:(?!\s+(?:left|right|cross|inner|outer|on|where|join|union)\b)\s*(?:[aA][sS]\s)?\s*"?([a-zA-Z_]+[a-zA-Z0-9_]*)?"?)?/gi; | ||
const fieldAliases = /@:?"?([a-zA-Z_]+[a-zA-Z0-9_]*)"?\."?([a-zA-Z_]+[a-zA-Z0-9_]+|\*)"?/gi; | ||
@@ -511,2 +509,3 @@ const params = /\$([a-zA-Z_]+[a-zA-Z0-9_]*)/g; | ||
let sql = mapParams(query.sql, parms)[0]; | ||
const referencedTables = {}; | ||
// map tables to models and record relevant aliases | ||
@@ -517,2 +516,5 @@ sql = sql.replace(tableAliases, (_m, tbl, alias) => { | ||
throw new Error(`Could not find model for table ${tbl} referenced in query ${query.name}.`); | ||
if (!referencedTables[tbl]) | ||
referencedTables[tbl] = 0; | ||
referencedTables[tbl]++; | ||
const entry = { model: mdl, prefix: `${alias || tbl}__`, alias: alias || tbl, root: !alias && mdl === query.owner }; | ||
@@ -524,6 +526,24 @@ if (entry.root) | ||
}); | ||
const tableCount = Object.keys(referencedTables).length; | ||
if (!tableCount && (query.owner.cols.find(c => c.cast) || (config.tzTimestamps && query.owner.cols.find(c => c.pgtype === 'timestamp')))) { | ||
console.warn(` >>>> query ${query.owner.name}.${query.name} may have columns that need to be cast\n >> using something like 'select @${model.table[0]}.* from @${model.table} as ${model.table[0]};' will automtaically cast columns`); | ||
} | ||
if (!root) { | ||
query.root = root = { model: query.owner, prefix: '', alias: query.owner.table, root: true }; | ||
aliases[root.alias] = root; | ||
const owners = {}; | ||
for (const k in aliases) { | ||
if (aliases[k].model === query.owner) | ||
owners[k] = aliases[k]; | ||
} | ||
const keys = Object.keys(owners); | ||
if (keys.length === 1) { | ||
owners[keys[0]].root = true; | ||
query.root = root = owners[keys[0]]; | ||
} | ||
else { | ||
query.root = root = { model: query.owner, prefix: '', alias: query.owner.table, root: true }; | ||
aliases[root.alias] = root; | ||
} | ||
} | ||
if (root && tableCount === 1) | ||
root.prefix = ''; | ||
// compute includes and select lists if available | ||
@@ -534,2 +554,3 @@ if (query.include && root) { | ||
buildTypes(query, root); | ||
const referencedCols = {}; | ||
// map and expand column aliases as necessary | ||
@@ -545,4 +566,7 @@ function mapFields(sql) { | ||
} | ||
if (!referencedCols[mdl.table]) | ||
referencedCols[mdl.table] = []; | ||
referencedCols[mdl.table].push(col); | ||
if (col === '*') { | ||
return (entry.cols || entry.model.cols).map(c => `${alias}.${c.name}${mdl.cast(config, c)} AS ${entry.prefix}${c.name}`).join(', '); | ||
return (entry.cols || entry.model.cols).map(c => tableCount === 1 ? `${c.name}${mdl.cast(config, c)}` : `${alias}.${c.name}${mdl.cast(config, c)} AS ${entry.prefix}${c.name}`).join(', '); | ||
} | ||
@@ -563,2 +587,10 @@ else { | ||
sql = mapFields(sql); | ||
for (const k in aliases) { | ||
const alias = aliases[k]; | ||
const table = alias.model.table; | ||
if (!referencedTables[table]) | ||
continue; | ||
if (!referencedCols[table]) | ||
console.warn(` >>>> table ${table} in ${query.owner.name}.${query.name} may have columns that need to be cast\n >> referencing the columns as '@${alias.alias}.columnname' or '@${alias.alias}.*' will automatically cast columns`); | ||
} | ||
const defaulted = (query.params || []).reduce((a, c) => a && (c.optional || !!c.default), true); | ||
@@ -565,0 +597,0 @@ query.parts && Object.entries(query.parts).forEach(([condition, sql]) => { |
107443
2264