Initial release, v0.0.1

master
Sven Slootweg 8 years ago
commit 262a79ae03

2
.gitignore vendored

@ -0,0 +1,2 @@
/node_modules/
/lib/

@ -0,0 +1 @@
/node_modules/

@ -0,0 +1,3 @@
## 1.0.0 (November 11, 2016)
Initial release.

@ -0,0 +1,203 @@
# database-error
A small library to make error objects from databases more programmatically useful. Currently supports:
* __PostgreSQL:__ Through `pg` (*not* `pg.native`!).
Database libraries tend to throw rather hard-to-process errors - often, they will contain a mishmash of information in a single error message string, with no clear way to filter or handle them programmatically. This library solves that by parsing the errors for you, and turning them into more consistent, useful, identifiable, and structured error objects.
Note that while Knex *is* supported (since it uses `pg` internally, for talking to PostgreSQL), it is *not* required - this library should work fine with any other `pg`-based implementation as well. Support for other (relational) database libraries is planned in the future, with the intention to provide a standardized format for useful database errors.
## License
[WTFPL](http://www.wtfpl.net/txt/copying/) or [CC0](https://creativecommons.org/publicdomain/zero/1.0/), whichever you prefer. A donation and/or attribution are appreciated, but not required.
## Donate
Maintaining open-source projects takes a lot of time, and the more donations I receive, the more time I can dedicate to open-source. If this module is useful to you, consider [making a donation](http://cryto.net/~joepie91/donate.html)!
You can donate using Bitcoin, PayPal, Flattr, cash-in-mail, SEPA transfers, and pretty much anything else. Thank you!
## Contributing
Pull requests welcome. Please make sure your modifications are in line with the overall code style, and ensure that you're editing the files in `src/`, not those in `lib/`.
Build tool of choice is `gulp`; simply run `gulp` while developing, and it will watch for changes.
Be aware that by making a pull request, you agree to release your modifications under the licenses stated above.
## Usage
A simple example, detecting a duplicate entry in the database:
```javascript
const Promise = require("bluebird");
const databaseError = require("database-error");
/* The below objects are used as 'object predicates' in Bluebird's .catch, to filter out the specific kind of database errors we're interested in. Errors are matched by the properties of the predicate object(s). */
let duplicateEmailAddress = {
name: "UniqueConstraintViolationError",
table: "users",
column: "email"
}
let duplicateUsername = {
name: "UniqueConstraintViolationError",
table: "users",
column: "username"
}
// ... assuming we have a Knex instance, and an Express application with an `express-promise-router` ...
router.post("/register", (req, res) => {
Promise.try(() => {
return knex("users").insert({
username: req.body.username,
email: req.body.emailAddress
}).returning("id");
}).then((id) => {
res.send(`Hello, user ${id[0]}!`);
}).catch(databaseError.rethrow).catch(duplicateEmailAddress, (err) => {
res.status(422).send("That e-mail address already exists! Please pick a different one.");
}).catch(duplicateUsername, (err) => {
res.status(422).send("That username already exists! Please pick a different one.");
})
});
```
This assumes the following:
* A `users` table exists, with an `id`, `username` and `email` column.
* The `email` and `username` columns both have a UNIQUE constraint set on them, disallowing duplicates.
When a user attempts to register a new account, and either their provided username or e-mail address already exist in the database the following happens:
1. A generic error will be thrown by the database library (eg. `pg`).
2. This error is picked up by `databaseError.rethrow` in the first `.catch` statement, and turned into a more useful error (with the clearly distinguishable `UniqueConstraintViolationError` name, and structured `table`/`column` properties).
3. This new, more useful error is *rethrown* by `databaseError.rethrow`.
4. The second and third `.catch` statement each receive the *new* error, check whether it has the properties they're looking for, and if so, trigger the corresponding error handler.
## API
### databaseError.rethrow(error)
Rethrows a more useful error, where possible.
* __error:__ The original database error object to convert.
Throws:
* __If the original error could be parsed:__ Throws the new, converted `database-error` error object. This will be one of the types specified below.
* __If the original error was not of a recognized type:__ Rethrows the original error, unmodified.
In practice, this means that unrecognized errors are "passed through", while recognized errors are converted into more useful errors.
### databaseError.convert(error)
__You would not normally use this method directly, and you should probably use `databaseError.rethrow` instead.__
Converts the specified `error` into a more useful Error object.
* __error:__ The original database error object to convert.
Returns/throws:
* __If the original error could be parsed:__ Returns the new, converted `database-error` error object. This will be one of the types specified below.
* __If the original error was not of a recognized type:__ Throws a `databaseError.UnknownError`.
### databaseError.UnknownError
Special error type that's thrown for `databaseError.convert` when an unrecognized error is provided. You will never encounter this error when using `databaseError.rethrow`, and it does not include any further information.
### Database error types
#### databaseError.DatabaseError
"Base type" that all other error types (except for `UnknownError`) are inheriting from. This can be useful for logging all recognized database errors.
However, note that you will never encounter a `DatabaseError` directly - it purely functions as a base type, meaning that you need to use `instanceof` to recognize an error as a `DatabaseError`, and matching by name is not sufficient.
Every error inheriting from `DatabaseError` will always contain the following properties:
* __originalError:__ The original error being converted.
* __pgCode:__ *Only for PostgreSQL.* The [SQLSTATE error code](https://www.postgresql.org/docs/current/static/errcodes-appendix.html) from PostgreSQL.
* __code:__ A unique, string-typed identifier for the error type.
In addition, different errors include different other properties. These are listed for each error type below.
#### databaseError.UniqueConstraintViolationError
This error is thrown when a query violates a `UNIQUE` constraint. In practice, this means that there's an attempt to insert a duplicate entry into your database (see also the Usage section above).
A `UniqueConstraintViolationError` will contain the following additional properties:
* __schema:__ The schema in which the violation occurred.
* __table:__ The table in which the violation occurred.
* __column:__ The column in which the violation occurred.
* __value:__ The offending duplicate value.
* __constraint:__ The name of the `UNIQUE` constraint that is being violated.
#### databaseError.ForeignKeyConstraintViolationError
This error is thrown when a query violates a foreign key constraint. In practice, this means that there's an attempt to point a foreign key at a non-existent entry (usually in another table).
A `ForeignKeyConstraintViolationError` will contain the following additional properties:
* __schema:__ The schema in which the violation occurred.
* __table:__ The table in which the violation occurred.
* __column:__ The column in which the violation occurred.
* __value:__ The offending foreign key (value).
* __constraint:__ The name of the foreign key constraint that is being violated.
* __foreignTable:__ The table to which the violating foreign key refers.
#### databaseError.NotNullConstraintViolationError
This error is thrown when a query violates a `NOT NULL` constraint. In practice, this usually means that a required value is missing.
A `NotNullConstraintViolationError` will contain the following additional properties:
* __schema:__ The schema in which the violation occurred.
* __table:__ The table in which the violation occurred.
* __column:__ The column in which the violation occurred - ie. the column for which a required value is missing.
* __values:__ An array of values, for the item being inserted/updated. Note that __all these values are string representations__, and they are intended purely for reference purposes.
* __query:__ The query that caused the violation. This query may contain placeholders or be incomplete, and so is intended purely for reference purposes.
#### databaseError.CheckConstraintViolationError
This error is thrown when a query violates a `CHECK` constraint. This can mean a lot of things (as `CHECK` constraints can be fairly arbitrary), but one of the most common cases is probably an invalid value for a Knex-defined "enum", since [Knex uses `CHECK` constraints rather than native ENUMs](https://github.com/tgriesser/knex/issues/394).
An `CheckConstraintViolationError` will contain the following additional properties:
* __schema:__ The schema in which the violation occurred.
* __table:__ The table in which the violation occurred.
* __column:__ The column in which the violation occurred - ie. the column for which an invalid value was specified. __This value is not always present.__ It is only present if the constraint name matches the `<table>_<column>_check` format (like is the case for Knex-defined "enum" columns).
* __values:__ An array of values, for the item being inserted/updated. Note that __all these values are string representations__, and they are intended purely for reference purposes.
* __query:__ The query that caused the violation. This query may contain placeholders or be incomplete, and so is intended purely for reference purposes.
* __constraint:__ The name of the `CHECK` constraint that is being violated.
#### databaseError.InvalidTypeError
This error is thrown when a query attempts to store a value of the wrong type in a column. Note that it does not *always* occur when storing a value of the wrong type, as some databases and libraries attempt to cast values automatically, and will only throw this error when they are unable to do so.
Unfortunately, when using PostgreSQL, `InvalidTypeError` errors include *very little information* to pinpoint the problem, and there is no way for this library to obtain more information. You should make sure to validate value types carefully before attempting to insert them into the database, to prevent this type of error from occurring.
An `InvalidTypeError` will contain the following additional properties:
* __table:__ The table in which the invalid value was inserted. __This value is not always present, and is purely for reference purposes.__ It is only available if the table name could be extracted from the query, and it *may* include a schema name as well.
* __expectedType:__ The type of value that was expected for the column in question.
* __value:__ The value that caused the type error.
* __query:__ The query that caused the type error. This query may contain placeholders or be incomplete, and so is intended purely for reference purposes.
#### databaseError.EnumError
This error is thrown when a query attempts to store a value in an ENUM-type column, and that value isn't one of the allowed values.
Unfortunately, when using PostgreSQL, `EnumError` errors include *very little information* to pinpoint the problem, and there is no way for this library to obtain more information. You should make sure to validate ENUM values in your application carefully before attempting to insert them into the database, to prevent this type of error from occurring.
An `EnumError` will contain the following additional properties:
* __table:__ The table in which the invalid value was inserted. __This value is not always present, and is purely for reference purposes.__ It is only available if the table name could be extracted from the query, and it *may* include a schema name as well.
* __enumType:__ The name of the ENUM type for which an invalid value was inserted.
* __value:__ The value that caused the error.
* __query:__ The query that caused the error. This query may contain placeholders or be incomplete, and so is intended purely for reference purposes.

@ -0,0 +1,18 @@
var gulp = require("gulp");
var presetES2015 = require("@joepie91/gulp-preset-es2015");
var source = ["src/**/*.js"]
gulp.task('babel', function() {
return gulp.src(source)
.pipe(presetES2015({
basePath: __dirname
}))
.pipe(gulp.dest("lib/"));
});
gulp.task("watch", function () {
gulp.watch(source, ["babel"]);
});
gulp.task("default", ["babel", "watch"]);

@ -0,0 +1,3 @@
'use strict';
module.exports = require("./lib");

@ -0,0 +1,36 @@
{
"name": "database-error",
"version": "1.0.0",
"description": "Turns errors from database libraries into more useful error objects",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "http://git.cryto.net/joepie91/node-database-error.git"
},
"keywords": [
"database",
"pg",
"postgresql",
"errors",
"error handling",
"knex",
"normalization"
],
"author": "Sven Slootweg",
"license": "WTFPL",
"dependencies": {
"create-error": "^0.3.1",
"pg-error-codes": "^1.0.0"
},
"devDependencies": {
"@joepie91/gulp-preset-es2015": "^1.0.1",
"babel-preset-es2015": "^6.6.0",
"bluebird": "^3.4.6",
"gulp": "^3.9.1",
"knex": "^0.12.6",
"pg": "^6.1.0"
}
}

@ -0,0 +1,5 @@
'use strict';
const createError = require("create-error");
module.exports = createError("DatabaseError");

@ -0,0 +1,60 @@
'use strict';
const createError = require("create-error");
const pgErrorCodes = require("pg-error-codes");
const DatabaseError = require("../database-error.js");
const getColumns = require("../get-columns");
const getValues = require("../get-values");
let CheckConstraintViolationError = createError(DatabaseError, "CheckConstraintViolationError");
let messageRegex = /^(.+) - new row for relation "([^"]+)" violates check constraint "([^"]+)"$/;
module.exports = {
error: CheckConstraintViolationError,
errorName: "CheckConstraintViolationError",
check: function checkType(error) {
return (
// PostgreSQL (via `pg`):
(error.length != null && error.file != null && error.line != null && error.routine != null && error.code === "23514")
)
},
convert: function convertError(error) {
let messageMatch = messageRegex.exec(error.message);
if (messageMatch == null) {
throw new Error("Encountered unknown error format");
}
let [_, query, table, constraint] = messageMatch;
let columns = getColumns(query);
let offendingColumn, message;
if (columns != null) {
/* This is the naming convention that Knex uses for .enum() in PostgreSQL */
offendingColumn = columns.find(column => {
console.log(`${error.table}_${column}_check`)
return error.constraint === `${error.table}_${column}_check`
});
message = `Value violates the '${error.constraint}' constraint for the '${offendingColumn}' column in the '${error.table}' table`;
} else {
message = `Value violates the '${error.constraint}' constraint for the '${error.table}' table`;
}
return new CheckConstraintViolationError(message, {
originalError: error,
pgCode: error.code,
code: pgErrorCodes[error.code],
query: query,
schema: error.schema,
table: error.table,
column: offendingColumn,
constraint: error.constraint,
values: getValues(error.detail)
});
}
};

@ -0,0 +1,50 @@
'use strict';
const createError = require("create-error");
const pgErrorCodes = require("pg-error-codes");
const DatabaseError = require("../database-error.js");
const getTable = require("../get-table");
let EnumError = createError(DatabaseError, "EnumError");
let messageRegex = /^(.+) - invalid input value for enum ([^:]+): "([^"]+)"$/;
module.exports = {
error: EnumError,
errorName: "EnumError",
check: function checkType(error) {
return (
// PostgreSQL (via `pg`):
(error.length != null && error.file != null && error.line != null && error.routine != null && error.code === "22P02" && error.message.includes("invalid input value for enum"))
)
},
convert: function convertError(error) {
let messageMatch = messageRegex.exec(error.message);
if (messageMatch == null) {
throw new Error("Encountered unknown error format");
}
let [_, query, enumType, value] = messageMatch;
let table = getTable(query);
let message;
if (table != null) {
message = `Value '${value}' is not an allowed value for the ENUM type '${enumType}' (in table '${table}')`;
} else {
message = `Value '${value}' is not an allowed value for the ENUM type '${enumType}'`;
}
return new EnumError(message, {
originalError: error,
pgCode: error.code,
code: pgErrorCodes[error.code],
query: query,
table: table,
enumType: enumType,
value: value
});
}
};

@ -0,0 +1,35 @@
'use strict';
const createError = require("create-error");
const pgErrorCodes = require("pg-error-codes");
const DatabaseError = require("../database-error.js");
let ForeignKeyConstraintViolationError = createError(DatabaseError, "ForeignKeyConstraintViolationError");
let detailsRegex = /^Key \(([^\)]+)\)=\(([^\)]+)\) is not present in table "([^"]+)"\.$/;
module.exports = {
error: ForeignKeyConstraintViolationError,
errorName: "ForeignKeyConstraintViolationError",
check: function checkType(error) {
return (
// PostgreSQL (via `pg`):
(error.length != null && error.file != null && error.line != null && error.routine != null && error.code === "23503")
)
},
convert: function convertError(error) {
let [_, column, value, foreignTable] = detailsRegex.exec(error.detail);
return new ForeignKeyConstraintViolationError(`Value for column '${column}' in table '${error.table}' refers to a non-existent key '${value}' in table '${foreignTable}'`, {
originalError: error,
pgCode: error.code,
code: pgErrorCodes[error.code],
schema: error.schema,
table: error.table,
foreignTable: foreignTable,
column: column,
value: value,
constraint: error.constraint
});
}
};

@ -0,0 +1,51 @@
'use strict';
const createError = require("create-error");
const pgErrorCodes = require("pg-error-codes");
const DatabaseError = require("../database-error.js");
const getTable = require("../get-table");
let InvalidTypeError = createError(DatabaseError, "InvalidTypeError");
/* NOTE: error messages vary. Eg. for boolean-type columns, it states "for type boolean", but for integer-type columns, it starts "for integer". */
let messageRegex = /^(.+) - invalid input syntax for(?: type)? ([^:]+): "([^"]+)"$/;
module.exports = {
error: InvalidTypeError,
errorName: "InvalidTypeError",
check: function checkType(error) {
return (
// PostgreSQL (via `pg`):
(error.length != null && error.file != null && error.line != null && error.routine != null && error.code === "22P02" && error.message.includes("invalid input syntax for"))
)
},
convert: function convertError(error) {
let messageMatch = messageRegex.exec(error.message);
if (messageMatch == null) {
throw new Error("Encountered unknown error format");
}
let [_, query, expectedType, value] = messageMatch;
let table = getTable(query);
let message;
if (table != null) {
message = `Value '${value}' is of the wrong type for a '${expectedType}'-type column (in table '${table}')`;
} else {
message = `Value '${value}' is of the wrong type for a '${expectedType}'-type column '${enumType}'`;
}
return new InvalidTypeError(message, {
originalError: error,
pgCode: error.code,
code: pgErrorCodes[error.code],
query: query,
table: table,
expectedType: expectedType,
value: value,
});
}
};

@ -0,0 +1,36 @@
'use strict';
const createError = require("create-error");
const pgErrorCodes = require("pg-error-codes");
const DatabaseError = require("../database-error.js");
const getValues = require("../get-values");
let NotNullConstraintViolationError = createError(DatabaseError, "NotNullConstraintViolationError");
let messageRegex = /^(.+) - null value in column "([^"]+)" violates not-null constraint$/;
module.exports = {
error: NotNullConstraintViolationError,
errorName: "NotNullConstraintViolationError",
check: function checkType(error) {
return (
// PostgreSQL (via `pg`):
(error.length != null && error.file != null && error.line != null && error.routine != null && error.code === "23502")
)
},
convert: function convertError(error) {
let [_, query, column] = messageRegex.exec(error.message);
return new NotNullConstraintViolationError(`Missing required value for column '${error.column}' in table '${error.table}'`, {
originalError: error,
pgCode: error.code,
code: pgErrorCodes[error.code],
schema: error.schema,
table: error.table,
column: error.column,
values: getValues(error.detail),
query: query
});
}
};

@ -0,0 +1,34 @@
'use strict';
const createError = require("create-error");
const pgErrorCodes = require("pg-error-codes");
const DatabaseError = require("../database-error.js");
let UniqueConstraintViolationError = createError(DatabaseError, "UniqueConstraintViolationError");
let detailsRegex = /Key \(([^\)]+)\)=\(([^\)]+)\) already exists\./;
module.exports = {
error: UniqueConstraintViolationError,
errorName: "UniqueConstraintViolationError",
check: function checkType(error) {
return (
// PostgreSQL (via `pg`):
(error.length != null && error.file != null && error.line != null && error.routine != null && error.code === "23505")
)
},
convert: function convertError(error) {
let [_, column, value] = detailsRegex.exec(error.detail);
return new UniqueConstraintViolationError(`Value '${value}' already exists for column '${column}' in table '${error.table}'`, {
originalError: error,
pgCode: error.code,
code: pgErrorCodes[error.code],
schema: error.schema,
table: error.table,
column: column,
value: value,
constraint: error.constraint
});
}
};

@ -0,0 +1,19 @@
'use strict';
let insertRegex = /^insert into "[^"]+" \(([^\)]+)\)/;
let updateRegex = /^update "[^"]+" set (.+) where/;
let updateColumnRegex = /^"([^"]+)"/;
module.exports = function getColumns(query) {
let match, columns;
if (match = insertRegex.exec(query)) {
return match[1].split(",").map((columnName) => {
return columnName.trim().slice(1, -1)
});
} else if (match = updateRegex.exec(query)) {
return match[1].split(",").map((statement) => {
return updateColumnRegex.exec(statement.trim())[1];
});
}
};

@ -0,0 +1,14 @@
'use strict';
let insertRegex = /^insert into "([^"]+)"/;
let updateRegex = /^update "([^"]+)"/;
module.exports = function getTable(query) {
let match;
if (match = insertRegex.exec(query)) {
return match[1];
} else if (match = updateRegex.exec(query)) {
return match[1];
}
};

@ -0,0 +1,15 @@
'use strict';
let detailsRegex = /^Failing row contains \(([^\)]+)\).$/;
module.exports = function getValues(detail) {
let detailsMatch = detailsRegex.exec(detail);
if (detailsMatch == null) {
throw new Error("Could not determine values for query");
}
let [_, valueList] = detailsMatch;
return valueList.split(", ");
};

@ -0,0 +1,51 @@
'use strict';
const createError = require("create-error");
const DatabaseError = require("./database-error");
let UnknownError = createError("UnknownError");
let handlers = [
require("./errors/unique-constraint-violation"),
require("./errors/check-constraint-violation"),
require("./errors/foreign-key-constraint-violation"),
require("./errors/enum"),
require("./errors/invalid-type"),
require("./errors/not-null-constraint-violation"),
]
function convertError(error) {
let handler = handlers.find(handler => handler.check(error));
if (handler != null) {
return handler.convert(error);
} else {
throw new UnknownError("The specified error is not of a recognized type");
}
}
function rethrowBetterError(originalError) {
try {
let betterError = convertError(originalError);
throw betterError;
} catch (err) {
if (err instanceof UnknownError) {
throw originalError;
} else {
throw err;
}
}
}
let errorTypes = handlers.reduce((errors, handler) => {
errors[handler.errorName] = handler.error;
return errors;
}, {});
module.exports = Object.assign({
convert: convertError,
rethrow: rethrowBetterError,
UnknownError: UnknownError,
DatabaseError: DatabaseError
}, errorTypes)

@ -0,0 +1,16 @@
'use strict';
const Promise = require("bluebird");
module.exports = {
up: function createCheckConstraintViolationTable(knex, errorHandler) {
return knex.schema.createTable("check_constraint_violation", (table) => {
table.increments("id");
table.enum("number_value", ["one", "two", "three"]);
table.text("name");
}).catch(errorHandler);
},
down: function dropCheckConstraintViolationTable(knex, errorHandler) {
return knex.schema.dropTable("check_constraint_violation").catch(errorHandler);
}
};

@ -0,0 +1,24 @@
'use strict';
const Promise = require("bluebird");
module.exports = {
up: function createEnumViolationTable(knex, errorHandler) {
return Promise.try(() => {
return knex.raw("CREATE TYPE number AS ENUM ('one', 'two', 'three')").catch(errorHandler);
}).then(() => {
return knex.schema.createTable("enum_violation", (table) => {
table.increments("id");
table.specificType("number_value", "number");
table.text("name");
}).catch(errorHandler);
});
},
down: function dropEnumViolationTable(knex, errorHandler) {
return Promise.try(() => {
return knex.schema.dropTable("enum_violation").catch(errorHandler);
}).then(() => {
return knex.raw("DROP TYPE number").catch(errorHandler);
});
}
};

@ -0,0 +1,28 @@
'use strict';
const Promise = require("bluebird");
module.exports = {
up: function createForeignKeyConstraintViolationTable(knex, errorHandler) {
return Promise.try(() => {
return knex.schema.createTable("foreign_key_constraint_users", (table) => {
table.increments("id");
table.text("username");
table.text("name");
}).catch(errorHandler);
}).then(() => {
return knex.schema.createTable("foreign_key_constraint_posts", (table) => {
table.increments("id");
table.integer("user_id").references("id").inTable("foreign_key_constraint_users");
table.text("body");
}).catch(errorHandler);
});
},
down: function dropForeignKeyConstraintViolationTable(knex, errorHandler) {
return Promise.try(() => {
return knex.schema.dropTable("foreign_key_constraint_posts").catch(errorHandler);
}).then(() => {
return knex.schema.dropTable("foreign_key_constraint_users").catch(errorHandler);
});
}
};

@ -0,0 +1,41 @@
'use strict';
const Promise = require("bluebird");
let tables = [
require("./unique-constraint-violation"),
require("./check-constraint-violation"),
require("./foreign-key-constraint-violation"),
require("./enum"),
require("./invalid-type"),
require("./not-null-constraint-violation")
];
let noop = function noop(err) {
// Do nothing.
}
let rethrow = function rethrowError(err) {
throw err;
}
module.exports = {
up: function createTables(knex) {
return Promise.map(tables, (table) => {
return table.up(knex, rethrow);
});
},
down: function dropTables(knex, ignoreErrors) {
let errorHandler;
if (ignoreErrors) {
errorHandler = noop;
} else {
errorHandler = rethrow;
}
return Promise.map(tables, (table) => {
return table.down(knex, errorHandler);
});
}
}

@ -0,0 +1,15 @@
'use strict';
module.exports = {
up: function createInvalidTypeTable(knex, errorHandler) {
return knex.schema.createTable("invalid_type", (table) => {
table.increments("id");
table.integer("age");
table.text("name");
table.boolean("active");
}).catch(errorHandler);
},
down: function dropInvalidTypeTable(knex, errorHandler) {
return knex.schema.dropTable("invalid_type").catch(errorHandler);
}
};

@ -0,0 +1,14 @@
'use strict';
module.exports = {
up: function createNotNullConstraintViolationTable(knex, errorHandler) {
return knex.schema.createTable("not_null_violation", (table) => {
table.increments("id");
table.text("email").notNull();
table.text("name");
}).catch(errorHandler);
},
down: function dropNotNullConstraintViolationTable(knex, errorHandler) {
return knex.schema.dropTable("not_null_violation").catch(errorHandler);
}
};

@ -0,0 +1,14 @@
'use strict';
module.exports = {
up: function createUniqueConstraintViolationTable(knex, errorHandler) {
return knex.schema.createTable("unique_constraint_violation", (table) => {
table.increments("id");
table.text("email").unique();
table.text("name");
}).catch(errorHandler);
},
down: function dropUniqueConstraintViolationTable(knex, errorHandler) {
return knex.schema.dropTable("unique_constraint_violation").catch(errorHandler);
}
};

@ -0,0 +1,38 @@
'use strict';
const databaseError = require("../");
const Promise = require("bluebird");
const knex = require("knex");
const util = require("util");
const path = require("path");
const createTables = require("./create-tables");
let db = knex({
client: "pg",
connection: {
host: "localhost",
user: "sven",
password: "password",
database: "database_error"
},
debug: true
});
return Promise.try(() => {
return createTables.down(db, true);
}).then(() => {
return createTables.up(db);
}).then(() => {
let testcase = require(path.join(process.cwd(), process.argv[2]));
return testcase(db);
}).catch(databaseError.rethrow).catch((err) => {
console.log("__________________\n");
console.error(util.inspect(err, {colors: true, depth: 1}));
console.log("__________________\n");
}).finally(() => {
return createTables.down(db);
}).finally(() => {
return db.destroy();
});

@ -0,0 +1,11 @@
'use strict';
module.exports = function attemptCheckConstraintViolation(knex) {
return knex("check_constraint_violation").insert([{
number_value: "one",
name: "Joe"
}, {
number_value: "four",
name: "Jane"
}]).returning("*");
};

@ -0,0 +1,18 @@
'use strict';
const Promise = require("bluebird");
module.exports = function attemptCheckConstraintViolation(knex) {
return Promise.try(() => {
return knex("check_constraint_violation").insert({
number_value: "one",
name: "Joe"
}).returning("id");
}).then((id) => {
return knex("check_constraint_violation").update({
number_value: "four"
}).where({
id: id[0]
});
});
};

@ -0,0 +1,11 @@
'use strict';
module.exports = function attemptEnumViolation(knex) {
return knex("enum_violation").insert([{
number_value: "one",
name: "Joe"
}, {
number_value: "four",
name: "Jane"
}]).returning("*");
};

@ -0,0 +1,18 @@
'use strict';
const Promise = require("bluebird");
module.exports = function attemptEnumViolation(knex) {
return Promise.try(() => {
return knex("enum_violation").insert({
number_value: "one",
name: "Joe"
}).returning("id");
}).then((id) => {
return knex("enum_violation").update({
number_value: "four"
}).where({
id: id[0]
});
});
};

@ -0,0 +1,26 @@
'use strict';
const Promise = require("bluebird");
module.exports = function attemptForeignKeyConstraintViolation(knex) {
return Promise.try(() => {
return knex("foreign_key_constraint_users").insert([{
name: "Joe",
username: "joe"
}, {
name: "Jane",
username: "jane"
}]).returning("id");
}).then((ids) => {
return knex("foreign_key_constraint_posts").insert([{
user_id: ids[0],
body: "Foo"
}, {
user_id: ids[1],
body: "Bar"
}, {
user_id: 567213,
body: "Baz"
}])
});
};

@ -0,0 +1,17 @@
'use strict';
module.exports = function attemptNotNullConstraintViolation(knex) {
return knex("invalid_type").insert([{
name: "Joe",
age: 29,
active: true
}, {
name: "Jane",
age: 42,
active: true
}, {
name: true,
age: 24,
active: false
}]).returning("*");
};

@ -0,0 +1,17 @@
'use strict';
module.exports = function attemptNotNullConstraintViolation(knex) {
return knex("invalid_type").insert([{
name: "Joe",
age: 29,
active: true
}, {
name: "Jane",
age: 42,
active: true
}, {
name: "Pete",
age: 24,
active: "foo bar"
}]).returning("*");
};

@ -0,0 +1,17 @@
'use strict';
module.exports = function attemptNotNullConstraintViolation(knex) {
return knex("invalid_type").insert([{
name: "Joe",
age: 29,
active: true
}, {
name: "Jane",
age: 42,
active: true
}, {
name: "Pete",
age: "twenty-four",
active: false
}]).returning("*");
};

@ -0,0 +1,12 @@
'use strict';
module.exports = function attemptNotNullConstraintViolation(knex) {
return knex("not_null_violation").insert([{
email: "foo@bar.com",
name: "Joe"
}, {
email: "baz@qux.com"
}, {
name: "Pete"
}]).returning("*");
};

@ -0,0 +1,14 @@
'use strict';
module.exports = function attemptUniqueConstraintViolation(knex) {
return knex("unique_constraint_violation").insert([{
email: "foo@bar.com",
name: "Joe"
}, {
email: "baz@qux.com",
name: "Jane"
}, {
email: "foo@bar.com",
name: "Pete"
}]).returning("*");
};
Loading…
Cancel
Save