@proteinjs/db-driver-knex 1.13.9 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +10 -0
- package/dist/generated/index.js +1 -1
- package/dist/generated/index.js.map +1 -1
- package/dist/generated/test/index.js +1 -1
- package/dist/generated/test/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/src/KnexDriver.d.ts +66 -0
- package/dist/src/KnexDriver.d.ts.map +1 -1
- package/dist/src/KnexDriver.js +181 -1
- package/dist/src/KnexDriver.js.map +1 -1
- package/dist/src/KnexLogValues.d.ts +38 -0
- package/dist/src/KnexLogValues.d.ts.map +1 -0
- package/dist/src/KnexLogValues.js +47 -0
- package/dist/src/KnexLogValues.js.map +1 -0
- package/dist/test/LogValuesSwitch.test.d.ts +2 -0
- package/dist/test/LogValuesSwitch.test.d.ts.map +1 -0
- package/dist/test/LogValuesSwitch.test.js +255 -0
- package/dist/test/LogValuesSwitch.test.js.map +1 -0
- package/dist/test/StatementLogNeverThrows.test.d.ts +2 -0
- package/dist/test/StatementLogNeverThrows.test.d.ts.map +1 -0
- package/dist/test/StatementLogNeverThrows.test.js +541 -0
- package/dist/test/StatementLogNeverThrows.test.js.map +1 -0
- package/dist/test/StatementParamLogging.test.d.ts +2 -0
- package/dist/test/StatementParamLogging.test.d.ts.map +1 -0
- package/dist/test/StatementParamLogging.test.js +403 -0
- package/dist/test/StatementParamLogging.test.js.map +1 -0
- package/dist/test/util/printedLine.d.ts +20 -0
- package/dist/test/util/printedLine.d.ts.map +1 -0
- package/dist/test/util/printedLine.js +48 -0
- package/dist/test/util/printedLine.js.map +1 -0
- package/generated/index.ts +1 -1
- package/generated/test/index.ts +1 -1
- package/index.ts +1 -0
- package/package.json +4 -4
- package/src/KnexDriver.ts +191 -2
- package/src/KnexLogValues.ts +42 -0
- package/test/LogValuesSwitch.test.ts +201 -0
- package/test/StatementLogNeverThrows.test.ts +466 -0
- package/test/StatementParamLogging.test.ts +313 -0
- package/test/util/printedLine.ts +43 -0
package/generated/test/index.ts
CHANGED
|
@@ -8,7 +8,7 @@ import '@proteinjs/logger';
|
|
|
8
8
|
/** Generate Source Graph */
|
|
9
9
|
|
|
10
10
|
const sourceGraph =
|
|
11
|
-
'{"options":{"directed":true,"multigraph":false,"compound":false},"nodes":[{"v":"@proteinjs/db-driver-knex/TestDbDriverFactory","value":{"packageName":"@proteinjs/db-driver-knex","name":"TestDbDriverFactory","filePath":"test/config/TestDbDriverFactory.ts","qualifiedName":"@proteinjs/db-driver-knex/TestDbDriverFactory","isAbstract":false,"isStatic":false,"visibility":"public","properties":[],"methods":[{"name":"getDbDriver","returnType":{"packageName":"@proteinjs/db","name":"DbDriver","filePath":null,"qualifiedName":"@proteinjs/db/DbDriver","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[]}],"typeParameters":[],"directParentInterfaces":[{"packageName":"@proteinjs/db","name":"DefaultDbDriverFactory","filePath":null,"qualifiedName":"@proteinjs/db/DefaultDbDriverFactory","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"directParentClasses":[],"sourceType":2}},{"v":"@proteinjs/db/DefaultDbDriverFactory"},{"v":"@proteinjs/db-driver-knex/KnexDriver","value":{"packageName":"@proteinjs/db-driver-knex","name":"KnexDriver","filePath":"src/KnexDriver.ts","qualifiedName":"@proteinjs/db-driver-knex/KnexDriver","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"KNEX","type":{"packageName":"","name":"knex","filePath":null,"qualifiedName":"/knex","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":true,"visibility":"private"},{"name":"logger","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private"},{"name":"config","type":{"packageName":"@proteinjs/db-driver-knex","name":"KnexConfig","filePath":null,"qualifiedName":"@proteinjs/db-driver-knex/KnexConfig","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private"},{"name":"knexConfig","type":{"packageName":"","name":"any","filePath":null,"qualifiedName":"/any","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private"},{"name":"getTable","type":{"packageName":"","name":"((name: string) => Table<any>) | undefined","filePath":null,"qualifiedName":"/((name: string) => Table<any>) | undefined","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[{"name":"getKnex","returnType":{"packageName":"","name":"knex","filePath":null,"qualifiedName":"/knex","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[]},{"name":"getDbName","returnType":null,"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[]},{"name":"createDbIfNotExists","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[]},{"name":"createDb","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"name","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}},{"name":"options","type":{"packageName":"@proteinjs/db-driver-knex","name":"{ ddl?: string[] }","filePath":null,"qualifiedName":"@proteinjs/db-driver-knex/{ ddl?: string[] }","typeParameters":null,"directParents":null}}]},{"name":"dropDb","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"name","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"dbExists","returnType":{"packageName":"","name":"Promise<boolean>","filePath":null,"qualifiedName":"/Promise<boolean>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"databaseName","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"start","returnType":null,"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[]},{"name":"stop","returnType":null,"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[]},{"name":"setMaxAllowedPacketSize","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[]},{"name":"getTableManager","returnType":{"packageName":"@proteinjs/db","name":"TableManager","filePath":null,"qualifiedName":"@proteinjs/db/TableManager","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[]},{"name":"handleCaseSensitivity","returnType":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"tableName","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}},{"name":"columnName","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}},{"name":"caseSensitive","type":{"packageName":"","name":"boolean","filePath":null,"qualifiedName":"/boolean","typeParameters":null,"directParents":null}}]},{"name":"runQuery","returnType":{"packageName":"","name":"Promise<SerializedRecord[]>","filePath":null,"qualifiedName":"/Promise<SerializedRecord[]>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"generateStatement","type":{"packageName":"","name":"(config: DbDriverQueryStatementConfig) => Statement","filePath":null,"qualifiedName":"/(config: DbDriverQueryStatementConfig) => Statement","typeParameters":null,"directParents":null}},{"name":"transaction","type":{"packageName":"knex","name":"Transaction","filePath":null,"qualifiedName":"knex/Transaction","typeParameters":null,"directParents":null}}]},{"name":"runDml","returnType":{"packageName":"","name":"Promise<number>","filePath":null,"qualifiedName":"/Promise<number>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"generateStatement","type":{"packageName":"","name":"(config: DbDriverDmlStatementConfig) => Statement","filePath":null,"qualifiedName":"/(config: DbDriverDmlStatementConfig) => Statement","typeParameters":null,"directParents":null}},{"name":"transaction","type":{"packageName":"knex","name":"Transaction","filePath":null,"qualifiedName":"knex/Transaction","typeParameters":null,"directParents":null}}]},{"name":"runTransaction","returnType":{"packageName":"","name":"Promise<T>","filePath":null,"qualifiedName":"/Promise<T>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"fn","type":{"packageName":"","name":"(transaction: Transaction) => Promise<T>","filePath":null,"qualifiedName":"/(transaction: Transaction) => Promise<T>","typeParameters":null,"directParents":null}}]}],"typeParameters":[],"directParentInterfaces":[{"packageName":"@proteinjs/db","name":"DbDriver","filePath":null,"qualifiedName":"@proteinjs/db/DbDriver","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"directParentClasses":[],"sourceType":2}},{"v":"@proteinjs/db/DbDriver"},{"v":"@proteinjs/db-driver-knex/KnexSchemaOperations","value":{"packageName":"@proteinjs/db-driver-knex","name":"KnexSchemaOperations","filePath":"src/KnexSchemaOperations.ts","qualifiedName":"@proteinjs/db-driver-knex/KnexSchemaOperations","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"logger","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private"},{"name":"knexDriver","type":{"packageName":"@proteinjs/db-driver-knex","name":"KnexDriver","filePath":null,"qualifiedName":"@proteinjs/db-driver-knex/KnexDriver","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private"}],"methods":[{"name":"createTables","returnType":null,"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"tables","type":{"packageName":"","name":"Table<any>[]","filePath":null,"qualifiedName":"/Table<any>[]","typeParameters":null,"directParents":null}}]},{"name":"createTable","returnType":null,"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[{"name":"table","type":{"packageName":"","name":"Table<any>","filePath":null,"qualifiedName":"/Table<any>","typeParameters":null,"directParents":null}}]},{"name":"alterTable","returnType":null,"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"table","type":{"packageName":"","name":"Table<any>","filePath":null,"qualifiedName":"/Table<any>","typeParameters":null,"directParents":null}},{"name":"tableChanges","type":{"packageName":"@proteinjs/db","name":"TableChanges","filePath":null,"qualifiedName":"@proteinjs/db/TableChanges","typeParameters":null,"directParents":null}}]},{"name":"createColumn","returnType":null,"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[{"name":"column","type":{"packageName":"","name":"Column<any, any>","filePath":null,"qualifiedName":"/Column<any, any>","typeParameters":null,"directParents":null}},{"name":"table","type":{"packageName":"","name":"Table<any>","filePath":null,"qualifiedName":"/Table<any>","typeParameters":null,"directParents":null}},{"name":"tableBuilder","type":{"packageName":"","name":"knex.TableBuilder","filePath":null,"qualifiedName":"/knex.TableBuilder","typeParameters":null,"directParents":null}},{"name":"tableChanges","type":{"packageName":"@proteinjs/db","name":"TableChanges","filePath":null,"qualifiedName":"@proteinjs/db/TableChanges","typeParameters":null,"directParents":null}}]}],"typeParameters":[],"directParentInterfaces":[{"packageName":"@proteinjs/db","name":"SchemaOperations","filePath":null,"qualifiedName":"@proteinjs/db/SchemaOperations","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"directParentClasses":[],"sourceType":2}},{"v":"@proteinjs/db/SchemaOperations"}],"edges":[{"v":"@proteinjs/db-driver-knex/TestDbDriverFactory","w":"@proteinjs/db/DefaultDbDriverFactory","value":"implements interface"},{"v":"@proteinjs/db-driver-knex/KnexDriver","w":"@proteinjs/db/DbDriver","value":"implements interface"},{"v":"@proteinjs/db-driver-knex/KnexSchemaOperations","w":"@proteinjs/db/SchemaOperations","value":"implements interface"}]}';
|
|
11
|
+
'{"options":{"directed":true,"multigraph":false,"compound":false},"nodes":[{"v":"@proteinjs/db-driver-knex/TestDbDriverFactory","value":{"packageName":"@proteinjs/db-driver-knex","name":"TestDbDriverFactory","filePath":"test/config/TestDbDriverFactory.ts","qualifiedName":"@proteinjs/db-driver-knex/TestDbDriverFactory","isAbstract":false,"isStatic":false,"visibility":"public","properties":[],"methods":[{"name":"getDbDriver","returnType":{"packageName":"@proteinjs/db","name":"DbDriver","filePath":null,"qualifiedName":"@proteinjs/db/DbDriver","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[]}],"typeParameters":[],"directParentInterfaces":[{"packageName":"@proteinjs/db","name":"DefaultDbDriverFactory","filePath":null,"qualifiedName":"@proteinjs/db/DefaultDbDriverFactory","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"directParentClasses":[],"sourceType":2}},{"v":"@proteinjs/db/DefaultDbDriverFactory"},{"v":"@proteinjs/db-driver-knex/KnexDriver","value":{"packageName":"@proteinjs/db-driver-knex","name":"KnexDriver","filePath":"src/KnexDriver.ts","qualifiedName":"@proteinjs/db-driver-knex/KnexDriver","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"KNEX","type":{"packageName":"","name":"knex","filePath":null,"qualifiedName":"/knex","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":true,"visibility":"private"},{"name":"logger","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private"},{"name":"config","type":{"packageName":"@proteinjs/db-driver-knex","name":"KnexConfig","filePath":null,"qualifiedName":"@proteinjs/db-driver-knex/KnexConfig","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private"},{"name":"knexConfig","type":{"packageName":"","name":"any","filePath":null,"qualifiedName":"/any","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private"},{"name":"getTable","type":{"packageName":"","name":"((name: string) => Table<any>) | undefined","filePath":null,"qualifiedName":"/((name: string) => Table<any>) | undefined","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[{"name":"getKnex","returnType":{"packageName":"","name":"knex","filePath":null,"qualifiedName":"/knex","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[]},{"name":"getDbName","returnType":null,"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[]},{"name":"createDbIfNotExists","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[]},{"name":"createDb","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"name","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}},{"name":"options","type":{"packageName":"@proteinjs/db-driver-knex","name":"{ ddl?: string[] }","filePath":null,"qualifiedName":"@proteinjs/db-driver-knex/{ ddl?: string[] }","typeParameters":null,"directParents":null}}]},{"name":"dropDb","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"name","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"dbExists","returnType":{"packageName":"","name":"Promise<boolean>","filePath":null,"qualifiedName":"/Promise<boolean>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"databaseName","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"start","returnType":null,"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[]},{"name":"stop","returnType":null,"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[]},{"name":"setMaxAllowedPacketSize","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[]},{"name":"getTableManager","returnType":{"packageName":"@proteinjs/db","name":"TableManager","filePath":null,"qualifiedName":"@proteinjs/db/TableManager","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[]},{"name":"handleCaseSensitivity","returnType":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"tableName","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}},{"name":"columnName","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}},{"name":"caseSensitive","type":{"packageName":"","name":"boolean","filePath":null,"qualifiedName":"/boolean","typeParameters":null,"directParents":null}}]},{"name":"runQuery","returnType":{"packageName":"","name":"Promise<SerializedRecord[]>","filePath":null,"qualifiedName":"/Promise<SerializedRecord[]>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"generateStatement","type":{"packageName":"","name":"(config: DbDriverQueryStatementConfig) => Statement","filePath":null,"qualifiedName":"/(config: DbDriverQueryStatementConfig) => Statement","typeParameters":null,"directParents":null}},{"name":"transaction","type":{"packageName":"knex","name":"Transaction","filePath":null,"qualifiedName":"knex/Transaction","typeParameters":null,"directParents":null}}]},{"name":"runDml","returnType":{"packageName":"","name":"Promise<number>","filePath":null,"qualifiedName":"/Promise<number>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"generateStatement","type":{"packageName":"","name":"(config: DbDriverDmlStatementConfig) => Statement","filePath":null,"qualifiedName":"/(config: DbDriverDmlStatementConfig) => Statement","typeParameters":null,"directParents":null}},{"name":"transaction","type":{"packageName":"knex","name":"Transaction","filePath":null,"qualifiedName":"knex/Transaction","typeParameters":null,"directParents":null}}]},{"name":"runTransaction","returnType":{"packageName":"","name":"Promise<T>","filePath":null,"qualifiedName":"/Promise<T>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"fn","type":{"packageName":"","name":"(transaction: Transaction) => Promise<T>","filePath":null,"qualifiedName":"/(transaction: Transaction) => Promise<T>","typeParameters":null,"directParents":null}}]},{"name":"logFailure","returnType":{"packageName":"","name":"void","filePath":null,"qualifiedName":"/void","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[{"name":"sql","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}},{"name":"params","type":{"packageName":"","name":"unknown","filePath":null,"qualifiedName":"/unknown","typeParameters":null,"directParents":null}},{"name":"error","type":{"packageName":"","name":"unknown","filePath":null,"qualifiedName":"/unknown","typeParameters":null,"directParents":null}}]},{"name":"writeStatementLine","returnType":{"packageName":"","name":"void","filePath":null,"qualifiedName":"/void","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[{"name":"write","type":{"packageName":"","name":"() => void","filePath":null,"qualifiedName":"/() => void","typeParameters":null,"directParents":null}}]},{"name":"causeSummary","returnType":{"packageName":"@proteinjs/db-driver-knex","name":"FailureCauseSummary","filePath":null,"qualifiedName":"@proteinjs/db-driver-knex/FailureCauseSummary","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[{"name":"error","type":{"packageName":"","name":"unknown","filePath":null,"qualifiedName":"/unknown","typeParameters":null,"directParents":null}}]},{"name":"describeParams","returnType":{"packageName":"","name":"ParamsDescription | undefined","filePath":null,"qualifiedName":"/ParamsDescription | undefined","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[{"name":"params","type":{"packageName":"","name":"unknown","filePath":null,"qualifiedName":"/unknown","typeParameters":null,"directParents":null}}]},{"name":"describeParam","returnType":{"packageName":"@proteinjs/db-driver-knex","name":"ParamDescription","filePath":null,"qualifiedName":"@proteinjs/db-driver-knex/ParamDescription","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[{"name":"read","type":{"packageName":"","name":"() => unknown","filePath":null,"qualifiedName":"/() => unknown","typeParameters":null,"directParents":null}}]},{"name":"paramKind","returnType":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[{"name":"value","type":{"packageName":"","name":"unknown","filePath":null,"qualifiedName":"/unknown","typeParameters":null,"directParents":null}}]},{"name":"isDictionary","returnType":{"packageName":"","name":"params is { [name: string]: unknown }","filePath":null,"qualifiedName":"/params is { [name: string]: unknown }","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[{"name":"params","type":{"packageName":"","name":"unknown","filePath":null,"qualifiedName":"/unknown","typeParameters":null,"directParents":null}}]},{"name":"vendorFact","returnType":{"packageName":"","name":"unknown","filePath":null,"qualifiedName":"/unknown","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[{"name":"error","type":{"packageName":"","name":"unknown","filePath":null,"qualifiedName":"/unknown","typeParameters":null,"directParents":null}},{"name":"fact","type":{"packageName":"","name":"keyof FailureCauseSummary","filePath":null,"qualifiedName":"/keyof FailureCauseSummary","typeParameters":null,"directParents":null}}]}],"typeParameters":[],"directParentInterfaces":[{"packageName":"@proteinjs/db","name":"DbDriver","filePath":null,"qualifiedName":"@proteinjs/db/DbDriver","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"directParentClasses":[],"sourceType":2}},{"v":"@proteinjs/db/DbDriver"},{"v":"@proteinjs/db-driver-knex/KnexSchemaOperations","value":{"packageName":"@proteinjs/db-driver-knex","name":"KnexSchemaOperations","filePath":"src/KnexSchemaOperations.ts","qualifiedName":"@proteinjs/db-driver-knex/KnexSchemaOperations","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"logger","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private"},{"name":"knexDriver","type":{"packageName":"@proteinjs/db-driver-knex","name":"KnexDriver","filePath":null,"qualifiedName":"@proteinjs/db-driver-knex/KnexDriver","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private"}],"methods":[{"name":"createTables","returnType":null,"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"tables","type":{"packageName":"","name":"Table<any>[]","filePath":null,"qualifiedName":"/Table<any>[]","typeParameters":null,"directParents":null}}]},{"name":"createTable","returnType":null,"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[{"name":"table","type":{"packageName":"","name":"Table<any>","filePath":null,"qualifiedName":"/Table<any>","typeParameters":null,"directParents":null}}]},{"name":"alterTable","returnType":null,"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"table","type":{"packageName":"","name":"Table<any>","filePath":null,"qualifiedName":"/Table<any>","typeParameters":null,"directParents":null}},{"name":"tableChanges","type":{"packageName":"@proteinjs/db","name":"TableChanges","filePath":null,"qualifiedName":"@proteinjs/db/TableChanges","typeParameters":null,"directParents":null}}]},{"name":"createColumn","returnType":null,"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[{"name":"column","type":{"packageName":"","name":"Column<any, any>","filePath":null,"qualifiedName":"/Column<any, any>","typeParameters":null,"directParents":null}},{"name":"table","type":{"packageName":"","name":"Table<any>","filePath":null,"qualifiedName":"/Table<any>","typeParameters":null,"directParents":null}},{"name":"tableBuilder","type":{"packageName":"","name":"knex.TableBuilder","filePath":null,"qualifiedName":"/knex.TableBuilder","typeParameters":null,"directParents":null}},{"name":"tableChanges","type":{"packageName":"@proteinjs/db","name":"TableChanges","filePath":null,"qualifiedName":"@proteinjs/db/TableChanges","typeParameters":null,"directParents":null}}]}],"typeParameters":[],"directParentInterfaces":[{"packageName":"@proteinjs/db","name":"SchemaOperations","filePath":null,"qualifiedName":"@proteinjs/db/SchemaOperations","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"directParentClasses":[],"sourceType":2}},{"v":"@proteinjs/db/SchemaOperations"}],"edges":[{"v":"@proteinjs/db-driver-knex/TestDbDriverFactory","w":"@proteinjs/db/DefaultDbDriverFactory","value":"implements interface"},{"v":"@proteinjs/db-driver-knex/KnexDriver","w":"@proteinjs/db/DbDriver","value":"implements interface"},{"v":"@proteinjs/db-driver-knex/KnexSchemaOperations","w":"@proteinjs/db/SchemaOperations","value":"implements interface"}]}';
|
|
12
12
|
|
|
13
13
|
/** Generate Source Links */
|
|
14
14
|
|
package/index.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@proteinjs/db-driver-knex",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.0",
|
|
4
4
|
"main": "./dist/generated/index.js",
|
|
5
5
|
"types": "./dist/generated/index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
"test": "jest --runInBand --passWithNoTests"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@proteinjs/db": "^1.47.
|
|
44
|
+
"@proteinjs/db": "^1.47.2",
|
|
45
45
|
"@proteinjs/db-query": "^1.10.1",
|
|
46
|
-
"@proteinjs/db-transaction-context": "^0.5.
|
|
46
|
+
"@proteinjs/db-transaction-context": "^0.5.34",
|
|
47
47
|
"@proteinjs/logger": "^1.0.21",
|
|
48
48
|
"@proteinjs/reflection": "^1.2.0",
|
|
49
49
|
"@proteinjs/util": "^1.6.1",
|
|
@@ -67,5 +67,5 @@
|
|
|
67
67
|
"ts-jest": "29.1.1",
|
|
68
68
|
"typescript": "5.2.2"
|
|
69
69
|
},
|
|
70
|
-
"gitHead": "
|
|
70
|
+
"gitHead": "5811ffac3d235bce810460223eaa567b9f36a3a6"
|
|
71
71
|
}
|
package/src/KnexDriver.ts
CHANGED
|
@@ -9,11 +9,24 @@ import {
|
|
|
9
9
|
tableByName,
|
|
10
10
|
} from '@proteinjs/db';
|
|
11
11
|
import { KnexConfig } from './KnexConfig';
|
|
12
|
+
import { KnexLogValues } from './KnexLogValues';
|
|
12
13
|
import { Logger } from '@proteinjs/logger';
|
|
13
14
|
import { Statement } from '@proteinjs/db-query';
|
|
14
15
|
import { KnexSchemaOperations } from './KnexSchemaOperations';
|
|
15
16
|
import { KnexColumnTypeFactory } from './KnexColumnTypeFactory';
|
|
16
17
|
|
|
18
|
+
/** A bound parameter as a log line carries it (see KnexDriver.describeParams): never its value. */
|
|
19
|
+
type ParamDescription = { type: string; length?: number; null?: true };
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A statement's bindings as a log line carries them: positional bindings by position, a bindings
|
|
23
|
+
* dictionary by entry name — or the one word for bindings that could not be read at all.
|
|
24
|
+
*/
|
|
25
|
+
type ParamsDescription = ParamDescription[] | { [name: string]: ParamDescription } | 'unreadable';
|
|
26
|
+
|
|
27
|
+
/** A failure as the log line carries it: the error's name, the vendor's codes and the server's own message. */
|
|
28
|
+
type FailureCauseSummary = { name?: string; code?: string; errno?: number; sqlState?: string; sqlMessage?: string };
|
|
29
|
+
|
|
17
30
|
/**
|
|
18
31
|
* Knex driver (configured for MariaDb) for ProteinJs Db
|
|
19
32
|
*/
|
|
@@ -160,8 +173,8 @@ export class KnexDriver implements DbDriver {
|
|
|
160
173
|
try {
|
|
161
174
|
const runner = transaction || this.getKnex();
|
|
162
175
|
return (await runner.raw(sql, params as any))[0]; // returns 2 arrays, first is records, second is metadata per record
|
|
163
|
-
} catch (error:
|
|
164
|
-
this.
|
|
176
|
+
} catch (error: unknown) {
|
|
177
|
+
this.logFailure(sql, params, error);
|
|
165
178
|
throw error;
|
|
166
179
|
}
|
|
167
180
|
}
|
|
@@ -185,4 +198,180 @@ export class KnexDriver implements DbDriver {
|
|
|
185
198
|
return result;
|
|
186
199
|
});
|
|
187
200
|
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* The one line a failed statement writes. What is THROWN is the vendor's error itself, untouched
|
|
204
|
+
* (runQuery rethrows it); this is only what the driver PRINTS of it.
|
|
205
|
+
*
|
|
206
|
+
* The vendor error is not safe to print as it is: the query layer rewrites a failed query's
|
|
207
|
+
* `message` to the SQL with its bindings INTERPOLATED, and the client library's error carries
|
|
208
|
+
* the same formatted text as `sql` — every bound value of the statement, on any line the error
|
|
209
|
+
* reaches. So the line carries the SQL text (placeholders only), the parameters DESCRIBED
|
|
210
|
+
* (describeParams) and a summary of the failure: the error's name, the vendor's codes and the
|
|
211
|
+
* server's own message (`sqlMessage` — which can itself quote a value, e.g. a colliding key; the
|
|
212
|
+
* driver prints it as it always has). The vendor error itself, and the values as bound, ride
|
|
213
|
+
* the line only behind the dev-only switch (KnexLogValues).
|
|
214
|
+
*
|
|
215
|
+
* `params` is whatever the statement carried. `Statement` types it as an array, but the query
|
|
216
|
+
* layer also accepts a bindings dictionary (and wraps anything else as one binding), and
|
|
217
|
+
* runQuery forwards what it was handed — so nothing here may assume a shape. The line is
|
|
218
|
+
* written through writeStatementLine: it sits on the way to a `throw`, and must never be the
|
|
219
|
+
* reason something else is thrown.
|
|
220
|
+
*/
|
|
221
|
+
private logFailure(sql: string, params: unknown, error: unknown): void {
|
|
222
|
+
this.writeStatementLine(() =>
|
|
223
|
+
this.logger.error({
|
|
224
|
+
message: `Failed when executing sql`,
|
|
225
|
+
obj: {
|
|
226
|
+
sql,
|
|
227
|
+
params: this.describeParams(params),
|
|
228
|
+
...KnexLogValues.ofStatement(params),
|
|
229
|
+
cause: this.causeSummary(error),
|
|
230
|
+
},
|
|
231
|
+
...KnexLogValues.ofFailure(error),
|
|
232
|
+
})
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* The ONE door a statement's log line is written through — the owner of the rule that writing
|
|
238
|
+
* a line can NEVER change what the driver throws. A statement line is written on the way to a
|
|
239
|
+
* `throw`, so whatever goes wrong while building or writing it — a log writer that is down, a
|
|
240
|
+
* serializer meeting a value it cannot print (a bigint or a throwing `toJSON`, under the values
|
|
241
|
+
* switch), a helper meeting a shape nobody foresaw — is caught HERE and reported as one FIXED
|
|
242
|
+
* line that carries nothing of the statement; the caller then throws exactly what it was going
|
|
243
|
+
* to throw. If the logger itself is what failed, the fixed line fails too and is dropped:
|
|
244
|
+
* nothing is left to write with.
|
|
245
|
+
*/
|
|
246
|
+
private writeStatementLine(write: () => void): void {
|
|
247
|
+
try {
|
|
248
|
+
write();
|
|
249
|
+
} catch {
|
|
250
|
+
try {
|
|
251
|
+
this.logger.error({ message: `Failed to write a statement log line` });
|
|
252
|
+
} catch {
|
|
253
|
+
// The logger itself is what failed: nothing is left to write with, and nothing here may throw.
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* The name, codes and server message of any thrown value — never the query layer's rewritten
|
|
260
|
+
* `message` or `sql`. Total: a fact that cannot be read (vendorFact) is left out.
|
|
261
|
+
*/
|
|
262
|
+
private causeSummary(error: unknown): FailureCauseSummary {
|
|
263
|
+
const name = this.vendorFact(error, 'name');
|
|
264
|
+
const code = this.vendorFact(error, 'code');
|
|
265
|
+
const errno = this.vendorFact(error, 'errno');
|
|
266
|
+
const sqlState = this.vendorFact(error, 'sqlState');
|
|
267
|
+
const sqlMessage = this.vendorFact(error, 'sqlMessage');
|
|
268
|
+
return {
|
|
269
|
+
...(typeof name === 'string' ? { name } : {}),
|
|
270
|
+
...(typeof code === 'string' ? { code } : {}),
|
|
271
|
+
...(typeof errno === 'number' ? { errno } : {}),
|
|
272
|
+
...(typeof sqlState === 'string' ? { sqlState } : {}),
|
|
273
|
+
...(typeof sqlMessage === 'string' ? { sqlMessage } : {}),
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* A statement's bound parameters as a LOG LINE carries them — the one owner of that form: by
|
|
279
|
+
* position, the KIND of each value and, for strings, arrays and bytes, its LENGTH. Never a
|
|
280
|
+
* value: a parameter is row content (a presented token, a credential hash, an address), and a
|
|
281
|
+
* log line outlives and out-travels the row it came from. The SQL text beside it carries `?`
|
|
282
|
+
* placeholders only, so position + kind + length is what locates a failure (which parameter was
|
|
283
|
+
* null, which was oversized) without quoting it.
|
|
284
|
+
*
|
|
285
|
+
* The shapes are the query layer's own: positional bindings (an array) are described by
|
|
286
|
+
* POSITION; a bindings DICTIONARY (a plain object, bound to `:name` placeholders) by entry
|
|
287
|
+
* NAME; no bindings describe nothing; and anything else — null, a scalar, a Map, a Date — is
|
|
288
|
+
* what the query layer binds as ONE parameter, so it is described as one.
|
|
289
|
+
*
|
|
290
|
+
* TOTAL, for any input: a value that cannot be read is described as `unreadable`
|
|
291
|
+
* (describeParam), and bindings that cannot even be walked are the one word `unreadable`.
|
|
292
|
+
*/
|
|
293
|
+
private describeParams(params?: unknown): ParamsDescription | undefined {
|
|
294
|
+
if (params === undefined) {
|
|
295
|
+
return undefined;
|
|
296
|
+
}
|
|
297
|
+
try {
|
|
298
|
+
if (Array.isArray(params)) {
|
|
299
|
+
const described: ParamDescription[] = [];
|
|
300
|
+
for (let position = 0; position < params.length; position++) {
|
|
301
|
+
described.push(this.describeParam(() => params[position]));
|
|
302
|
+
}
|
|
303
|
+
return described;
|
|
304
|
+
}
|
|
305
|
+
if (this.isDictionary(params)) {
|
|
306
|
+
const described: { [name: string]: ParamDescription } = {};
|
|
307
|
+
for (const name of Object.keys(params)) {
|
|
308
|
+
described[name] = this.describeParam(() => params[name]);
|
|
309
|
+
}
|
|
310
|
+
return described;
|
|
311
|
+
}
|
|
312
|
+
return [this.describeParam(() => params)];
|
|
313
|
+
} catch {
|
|
314
|
+
return 'unreadable';
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* One bound value, described: its kind, a length for strings, arrays and bytes, null named. The
|
|
320
|
+
* value is READ in here (`read`), so a read that throws — an accessor, a revoked proxy — is
|
|
321
|
+
* described as `unreadable` and its neighbours still are described. A length that is not a
|
|
322
|
+
* number is no length: nothing a value says about itself rides the line but that one number.
|
|
323
|
+
*/
|
|
324
|
+
private describeParam(read: () => unknown): ParamDescription {
|
|
325
|
+
try {
|
|
326
|
+
const value = read();
|
|
327
|
+
const description: ParamDescription = { type: this.paramKind(value) };
|
|
328
|
+
if (typeof value === 'string' || Array.isArray(value) || value instanceof Uint8Array) {
|
|
329
|
+
const length: unknown = value.length;
|
|
330
|
+
if (typeof length === 'number') {
|
|
331
|
+
description.length = length;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if (value === null || value === undefined) {
|
|
335
|
+
description.null = true;
|
|
336
|
+
}
|
|
337
|
+
return description;
|
|
338
|
+
} catch {
|
|
339
|
+
return { type: 'unreadable' };
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/** The kind of a bound value — a word, never the value. */
|
|
344
|
+
private paramKind(value: unknown): string {
|
|
345
|
+
if (value === null || value === undefined) {
|
|
346
|
+
return 'null';
|
|
347
|
+
}
|
|
348
|
+
if (Array.isArray(value)) {
|
|
349
|
+
return 'array';
|
|
350
|
+
}
|
|
351
|
+
if (value instanceof Date) {
|
|
352
|
+
return 'date';
|
|
353
|
+
}
|
|
354
|
+
if (value instanceof Uint8Array) {
|
|
355
|
+
return 'bytes';
|
|
356
|
+
}
|
|
357
|
+
return typeof value;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/** Whether bindings are a DICTIONARY — a plain object, which the query layer binds by entry name. */
|
|
361
|
+
private isDictionary(params: unknown): params is { [name: string]: unknown } {
|
|
362
|
+
if (typeof params !== 'object' || params === null) {
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
const prototype = Object.getPrototypeOf(params);
|
|
366
|
+
return prototype === null || Object.getPrototypeOf(prototype) === null;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/** One fact of a thrown value, or nothing when it cannot be read (nothing thrown, a scalar, a throwing accessor). */
|
|
370
|
+
private vendorFact(error: unknown, fact: keyof FailureCauseSummary): unknown {
|
|
371
|
+
try {
|
|
372
|
+
return (error as { [fact: string]: unknown } | null | undefined)?.[fact];
|
|
373
|
+
} catch {
|
|
374
|
+
return undefined;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
188
377
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONE owner of the dev-only switch that lets the driver's failure line carry the REAL values
|
|
3
|
+
* of a statement's bound parameters, and of the fields that carry them.
|
|
4
|
+
*
|
|
5
|
+
* By default the line carries no bound value: the parameters are described
|
|
6
|
+
* (KnexDriver.describeParams — positions, kinds, lengths) and the failure is summarized by the
|
|
7
|
+
* vendor's codes and the server's own message — never by the vendor error itself, whose `message`
|
|
8
|
+
* the query layer rewrites to the SQL with every binding INTERPOLATED (and whose `sql` is the
|
|
9
|
+
* same text). A parameter is row content, and a log line outlives and out-travels the row it came
|
|
10
|
+
* from. A local dev database holds nothing that needs that protection, and there the values are
|
|
11
|
+
* what locates a bug. So the switch: when BOTH `DEVELOPMENT` is set (the dev-server switch, never
|
|
12
|
+
* set in a production image) AND `DB_LOG_PARAM_VALUES=1`, the line adds `paramValues` — the
|
|
13
|
+
* parameters as they were bound — and carries the vendor error itself as `error`. With either
|
|
14
|
+
* unset: never. Both are read at each line, so a process cannot hold a stale answer.
|
|
15
|
+
*
|
|
16
|
+
* The switch adds FIELDS to a log line and changes nothing else: what the driver throws is the
|
|
17
|
+
* same with the switch on or off.
|
|
18
|
+
*/
|
|
19
|
+
export class KnexLogValues {
|
|
20
|
+
/** The dev-server switch: the first gate. */
|
|
21
|
+
static readonly DEVELOPMENT_VAR = 'DEVELOPMENT';
|
|
22
|
+
/** The values switch: the second gate, on only as exactly `1`. */
|
|
23
|
+
static readonly SWITCH_VAR = 'DB_LOG_PARAM_VALUES';
|
|
24
|
+
|
|
25
|
+
/** Whether lines may carry real values right now — both gates, read now. */
|
|
26
|
+
static enabled(): boolean {
|
|
27
|
+
return !!process.env[KnexLogValues.DEVELOPMENT_VAR] && process.env[KnexLogValues.SWITCH_VAR] === '1';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* What a statement's line adds beside its described parameters: `{ paramValues }` — the bindings
|
|
32
|
+
* as the statement carried them, positional or a bindings dictionary — or nothing.
|
|
33
|
+
*/
|
|
34
|
+
static ofStatement(params: unknown): { paramValues?: unknown } {
|
|
35
|
+
return KnexLogValues.enabled() && params !== undefined ? { paramValues: params } : {};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** What a failure's line carries beside its cause summary: the vendor error itself as `{ error }`, or nothing. */
|
|
39
|
+
static ofFailure(error: unknown): { error?: unknown } {
|
|
40
|
+
return KnexLogValues.enabled() ? { error } : {};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import knex from 'knex';
|
|
2
|
+
import { KnexDriver, KnexLogValues } from '@proteinjs/db-driver-knex';
|
|
3
|
+
import { Logger } from '@proteinjs/logger';
|
|
4
|
+
import { CapturedLog, lineOf } from './util/printedLine';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The dev-only values switch (`KnexLogValues`): the driver's failure line carries the REAL values
|
|
8
|
+
* of the bound parameters — `paramValues`, and the vendor error itself as `error` — only when BOTH
|
|
9
|
+
* `DEVELOPMENT` is set AND `DB_LOG_PARAM_VALUES=1`. With either gate closed: never.
|
|
10
|
+
*
|
|
11
|
+
* By default the line describes the parameters and summarizes the failure
|
|
12
|
+
* (StatementParamLogging.test.ts holds that). On a local dev database the values are what locates
|
|
13
|
+
* a bug, so there — and only there — the line adds them, beside the description, which stays. What
|
|
14
|
+
* a caller CATCHES is the same error whatever the switch says: the switch adds fields to a line
|
|
15
|
+
* and changes nothing else.
|
|
16
|
+
*
|
|
17
|
+
* No server is needed: the REAL query layer and dialect run over a stub connection that rejects
|
|
18
|
+
* the way the client library does. The stub server's message quotes no bound value: what the
|
|
19
|
+
* SERVER echoes is not this switch's subject (see StatementParamLogging.test.ts's header).
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
// A fixture value shaped like row content (not a real credential).
|
|
23
|
+
const VALUE = 'rst_5d41402abc4b2a76b9719d911017c592';
|
|
24
|
+
|
|
25
|
+
type DriverInternals = { logger: Logger };
|
|
26
|
+
type DriverStatics = { KNEX?: unknown };
|
|
27
|
+
|
|
28
|
+
/** A connection that rejects every statement like a missing table — a server message that quotes no binding. */
|
|
29
|
+
const rejectingConnection = () => {
|
|
30
|
+
const mysql = require('mysql');
|
|
31
|
+
return {
|
|
32
|
+
state: 'authenticated',
|
|
33
|
+
on: () => undefined,
|
|
34
|
+
removeAllListeners: () => undefined,
|
|
35
|
+
connect: (done: (error?: Error) => void) => done(),
|
|
36
|
+
end: (done: (error?: Error) => void) => done(),
|
|
37
|
+
query: (options: { sql: string }, bindings: any[], done: (error: Error) => void) => {
|
|
38
|
+
const sqlMessage = `Table 'test.credential' doesn't exist`;
|
|
39
|
+
done(
|
|
40
|
+
Object.assign(new Error(`ER_NO_SUCH_TABLE: ${sqlMessage}`), {
|
|
41
|
+
code: 'ER_NO_SUCH_TABLE',
|
|
42
|
+
errno: 1146,
|
|
43
|
+
sqlState: '42S02',
|
|
44
|
+
sqlMessage,
|
|
45
|
+
sql: mysql.format(options.sql, bindings),
|
|
46
|
+
})
|
|
47
|
+
);
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const GATES: [string, { [name: string]: string }, boolean][] = [
|
|
53
|
+
['DEVELOPMENT set AND DB_LOG_PARAM_VALUES=1', { DEVELOPMENT: 'true', DB_LOG_PARAM_VALUES: '1' }, true],
|
|
54
|
+
['DEVELOPMENT unset, DB_LOG_PARAM_VALUES=1', { DB_LOG_PARAM_VALUES: '1' }, false],
|
|
55
|
+
['DEVELOPMENT empty, DB_LOG_PARAM_VALUES=1', { DEVELOPMENT: '', DB_LOG_PARAM_VALUES: '1' }, false],
|
|
56
|
+
['DEVELOPMENT set, DB_LOG_PARAM_VALUES unset', { DEVELOPMENT: 'true' }, false],
|
|
57
|
+
[
|
|
58
|
+
'DEVELOPMENT set, DB_LOG_PARAM_VALUES=true (on only as exactly 1)',
|
|
59
|
+
{ DEVELOPMENT: 'true', DB_LOG_PARAM_VALUES: 'true' },
|
|
60
|
+
false,
|
|
61
|
+
],
|
|
62
|
+
['DEVELOPMENT set, DB_LOG_PARAM_VALUES=0', { DEVELOPMENT: 'true', DB_LOG_PARAM_VALUES: '0' }, false],
|
|
63
|
+
['neither set', {}, false],
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
describe('the dev-only values switch: real values ride the driver`s failure line only behind both gates', () => {
|
|
67
|
+
const driver = new KnexDriver({ host: 'localhost', user: 'root', password: '', dbName: 'test' });
|
|
68
|
+
const internals = driver as unknown as DriverInternals;
|
|
69
|
+
const statics = KnexDriver as unknown as DriverStatics;
|
|
70
|
+
const ENV = [KnexLogValues.DEVELOPMENT_VAR, KnexLogValues.SWITCH_VAR];
|
|
71
|
+
const envBefore: { [name: string]: string | undefined } = {};
|
|
72
|
+
let captured: CapturedLog[] = [];
|
|
73
|
+
let instance: ReturnType<typeof knex>;
|
|
74
|
+
let previousInstance: unknown;
|
|
75
|
+
|
|
76
|
+
beforeAll(() => {
|
|
77
|
+
instance = knex({ client: 'mysql', connection: { host: 'localhost', user: 'root', password: '' } });
|
|
78
|
+
(instance as any).client.driver = { createConnection: rejectingConnection };
|
|
79
|
+
previousInstance = statics.KNEX;
|
|
80
|
+
statics.KNEX = instance;
|
|
81
|
+
internals.logger = new Logger({
|
|
82
|
+
name: 'KnexDriver',
|
|
83
|
+
logLevel: 'debug',
|
|
84
|
+
logWriter: { write: (log: CapturedLog) => captured.push(log) } as any,
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
afterAll(async () => {
|
|
89
|
+
statics.KNEX = previousInstance;
|
|
90
|
+
await instance.destroy();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
beforeEach(() => {
|
|
94
|
+
captured = [];
|
|
95
|
+
for (const name of ENV) {
|
|
96
|
+
envBefore[name] = process.env[name];
|
|
97
|
+
delete process.env[name];
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
afterEach(() => {
|
|
102
|
+
for (const name of ENV) {
|
|
103
|
+
if (envBefore[name] === undefined) {
|
|
104
|
+
delete process.env[name];
|
|
105
|
+
} else {
|
|
106
|
+
process.env[name] = envBefore[name];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const params = [VALUE, 42];
|
|
112
|
+
const failingInsert = (): Promise<any> =>
|
|
113
|
+
driver
|
|
114
|
+
.runDml(() => ({ sql: 'INSERT INTO `credential` (`id`, `n`) VALUES (?, ?)', params }))
|
|
115
|
+
.then(
|
|
116
|
+
() => 'resolved',
|
|
117
|
+
(error: unknown) => error
|
|
118
|
+
);
|
|
119
|
+
/** Everything a caller can read off a caught error. */
|
|
120
|
+
const facts = (error: any) => ({
|
|
121
|
+
class: error.constructor.name,
|
|
122
|
+
message: error.message,
|
|
123
|
+
own: Object.getOwnPropertyNames(error)
|
|
124
|
+
.sort()
|
|
125
|
+
.map((name) => [name, name === 'stack' ? '(stack)' : error[name]]),
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test.each(GATES)('%s', async (_gate, env, on) => {
|
|
129
|
+
// The error a caller catches with the switch untouched — what every gate must reproduce.
|
|
130
|
+
const baseline = facts(await failingInsert());
|
|
131
|
+
captured = [];
|
|
132
|
+
Object.assign(process.env, env);
|
|
133
|
+
|
|
134
|
+
const caught = await failingInsert();
|
|
135
|
+
|
|
136
|
+
expect(KnexLogValues.enabled()).toBe(on);
|
|
137
|
+
const failures = captured.filter((log) => log.message === 'Failed when executing sql');
|
|
138
|
+
expect(failures).toHaveLength(1);
|
|
139
|
+
const [failure] = failures;
|
|
140
|
+
// What a caller catches is the same whatever the switch says.
|
|
141
|
+
expect(facts(caught)).toEqual(baseline);
|
|
142
|
+
// The description and the summary ride the line at every gate.
|
|
143
|
+
expect(failure.obj.params).toEqual([{ type: 'string', length: VALUE.length }, { type: 'number' }]);
|
|
144
|
+
expect(failure.obj.cause).toEqual({
|
|
145
|
+
name: 'Error',
|
|
146
|
+
code: 'ER_NO_SUCH_TABLE',
|
|
147
|
+
errno: 1146,
|
|
148
|
+
sqlState: '42S02',
|
|
149
|
+
sqlMessage: `Table 'test.credential' doesn't exist`,
|
|
150
|
+
});
|
|
151
|
+
if (!on) {
|
|
152
|
+
for (const log of captured) {
|
|
153
|
+
expect(lineOf(log)).not.toContain(VALUE);
|
|
154
|
+
expect(Object.keys(log.obj ?? {})).not.toContain('paramValues');
|
|
155
|
+
expect(log.error).toBeUndefined();
|
|
156
|
+
}
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
// The values as bound, BESIDE the description; and the vendor error itself, as it was thrown.
|
|
160
|
+
expect(failure.obj.paramValues).toEqual(params);
|
|
161
|
+
expect(failure.error).toBe(caught);
|
|
162
|
+
expect(lineOf(failure)).toContain(VALUE);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// A bindings DICTIONARY — wider than `Statement` declares, and what the query layer accepts.
|
|
166
|
+
const namedParams = { id: VALUE, n: 42 };
|
|
167
|
+
const failingNamedInsert = (): Promise<any> =>
|
|
168
|
+
driver
|
|
169
|
+
.runDml(() => ({ sql: 'INSERT INTO `credential` (`id`, `n`) VALUES (:id, :n)', params: namedParams as any }))
|
|
170
|
+
.then(
|
|
171
|
+
() => 'resolved',
|
|
172
|
+
(error: unknown) => error
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
test.each(GATES)('a bindings dictionary rides the same switch — %s', async (_gate, env, on) => {
|
|
176
|
+
const baseline = facts(await failingNamedInsert());
|
|
177
|
+
captured = [];
|
|
178
|
+
Object.assign(process.env, env);
|
|
179
|
+
|
|
180
|
+
const caught = await failingNamedInsert();
|
|
181
|
+
|
|
182
|
+
const failures = captured.filter((log) => log.message === 'Failed when executing sql');
|
|
183
|
+
expect(failures).toHaveLength(1);
|
|
184
|
+
const [failure] = failures;
|
|
185
|
+
expect(caught.code).toBe('ER_NO_SUCH_TABLE');
|
|
186
|
+
expect(facts(caught)).toEqual(baseline);
|
|
187
|
+
// The description, by entry name, rides the line at every gate.
|
|
188
|
+
expect(failure.obj.params).toEqual({ id: { type: 'string', length: VALUE.length }, n: { type: 'number' } });
|
|
189
|
+
if (!on) {
|
|
190
|
+
for (const log of captured) {
|
|
191
|
+
expect(lineOf(log)).not.toContain(VALUE);
|
|
192
|
+
expect(Object.keys(log.obj ?? {})).not.toContain('paramValues');
|
|
193
|
+
expect(log.error).toBeUndefined();
|
|
194
|
+
}
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
// The dictionary as bound, BESIDE the description; and the vendor error itself, as it was thrown.
|
|
198
|
+
expect(failure.obj.paramValues).toEqual(namedParams);
|
|
199
|
+
expect(failure.error).toBe(caught);
|
|
200
|
+
});
|
|
201
|
+
});
|