Full Code of Vincit/knex-db-manager for AI

master f1e565c5888e cached
26 files
55.2 KB
14.3k tokens
7 symbols
1 requests
Download .txt
Repository: Vincit/knex-db-manager
Branch: master
Commit: f1e565c5888e
Files: 26
Total size: 55.2 KB

Directory structure:
gitextract_2zlax8t9/

├── .gitignore
├── .istanbul.yml
├── .npmignore
├── .prettierrc
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── docker-compose.yml
├── lib/
│   ├── DatabaseManager.js
│   ├── MySqlDatabaseManager.js
│   ├── PostgresDatabaseManager.js
│   ├── SqliteDatabaseManager.js
│   ├── class-utils.js
│   ├── index.js
│   └── multi-require.js
├── package.json
├── tests/
│   ├── database-manager.spec.js
│   ├── dialect-aliases.spec.js
│   ├── migrations/
│   │   ├── 20141024070315_test_schema.js
│   │   └── 20150623130922_id_sequence_test_table.js
│   └── populate/
│       ├── 01-user-data.js
│       ├── 02-pet-data.js
│       └── sample-data-old.js
└── wait-databases.sh

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
test-coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Typescript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env

# vscode history directory
.history

# npm5 package lock
package-lock.json

# gh-pages doc dir on my local setup
doc


================================================
FILE: .istanbul.yml
================================================
instrumentation:
  excludes: ['lib/multi-require.js', 'lib/class-utils.js']

reporting:
  dir: ./test-coverage

================================================
FILE: .npmignore
================================================
.*
*
!lib/*


================================================
FILE: .prettierrc
================================================
{
  "singleQuote": true,
  "trailingComma": "es5",
  "arrowParens": "always"
}


================================================
FILE: .travis.yml
================================================
# .travis.yml
language: node_js

node_js:
  - '10'
  - '12'
  - '14'

dist: trusty

after_script:
  - npm run-script coveralls

notifications:
  email: false

sudo: required

before_install:
  - docker-compose up -d
  - sh wait-databases.sh

install:
  - npm install
  - npm install knex


================================================
FILE: CHANGELOG.md
================================================
# Changelog

### 0.7.0

- Dropped node 8 and added node 14 to tests and fixed 0.6.2 changes

### 0.6.2

- NOTE: THIS WAS DEPRECATED FROM NPM DUE TO SOME BREAKING TESTS
- Set seeding to sequential instead of parallel #89
- Support for old-style seeding modules #88

### 0.6.1

- Added more files to gitignore to make package smaller

### 0.6.0

- Fixed special character escaping in password for postgresql
- Updated dependencies
- Added prettier config
- Added CI testing for node 12

#### Breaking changes

- Moved knex from normal deps to peerDep, so now one has to npm install knex explicitly

### 0.5.0

- Fixed id sequence queries to work on postgresql 10 databases even when
- Updated dependencies
- Removed gatsby and started to use vuepress for docs
- Added CI testing for node 8 and 10

### 0.4.0

- Fix allowing to create new databases even when multiple clients are connected to postgres server #36
- Updated list of supported dialect aliases #33
- Fixed npm repo to point Vincit #31
- Updated various dependencies
- Initial support for mysql #13, #30

#### Breaking changes

- truncateDb doesn't automatically leave migration table out #10
- Add owner privileges for the database user after creating new database as super user #15

### 0.3.0

- Added documentation pages

#### Breaking changes

- Removed automatic ingnore of migrations table from truncate DB
- Removed automatic testing / support for node < 4

### 0.2.0

- Initial changelog entry

#### Breaking changes

- All functionality


================================================
FILE: CONTRIBUTING.md
================================================
# Installing development environment

TDB

# Running tests

TDB


================================================
FILE: LICENSE
================================================
Copyright (c) 2015, Sami Koskimäki
Copyright (c) 2016, Mikael Lepistö

Permission to use, copy, modify, and/or distribute this software for any purpose 
with or without fee is hereby granted, provided that the above copyright notice 
and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD 
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. 
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA 
OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.



================================================
FILE: README.md
================================================
[![Build Status](https://travis-ci.org/Vincit/knex-db-manager.svg?branch=master)](https://travis-ci.org/Vincit/knex-db-manager)
[![Coverage Status](https://coveralls.io/repos/github/Vincit/knex-db-manager/badge.svg?branch=master)](https://coveralls.io/github/Vincit/knex-db-manager?branch=master)
[![Greenkeeper badge](https://badges.greenkeeper.io/Vincit/knex-db-manager.svg)](https://greenkeeper.io/)

Pretty useful when writing scripts to initialize database for fresh install or
dropping / creating new database when running tests and for truncating database
between tests.

Library uses knex connection for non administrative queries, but also creates
priviliged connection directly with driver with superuser privileges for creating
and dropping databases / roles.

## Supported Databases

- PostgreSQL
- MySQL
- SQLite3 (partial support even though most of the functions won't make sense with this)
- ~~Oracle DB Express (TBD)~~
- ~~MSSQL (TBD if we can get integration tests to run automatically)~~

## Install

You need to install `knex`, database driver and `knex-db-manager`

```
npm install knex-db-manager knex pg pg-escape
```

## API & Usage

Database manager is initialized with normal `knex` configuration and with
superuser account which should be able to create / drop roles and databases.

> Initialization:

```js
let config = {
  knex: {
    // just the usual knex configuration
    client: 'postgres',
    connection: {
      host: 'localhost',
      database: 'appdb',
      user: 'dbowneruser',
      password: 'dbownerpassword',
    },
    pool: {
      min: 0,
      max: 10,
    },
    migrations: {
      directory: __dirname + '/migrations',
    },
  },
  dbManager: {
    // db manager related configuration
    collate: ['fi_FI.UTF-8', 'Finnish_Finland.1252'],
    superUser: 'userwithrightstocreateusersanddatabases',
    superPassword: 'privilegeduserpassword',
    populatePathPattern: 'data/**/*.js', // glob format for searching seeds
  },
};

let dbManager = require('knex-db-manager').databaseManagerFactory(config);
```

### `createDbOwnerIfNotExist(): Promise<void>`

Creates the user, which is described in `knex` configuration. This user is used as
the database owner when creating new databases.

```js
let promise = dbManager.createDbOwnerIfNotExist();
```

### `createDb(dbName?: string): Promise<void>`

Creates database described in `knex` configuration or by given name. Owner of the
created database is set to be the `config.knex.connection.user`.

`dbName` is the name of the database to be created, if not given the name is read
from `config.knex.connection.database`.

> Read database from `config.knex.connection.database`:

```js
let promise = dbManager.createDb();
```

> By given name:

```js
let promise = dbManager.createDb('brave-new-db');
```

### `dropDb(dbName?: string): Promise<void>`

Drops database described in `knex` configuration or by given name. Note
that if there are any active connections to the database that is being
dropped, the drop command might fail.

`dbName` is the name of the database to be dropped, if not given the name
is read from `config.knex.connection.database`.

> Drop database `config.knex.connection.database`:

```js
let promise = dbManager.dropDb();
```

> By specific name:

```js
let promise = dbManager.dropDb('brave-new-db');
```

### `copyDb(fromDbName: string, toDbName: string): Promise<void>`

Clones database to another name remotely on db serverside (may be useful e.g.
to make backup before running migrations).

New database `toDatabaseName` will be created containing a copy of `fromDbName`.

Note: This method is not supported with MySQL (yet).

> Making copy of DB:

```js
let promise = dbManager.copyDb('brave-new-db', 'brave-new-db-copy');
```

### `truncateDb(ignoreTables?: string[]): Promise<void>`

Truncate tables of the database and reset corresponding id sequences.

`ignoreTables` list of tables names which should not be truncated.

> Truncate database `config.knex.connection.database`:

```js
let promise = dbManager.truncateDb();
```

> ignore certain tables:

```js
let promise = dbManager.truncateDb(['migrations']);
```

### `updateIdSequences(): Promise<void>`

Updates all primary key id sequences to be biggest id in table + 1.
So after running this next `INSERT` to table will get valid id for
the row from the sequence.

This was motivated by some people who liked to create test data with
hard coded ids, so this helps them to make app to work normally after
adding rows to tables, which has not used id sequence to get ids.

The function assumes that the primary key for each table is called `id`.

Note: This method is not supported with MySQL (yet).

> Reset sequence of database `config.knex.connection.database`:

```js
let promise = dbManager.updateIdSequences();
```

### `populateDb(glob: string): Promise<void>`

Finds `knex` seed files by pattern and populate database with them.

`glob` is a pattern to match files to be ran, if not given the name is
read from `config.dbManager.populatePathPattern`.

> Get database from `config.knex.connection.database` and pattern
> from `config.dbManager.populatePathPattern`:

```js
let promise = dbManager.populateDb();
```

> with pattern:

```js
let promise = dbManager.populateDb(path.join(__dirname, 'seeds', 'test-*'));
```

### `migrateDb(): Promise<void>`

Runs `knex` migrations configured in knex config.

> Get database from `config.knex.connection.database`:

```js
let promise = dbManager.migrateDb();
```

### `dbVersion(): Promise<string>`

Checks which migrations has been ran to database.

Expects that migration name starts with timestamp.

If no migrations has been run, promise resolves to `'none'`. Otherwise
resolves to first numbers of latest migration file ran e.g. for
`20141024070315_test_schema.js` version will be `'20141024070315'`.

> Get database from `config.knex.connection.database`:

```js
let promise = dbManager.dbVersion();
```

### `close(): Promise<void>`

Closes the single privileged connection and all normal knex connections.

> Kill database connection:

```js
let promise = dbManager.close();
```

### `closeKnex(): Promise<void>`

Closes knex connection which is made to the database for unprivileged
queries. Sometimes this is needed e.g. for being able to drop database.

> Close knex connection

```js
let promise = dbManager.closeKnex();
```

### `knexInstance(): QueryBuilder`

Returns `knex` query builder bound to configured database.

> Get database from `config.knex.connection.database`:

```js
let knex = dbManager.knexInstance();
knex('table')
  .where('id', 1)
  .then((rows) => {
    console.log('Query was ran with db owner privileges', rows);
  });
```


================================================
FILE: docker-compose.yml
================================================
version: '3'

services:
  mysql:
    image: mysql:5.7
    ports:
      - '13306:3306'
    environment:
      - TZ=UTC
      - MYSQL_ROOT_PASSWORD=mysqlrootpassword
  postgresql:
    image: mdillon/postgis:9.6
    ports:
      - '15432:5432'
    environment:
      - POSTGRES_PASSWORD=po"st#gr%e!sr:oo;tpa@ss%word
      - POSTGRES_USER=postgres
  postgresql10:
    image: mdillon/postgis:10
    ports:
      - '25432:5432'
    environment:
      - POSTGRES_PASSWORD=po"st#gr%e!sr:oo;tpa@ss%word
      - POSTGRES_USER=postgres
#  mssql:
#    image: microsoft/mssql-server-linux
#    ports:
#      - "11433:1433"
#    environment:
#      - ACCEPT_EULA=Y
#      - SA_PASSWORD=mssqlpassword
# Actually its better to install oracle directly on host to be
# able to compile node-oracledb to connect the DB
#  oracledbxe:
#    image: wnameless/oracle-xe-11g
#    ports:
#      - "11521:1521"
#    environment:
#      - ORACLE_ALLOW_REMOTE=true


================================================
FILE: lib/DatabaseManager.js
================================================
var _ = require('lodash'),
  Knex = require('knex'),
  Promise = require('bluebird'),
  multiRequire = require('./multi-require').multiRequire;

/**
 * Base class for database managers.
 *
 * Database manager is used to create/drop databases, run migrations for them and doing other "super user" stuff.
 *
 * @constructor
 * @param {object} config
 *    Database configuration. See `index.js` for example config and `database` feature for further description.
 */
function DatabaseManager(config) {
  this.config = config;
  this._knex = null;
}

/**
 * Creates user which is used to access the database described in knex configuration.
 *
 * @returns {Promise} Resolves if success reject if user could not be created for some reason.
 */
DatabaseManager.prototype.createDbOwnerIfNotExist = function() {
  throw new Error(this.constructor.name + '.createDbOwner not implemented');
};

/**
 * Creates and initializes a database `databaseName`.
 *
 * Rejects promise if databaseName already exists or could not
 * be created.
 *
 * @param {String=} databaseName if not given, database from knex config is used
 * @returns {Promise}
 */
DatabaseManager.prototype.createDb = function(databaseName) {
  throw new Error(this.constructor.name + '.initDb not implemented');
};

/**
 * Drops the database `databaseName`.
 *
 * @param {String=} databaseName
 * @returns {Promise}
 */
DatabaseManager.prototype.dropDb = function(databaseName) {
  throw new Error(this.constructor.name + '.dropDb not implemented');
};

/**
 * Makes copy of database.
 *
 * Good for backing up stuff before running migrations etc.
 *
 * @param {String} fromDatabaseName
 * @param {String} toDatabaseName
 * @returns {Promise}
 */
DatabaseManager.prototype.copyDb = function(fromDatabaseName, toDatabaseName) {
  throw new Error(this.constructor.name + '.copyDb not implemented');
};

/**
 * Truncates all tables in the database from knex configuration and also resets all
 * sequences.
 *
 * @returns {Promise}
 */
DatabaseManager.prototype.truncateDb = function(ignoreTables) {
  throw new Error(this.constructor.name + '.truncateDb not implemented');
};

/**
 * Updates the primary key sequences for all tables so that next insert selects
 * correct id (one that does not conflict with previous ids and is valid).
 *
 * This means that the next id will be greater (by 1) than currently largest
 * id in the table. If the table is empty, minimum value for the key sequence
 * will be used instead.
 *
 * This function assumes that the primary key for each table is called `id`.
 *
 * @returns {Promise}
 */
DatabaseManager.prototype.updateIdSequences = function() {
  throw new Error(this.constructor.name + '.updateIdSequences not implemented');
};

/**
 * Populate database.
 *
 * @param {String=} populatePathPattern
 * @returns {Promise}
 */
DatabaseManager.prototype.populateDb = function(populatePathPattern) {
  populatePathPattern = populatePathPattern || this.config.dbManager.populatePathPattern;

  var knex = this.knexInstance();
  var modules = multiRequire(populatePathPattern)
    .filterModule(function(module) {
      return _.isFunction(module) || (module && _.isFunction(module.seed))
    })
    .require();

  return _.reduce(modules, function(res, module) {
    return res.then(function() {
      return knex.transaction(function(trx) {
        if (_.isFunction(module.module)) {
          return module.module(trx);
        } else if (module.module && _.isFunction(module.module.seed)) {
          return module.module.seed(trx);
        }
      });
    });
  }, Promise.resolve());
};

/**
 * Runs migrations for database in knex config.
 *
 * @returns {Promise}
 */
DatabaseManager.prototype.migrateDb = function() {
  var knex = this.knexInstance();
  return knex.migrate.latest();
};

/**
 * Gets the migration version of the database of knex config.
 *
 * If no migrations run returns 'none'
 * Otherwise returns first numbers of latest migration file ran
 * e.g. for 20141024070315_test_schema.js version will be
 * '20141024070315'
 *
 * @returns {Promise}
 */
DatabaseManager.prototype.dbVersion = function() {
  var knex = this.knexInstance();
  return knex.migrate.currentVersion();
};

/**
 * Closes all connections made by the the manager.
 *
 * @returns {Promise}
 */
DatabaseManager.prototype.close = function() {
  throw new Error(this.constructor.name + '.close not implemented');
};

/**
 * Closes connection made with knex directly to certain database
 */
DatabaseManager.prototype.closeKnex = function() {
  if (this._knex) {
    knex = this._knex;
    this._knex = null;
    return knex.destroy();
  }
  return Promise.resolve();
};

/**
 * @return {QueryBuilder} Knex query builder for knex configuration.
 */
DatabaseManager.prototype.knexInstance = function() {
  if (!this._knex) {
    this._knex = Knex(this.config.knex);
  }
  return this._knex;
};

module.exports = {
  default: DatabaseManager,
  DatabaseManager: DatabaseManager,
};


================================================
FILE: lib/MySqlDatabaseManager.js
================================================
var DatabaseManager = require('./DatabaseManager').default,
  classUtils = require('./class-utils'),
  mysql = require('mysql'),
  Promise = require('bluebird'),
  _ = require('lodash');

/**
 * @constructor
 * @extends DatabaseManager
 *
 * Notes:
 *   - Even though the method signature implicates that _masterConnectionUrl returns
 *     an URL string, it actually returns an object because MySQL node lib
 *     assumes that the database name is defined in the URL format.
 *
 */
function MySqlDatabaseManager() {
  DatabaseManager.apply(this, arguments);
  this._masterClient = null;
  this._cachedTableNames = null;
}

classUtils.inherits(MySqlDatabaseManager, DatabaseManager);

/**
 * @Override
 */
MySqlDatabaseManager.prototype.createDbOwnerIfNotExist = function() {
  return this._masterQuery("CREATE USER IF NOT EXISTS ?@'%' IDENTIFIED BY ?", [
    this.config.knex.connection.user,
    this.config.knex.connection.password,
  ]);
};

/**
 * @Override
 */
MySqlDatabaseManager.prototype.createDb = function(databaseName) {
  databaseName = databaseName || this.config.knex.connection.database;
  var collate = this.config.dbManager.collate;
  var owner = this.config.knex.connection.user;
  var self = this;
  var promise = Promise.reject(new Error());

  if (_.isEmpty(collate)) {
    promise = promise.catch(function() {
      return self._masterQuery(
        'CREATE DATABASE ?? DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci',
        [databaseName]
      );
    });
  } else {
    // Try to create with each collate. Use the first one that works. This is kind of a hack
    // but seems to be the only reliable way to make this work with both windows and unix.
    _.each(collate, function(locale) {
      promise = promise.catch(function() {
        return self._masterQuery(
          'CREATE DATABASE ?? DEFAULT CHARACTER SET utf8 DEFAULT COLLATE ?',
          [databaseName, locale]
        );
      });
    });
  }

  promise = promise.then(function() {
    return self._masterQuery('GRANT ALL PRIVILEGES ON ??.* TO ??', [
      databaseName,
      owner,
    ]);
  });

  return promise;
};

/**
 * Drops database with name if db exists.
 *
 * @Override
 */
MySqlDatabaseManager.prototype.dropDb = function(databaseName) {
  databaseName = databaseName || this.config.knex.connection.database;
  return this._masterQuery('DROP DATABASE IF EXISTS ??', [databaseName]);
};

/**
 * @Override
 */
MySqlDatabaseManager.prototype.truncateDb = function(ignoreTables) {
  var knex = this.knexInstance();
  var config = this.config;

  if (!this._cachedTableNames) {
    this._updateTableNameCache(knex, config);
  }

  return this._cachedTableNames.then(function(tableNames) {
    if (!_.isEmpty(tableNames)) {
      return knex.transaction(function(trx) {
        return knex
          .raw('SET FOREIGN_KEY_CHECKS = 0')
          .transacting(trx)
          .then(function() {
            // ignore the tables based on `ignoreTables`
            var filteredTables = _.differenceWith(
              tableNames,
              ignoreTables,
              _.isEqual
            );
            return Promise.map(
              filteredTables,
              function(tableName) {
                return knex
                  .table(tableName)
                  .truncate()
                  .transacting(trx);
              },
              { concurrency: 1 }
            );
          });
      });
    }
  });
};

/**
 * @private
 */
MySqlDatabaseManager.prototype._updateTableNameCache = function(knex, config) {
  this._cachedTableNames = knex('information_schema.tables')
    .select('table_name')
    .where('table_schema', config.knex.connection.database)
    .then(function(tables) {
      return _.without(
        _.map(tables, 'table_name'),
        config.knex.migrations.tableName
      );
    });
};

/**
 * @Override
 */
MySqlDatabaseManager.prototype.close = function() {
  var disconnectAll = [this.closeKnex()];
  if (this._masterClient) {
    disconnectAll.push(
      this._masterClient.then(function(client) {
        client.end();
      })
    );
    this._masterClient = null;
  }
  return Promise.all(disconnectAll);
};

/**
 * @private
 * @returns {Promise}
 */
MySqlDatabaseManager.prototype._masterQuery = function(query, params) {
  var self = this;
  if (!this._masterClient) {
    this._masterClient = this.create_masterClient();
  }
  return this._masterClient.then(function(client) {
    return self.perform_masterQuery(client, query, params);
  });
};

/**
 * @private
 * @returns {Promise}
 */
MySqlDatabaseManager.prototype.create_masterClient = function() {
  var self = this;
  return new Promise(function(resolve, reject) {
    var client = mysql.createConnection(self._masterConnectionUrl());
    client.connect(function(err) {
      if (err) {
        reject(err);
      } else {
        resolve(client);
      }
    });
  });
};

/**
 * @private
 * @returns {Promise}
 */
MySqlDatabaseManager.prototype.perform_masterQuery = function(
  client,
  query,
  params
) {
  return new Promise(function(resolve, reject) {
    if (params) {
      query = mysql.format(query, params);
    }
    client.query(query, function(err, result) {
      if (err) {
        reject(err);
      } else {
        resolve(result);
      }
    });
  });
};

/**
 * @private
 * @returns {String}
 */
MySqlDatabaseManager.prototype._masterConnectionUrl = function() {
  return {
    host: this.config.knex.connection.host,
    port: this.config.knex.connection.port || 3306,
    user: this.config.dbManager.superUser,
    password: this.config.dbManager.superPassword,
  };
};

module.exports = {
  default: MySqlDatabaseManager,
  MySqlDatabaseManager: MySqlDatabaseManager,
};


================================================
FILE: lib/PostgresDatabaseManager.js
================================================
var _ = require('lodash'),
  pg = require('pg'),
  escape = require('pg-escape'),
  Promise = require('bluebird'),
  classUtils = require('./class-utils'),
  DatabaseManager = require('./DatabaseManager').default;

/**
 * @constructor
 */
function PostgresDatabaseManager() {
  DatabaseManager.apply(this, arguments);
  this._masterClient = null;
  this._cachedTableNames = null;
  this._cachedIdSequences = null;
}

classUtils.inherits(PostgresDatabaseManager, DatabaseManager);

/**
 * @Override
 */
PostgresDatabaseManager.prototype.createDbOwnerIfNotExist = function() {
  return this._masterQuery(
    "DO $body$ BEGIN CREATE ROLE %I LOGIN PASSWORD %L; EXCEPTION WHEN others THEN RAISE NOTICE 'User exists, not re-creating'; END $body$;",
    [this.config.knex.connection.user, this.config.knex.connection.password]
  );
};

/**
 * @Override
 */
PostgresDatabaseManager.prototype.createDb = function(databaseName) {
  databaseName = databaseName || this.config.knex.connection.database;
  var collate = this.config.dbManager.collate;
  var owner = this.config.knex.connection.user;
  var self = this;
  var promise = Promise.reject(new Error());

  if (_.isEmpty(collate)) {
    promise = promise.catch(function() {
      return self._masterQuery(
        "CREATE DATABASE %I OWNER = '%I' ENCODING = 'UTF-8' TEMPLATE template1",
        [databaseName, owner]
      );
    });
  } else {
    // Try to create with each collate. Use the first one that works. This is kind of a hack
    // but seems to be the only reliable way to make this work with both windows and unix.
    _.each(collate, function(locale) {
      promise = promise.catch(function() {
        return self._masterQuery(
          "CREATE DATABASE %I OWNER = '%I' ENCODING = 'UTF-8' LC_COLLATE = %L TEMPLATE template0",
          [databaseName, owner, locale]
        );
      });
    });
  }

  return promise;
};

/**
 * Drops database with name if db exists.
 *
 * @Override
 */
PostgresDatabaseManager.prototype.dropDb = function(databaseName) {
  var self = this;
  databaseName = databaseName || this.config.knex.connection.database;
  return this.closeKnex().then(function() {
    return self._masterQuery('DROP DATABASE IF EXISTS %I', [databaseName]);
  });
};

/**
 * @Override
 */
PostgresDatabaseManager.prototype.copyDb = function(
  fromDatabaseName,
  toDatabaseName
) {
  var self = this;
  return this.closeKnex().then(function() {
    return self._masterQuery('CREATE DATABASE %I template %I', [
      toDatabaseName,
      fromDatabaseName,
    ]);
  });
};

/**
 * @Override
 */
PostgresDatabaseManager.prototype.truncateDb = function(ignoreTables) {
  var knex = this.knexInstance();
  var config = this.config;

  if (!this._cachedTableNames) {
    this._updateTableNameCache(knex, config);
  }

  return this._cachedTableNames.then(function(tableNames) {
    var filteredTableNames = _.filter(tableNames, function(tableName) {
      return !_.includes(ignoreTables || [], tableName);
    });
    if (!_.isEmpty(filteredTableNames)) {
      return knex.raw(
        'TRUNCATE TABLE "' +
          filteredTableNames.join('","') +
          '" RESTART IDENTITY'
      );
    }
  });
};

/**
 * @Override
 */
PostgresDatabaseManager.prototype.updateIdSequences = function() {
  var knex = this.knexInstance();
  var config = this.config;

  if (!this._cachedIdSequences) {
    this._updateIdSequenceCache(knex, config);
  }

  // Set current value of id sequence for each table.
  // If there are no rows in the table, the value will be set to sequence's minimum constraint.
  // Otherwise, it will be set to max(id) + 1.
  return this._cachedIdSequences.then(function(result) {
    var query = _.map(result.rows, function(row) {
      return escape(
        'SELECT setval(\'"%s"\', GREATEST(coalesce(max(id),0) + 1, \'%s\'), false) FROM "%I"',
        row.sequence,
        row.min,
        row.table
      );
    });

    query = query.join(' UNION ALL ') + ';';
    return knex.raw(query);
  });
};

/**
 * @private
 */
PostgresDatabaseManager.prototype._updateTableNameCache = function(
  knex,
  config
) {
  this._cachedTableNames = knex('pg_tables')
    .select('tablename')
    .where('schemaname', 'public')
    .then(function(tables) {
      return _.map(tables, 'tablename');
    });
};

/**
 * Id sequence cache holds a Promise, that returns following objects:
 * {
 *   table: String, // Table that rest of the values target
 *   sequence: String, // Sequence for the primary key (which is assumed to be id)
 *   min: String // Minimum allowed value for the sequence
 * }
 *
 * These values are cached because they are not expected to change often,
 * and finding them is slow.
 *
 * @private
 */
PostgresDatabaseManager.prototype._updateIdSequenceCache = function(
  knex,
  config
) {
  if (!this._cachedTableNames) {
    this._updateTableNameCache(knex, config);
  }

  this._cachedIdSequences = this._cachedTableNames
    .then(function(tableNames) {
      // Skip tables without id column.
      return knex('information_schema.columns')
        .select('table_name')
        .where('column_name', 'id')
        .then(function(tables) {
          return _.intersection(_.map(tables, 'table_name'), tableNames);
        });
      // Find name of the id sequence for each table.
      // This is required for searching the minimum constraint for the sequence.
    })
    .then(function(idTableNames) {
      var query = _.map(idTableNames, function(tableName) {
        return escape(
          "SELECT '%I' AS table, substring(pg_get_serial_sequence('\"%I\"', 'id') from '\"\\?([^\".]+)\"\\?$') AS sequence, substring(pg_get_serial_sequence('\"%I\"', 'id') from '^\"\\?([^\".]+)\"\\?.') AS schema",
          tableName,
          tableName,
          tableName
        );
      });

      query = query.join(' UNION ALL ') + ';';
      return knex.raw(query);
      // Find min constraint for each of the id sequences.
    })
    .then(function(result) {
      var query = _.map(result.rows, function(row) {
        return escape(
          "SELECT '%I' AS table, '%s' AS sequence, minimum_value AS min FROM information_schema.sequences where sequence_name = '%s' and sequence_schema = '%s'",
          row.table,
          row.sequence,
          row.sequence,
          row.schema
        );
      });

      query = query.join(' UNION ALL ') + ';';
      return knex.raw(query);
    });
};

/**
 * @Override
 */
PostgresDatabaseManager.prototype.close = function() {
  var disconnectAll = [this.closeKnex()];
  if (this._masterClient) {
    disconnectAll.push(
      this._masterClient.then(function(client) {
        client.end();
      })
    );
    this._masterClient = null;
  }
  return Promise.all(disconnectAll);
};

/**
 * @private
 * @returns {Promise}
 */
PostgresDatabaseManager.prototype._masterQuery = function(query, params) {
  var self = this;
  if (!this._masterClient) {
    this._masterClient = this.create_masterClient();
  }
  return this._masterClient.then(function(client) {
    return self.perform_masterQuery(client, query, params);
  });
};

/**
 * @private
 * @returns {Promise}
 */
PostgresDatabaseManager.prototype.create_masterClient = function() {
  var self = this;
  return new Promise(function(resolve, reject) {
    var client = new pg.Client({
      connectionString: self._masterConnectionUrl(),
    });

    client.connect(function(err) {
      if (err) {
        reject(err);
      } else {
        resolve(client);
      }
    });
  });
};

/**
 * @private
 * @returns {Promise}
 */
PostgresDatabaseManager.prototype.perform_masterQuery = function(
  client,
  query,
  params
) {
  return new Promise(function(resolve, reject) {
    if (params) {
      var args = [query].concat(params);
      query = escape.apply(global, args);
    }
    client.query(query, function(err, result) {
      if (err) {
        reject(err);
      } else {
        resolve(result);
      }
    });
  });
};

/**
 * @private
 * @returns {String}
 */
PostgresDatabaseManager.prototype._masterConnectionUrl = function() {
  var url = 'postgres://';
  if (this.config.dbManager.superUser) {
    url += this.config.dbManager.superUser;
  } else {
    throw new Error('DatabaseManager: database config must have `superUser`');
  }
  if (this.config.dbManager.superPassword) {
    url +=
      ':' +
      encodeURIComponent(this.config.dbManager.superPassword).replace(
        /!/g,
        '%21'
      );
  }
  var port = this.config.knex.connection.port || 5432;
  url += '@' + this.config.knex.connection.host + ':' + port + '/postgres';

  return url;
};

module.exports = {
  default: PostgresDatabaseManager,
  PostgresDatabaseManager: PostgresDatabaseManager,
};


================================================
FILE: lib/SqliteDatabaseManager.js
================================================
var DatabaseManager = require('./DatabaseManager').default,
  classUtils = require('./class-utils');

/**
 * @constructor
 * @extends DatabaseManager
 */
function SqliteDatabaseManager() {
  DatabaseManager.apply(this, arguments);
}

classUtils.inherits(SqliteDatabaseManager, DatabaseManager);

module.exports = {
  default: SqliteDatabaseManager,
  SqliteDatabaseManager: SqliteDatabaseManager
};


================================================
FILE: lib/class-utils.js
================================================
var _ = require('lodash'),
  util = require('util');

/**
 * Makes the `Constructor` inherit `SuperConstructor`.
 *
 * Calls node.js `util.inherits` but also copies the "static" properties from
 * `SuperConstructor` to `Constructor`.
 *
 * @param {function} Constructor
 * @param {function} SuperConstructor
 */
module.exports.inherits = function(Constructor, SuperConstructor) {
  var keys = Object.keys(SuperConstructor);
  for (var i = 0, l = keys.length; i < l; ++i) {
    var key = keys[i];
    Constructor[key] = SuperConstructor[key];
  }
  util.inherits(Constructor, SuperConstructor);
  Constructor.super_ = SuperConstructor;
};

/**
 * Tests if a constructor function inherits another constructor function.
 *
 * @ignore
 * @param {Object} Constructor
 * @param {Object} SuperConstructor
 * @returns {boolean}
 */
module.exports.isSubclassOf = function(Constructor, SuperConstructor) {
  if (!_.isFunction(SuperConstructor)) {
    return false;
  }

  while (_.isFunction(Constructor)) {
    if (Constructor === SuperConstructor) return true;
    var proto = Constructor.prototype.__proto__;
    Constructor = proto && proto.constructor;
  }

  return false;
};


================================================
FILE: lib/index.js
================================================
/**
 * Configuration is dodo-objection configuration.
 *
 * @param config Dodo-objection configuration.
 * @return {DatabaseManager}
 */
module.exports = {
  databaseManagerFactory: function databaseManagerFactory(config) {
    // Prevent people from invoking this as a constructor.
    if (this instanceof databaseManagerFactory) {
      throw new Error('this is not a constructor');
    }

    switch (config.knex.client) {
      case 'pg':
      case 'postgresql':
      case 'postgres': {
        var PostgresDatabaseManager = require('./PostgresDatabaseManager')
          .default;
        return new PostgresDatabaseManager(config);
      }
      case 'maria':
      case 'mariadb':
      case 'mariasql':
      case 'mysql': {
        var MySqlDatabaseManager = require('./MySqlDatabaseManager').default;
        return new MySqlDatabaseManager(config);
      }
      case 'sqlite3':
      case 'sqlite': {
        var SqliteDatabaseManager = require('./SqliteDatabaseManager').default;
        return new SqliteDatabaseManager(config);
      }
      default:
        throw new Error(
          config.knex.client +
            ' is not supported. Supported clients currently: postgres, mysql and sqlite'
        );
    }
  },
};


================================================
FILE: lib/multi-require.js
================================================
var _ = require('lodash'),
  fs = require('fs'),
  path = require('path'),
  glob = require('glob');

/**
 * Requires multiple modules from the given path pattern.
 *
 * The path pattern can be anything the node-glob library supports.
 *
 * ```js
 * var modules = multiRequire('some/path/**.js')
 *   .filterModule(_.isFunction)
 *   .require();
 * ```
 *
 * @param {String} pathPattern
 * @returns {MultiRequire}
 */
module.exports.multiRequire = function(pathPattern) {
  return new MultiRequire(pathPattern);
};

/**
 * @param {*} module
 * @param {String} filePath
 * @param {String} fileName
 * @param {String} fileExt
 * @constructor
 */
function RequireResult(module, filePath, fileName, fileExt) {
  this.module = module;
  this.filePath = filePath;
  this.fileName = fileName;
  this.fileExt = fileExt;
}

/**
 * @param {String} pathPattern
 * @constructor
 */
function MultiRequire(pathPattern) {
  this.pathPattern_ = pathPattern;
  this.folderModules_ = true;
  this.filters_ = [];
  this.moduleFilters_ = [];
}

/**
 * Are folders that contain an `index.js` file considered as single modules.
 *
 * Default is true.
 *
 * @param {Boolean} folderModules
 * @returns {MultiRequire}
 */
MultiRequire.prototype.folderModules = function(folderModules) {
  this.folderModules_ = folderModules;
  return this;
};

/**
 * Filters the result by requiring the modules to have a certain property.
 *
 * Example:
 *
 * ```js
 * multiRequire(path)
 *   .hasProperty('prop1')
 *   .hasProperty(['prop2', 'prop3'])
 *   .hasProperty('prop4', 'prop5', 'prop6')
 *   .require();
 * ```
 *
 * @param {String|Array.<String>} property
 * @returns {MultiRequire}
 */
MultiRequire.prototype.hasProperty = function(property) {
  if (arguments.length > 1 || _.isArray(property)) {
    var array = arguments.length > 1 ? arguments : property;
    for (var i = 0; i < array.length; ++i) {
      this.hasProperty(array[i]);
    }
  } else {
    this.filterModule(function(module) {
      return _.has(module, property);
    });
  }
  return this;
};

/**
 * Adds a filter function.
 *
 * @param {function(RequireResult):Boolean} filter
 * @param {*=} context
 * @returns {MultiRequire}
 */
MultiRequire.prototype.filter = function(filter, context) {
  this.filters_.push({ func: filter, context: context });
  return this;
};

/**
 * Adds a filter function that is passed the module instead of a `RequireResult`.
 *
 * @param {function(*):Boolean} filter
 * @param {*=} context
 * @returns {MultiRequire}
 */
MultiRequire.prototype.filterModule = function(filter, context) {
  this.moduleFilters_.push({ func: filter, context: context });
  return this;
};

/**
 * Synchronously requires all modules that pass the filters.
 *
 * @param {function(RequireResult)=} callback
 * @returns {Array.<RequireResult>}
 */
MultiRequire.prototype.require = function(callback) {
  return this.doRequire_(callback);
};

/**
 * @param {function(RequireResult)=} callback
 * @returns {Array.<RequireResult>}
 * @private
 */
MultiRequire.prototype.doRequire_ = function(callback) {
  var files,
    modules = [],
    folderModules = [],
    includeFolderModules = this.folderModules_,
    filters = this.filters_,
    moduleFilters = this.moduleFilters_;

  files = glob.sync(this.pathPattern_);

  _.each(files, function(filePath) {
    var fileExt = path.extname(filePath),
      fileName = path.basename(filePath, fileExt),
      isDir = fs.statSync(filePath).isDirectory(),
      hasIndex = isDir && fs.existsSync(path.join(filePath, 'index.js')),
      insideFolderModule;

    insideFolderModule = _.some(folderModules, function(folderModule) {
      return filePath.indexOf(folderModule.filePath) == 0;
    });

    // filePath is inside a folder module that was already required. Skip it.
    if (insideFolderModule) {
      return;
    }

    if (!isDir || (isDir && includeFolderModules && hasIndex)) {
      var module = require(filePath);
      var result = new RequireResult(module, filePath, fileName, fileExt);

      var filtersPass = _.every(filters, function(filter) {
        return filter.func.call(filter.context, result);
      });

      var moduleFiltersPass = _.every(moduleFilters, function(filter) {
        return filter.func.call(filter.context, result.module);
      });

      if (filtersPass && moduleFiltersPass) {
        modules.push(result);

        if (isDir) {
          folderModules.push(result);
        }

        if (callback) {
          callback(result);
        }
      }
    }
  });

  return modules;
};


================================================
FILE: package.json
================================================
{
  "name": "knex-db-manager",
  "version": "0.7.0",
  "description": "Collection of administrative database operations for knex supported databases",
  "main": "lib/index.js",
  "scripts": {
    "test": "node_modules/.bin/istanbul --config=.istanbul.yml cover _mocha -- --slow 10 --timeout 10000 --reporter spec --recursive tests",
    "test-no-coverage": "node_modules/.bin/mocha --slow 10 --timeout 10000 --reporter spec --recursive tests",
    "coveralls": "cat ./test-coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js",
    "check": "node_modules/.bin/npm-check; node_modules/.bin/nsp check"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/Vincit/knex-db-manager.git"
  },
  "keywords": [
    "knex",
    "sql",
    "admin",
    "management",
    "postgresql",
    "mysql"
  ],
  "author": "Mikael Lepistö",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/Vincit/knex-db-manager/issues"
  },
  "homepage": "https://github.com/Vincit/knex-db-manager#readme",
  "devDependencies": {
    "chai": "^4.1.2",
    "coveralls": "^3.0.9",
    "istanbul": "^0.4.5",
    "mocha": "^7.1.1",
    "mysql": "^2.13.0",
    "npm-check": "^5.9.2",
    "nsp": "^3.2.1",
    "pg": "^8.3.2",
    "pg-escape": "^0.2.0",
    "sqlite3": "^4.1.1"
  },
  "dependencies": {
    "bluebird": "^3.7.2",
    "glob": "^7.1.6",
    "lodash": "^4.17.15"
  },
  "peerDependencies": {
    "knex": "0.x"
  },
  "engines": {
    "node": ">=8.0.0"
  }
}


================================================
FILE: tests/database-manager.spec.js
================================================
var _ = require('lodash'),
  expect = require('chai').expect,
  dbManagerFactory = require('../lib').databaseManagerFactory,
  Bluebird = require('bluebird'),
  Knex = require('knex');

Bluebird.longStackTraces();

var connection = {
  host: 'localhost',
  database: 'dbmanger-test-database-deleteme',
  user: 'knexdbmanagerdbowneruser',
  password: 'knexdbmanagerdbowneruserpassword',
};

var pool = {
  min: 0,
  max: 10,
};

var migrations = {
  directory: __dirname + '/migrations',
  tableName: 'testmigrations',
};

var postgresConf = {
  knex: {
    client: 'postgres',
    connection: _.assign({}, connection, {
      port: 15432,
    }),
    pool: pool,
    migrations: migrations,
  },
  dbManager: {
    collate: ['fi_FI.UTF-8', 'Finnish_Finland.1252', 'en_US.utf8', 'C.UTF-8'],
    superUser: process.env.POSTGRES_SUPERUSER || 'postgres',
    superPassword:
      process.env.POSTGRES_SUPERUSER_PW || 'po"st#gr%e!sr:oo;tpa@ss%word',
  },
};

var postgresConf10 = {
  knex: {
    client: 'postgres',
    connection: _.assign({}, connection, {
      port: 25432,
    }),
    pool: pool,
    migrations: migrations,
  },
  dbManager: {
    collate: ['fi_FI.UTF-8', 'Finnish_Finland.1252', 'en_US.utf8', 'C.UTF-8'],
    superUser: process.env.POSTGRES_SUPERUSER || 'postgres',
    superPassword:
      process.env.POSTGRES_SUPERUSER_PW || 'po"st#gr%e!sr:oo;tpa@ss%word',
  },
};

var mySqlConf = {
  knex: {
    client: 'mysql',
    connection: _.assign({}, connection, {
      port: 13306,
    }),
    pool: pool,
    migrations: migrations,
  },
  dbManager: {
    collate: ['utf8_swedish_ci'],
    superUser: 'root',
    superPassword: 'mysqlrootpassword',
  },
};

var sqliteConf = {
  knex: {
    client: 'sqlite',
    connection: connection,
    pool: pool,
    migrations: migrations,
  },
  dbManager: {},
};

/**
  wnameless/oracle-xe-11g
  hostname: localhost
  port: 11521
  sid: xe
  username: system
  password: oracle
  Password for SYS & SYSTEM
 */
var oracleConf = {
  knex: {
    client: 'oracledb',
    connection: _.assign({}, connection, {
      port: 1521,
      connectString: 'localhost/XE',
      stmtCacheSize: 0,
    }),
    pool: pool,
    migrations: migrations,
  },
  dbManager: {
    collate: ['fi_FI.UTF-8', 'Finnish_Finland.1252', 'en_US.utf8', 'C.UTF-8'],
    superUser: process.env.ORACLE_SUPERUSER || 'sys',
    superPassword: process.env.ORACLE_SUPERUSER_PW || 'oracle',
  },
};

var mssqlConf = {
  knex: {
    client: 'mssql',
    connection: _.assign({}, connection, {
      port: 11433,
    }),
    pool: pool,
    migrations: migrations,
  },
  dbManager: {
    collate: ['fi_FI.UTF-8', 'Finnish_Finland.1252', 'en_US.utf8', 'C.UTF-8'],
    superUser: process.env.MSSQL_SUPERUSER || 'sa',
    superPassword: process.env.MSSQL_SUPERUSER_PW || 'mssqlpassword',
  },
};

function knexWithCustomDb(dbManager, dbName) {
  var tempKnex = _.cloneDeep(dbManager.config.knex);
  tempKnex.client.database = dbName;
  return Knex(tempKnex);
}

/**
 * All tests depends that the ones ran earlier were success.
 */
var availableDatabases = [
  {
    name: 'PostgreSQL 9.6',
    manager: dbManagerFactory(postgresConf),
  },
  {
    name: 'PostgreSQL 10',
    manager: dbManagerFactory(postgresConf10),
  },
  {
    name: 'MySQL 5.7',
    manager: dbManagerFactory(mySqlConf),
    //  },{
    //    name: 'SQLite',
    //    manager: dbManagerFactory(sqliteConf)
    //  },{
    //    name: 'Oracle XE 11g',
    //    manager: dbManagerFactory(oracleConf)
    //  },{
    //    name: 'SQL Server 2017',
    //    manager: dbManagerFactory(mySqlConf)
  },
];

var dbCopyName = 'dbmanger-test-database-copy-deleteme';

_.map(availableDatabases, function(db) {
  let dbManager = db.manager;

  describe('Testing ' + db.name, function() {
    before(function() {
      // Make sure that database does not exist
      return dbManager.createDbOwnerIfNotExist().then(function() {
        return Bluebird.all([
          dbManager.dropDb(dbManager.config.knex.connection.database),
          dbManager.dropDb(dbCopyName),
        ]);
      });
    });

    after(function() {
      return dbManager.close();
    });

    it('#knexInstance should fail to create an instance with non existing db', function() {
      var knex = dbManager.knexInstance();
      return knex
        .raw('SELECT 1')
        .then(function() {
          expect('Expected error from DB').to.fail();
        })
        .catch(function() {
          expect('All good!').to.be.string;
        });
    });

    it('#createDb should create a database', function() {
      return dbManager
        .createDb(dbManager.config.knex.connection.database)
        .then(function() {
          // connecting db should work
          var knex = dbManager.knexInstance();
          return knex.raw('SELECT 1');
        });
    });

    it('#migrateDb should update version and run migrations', function() {
      return dbManager
        .dbVersion(dbManager.config.knex.connection.database)
        .then(function(originalVersionInfo) {
          expect(originalVersionInfo).to.equal('none');
          return dbManager.migrateDb(dbManager.config.knex.connection.database);
        })
        .then(function(migrateResponse) {
          expect(migrateResponse[0]).to.equal(1);
          return dbManager.dbVersion(dbManager.config.knex.connection.database);
        })
        .then(function(versionInfo) {
          expect(versionInfo).to.equal('20150623130922');
          return dbManager.migrateDb(dbManager.config.knex.connection.database);
        })
        .then(function(migrateResponse) {
          expect(migrateResponse[0]).to.equal(2);
          return dbManager.migrateDb(dbManager.config.knex.connection.database);
        })
        .then(function(migrateResponse) {
          expect(migrateResponse[0]).to.equal(2);
          return dbManager.dbVersion(dbManager.config.knex.connection.database);
        })
        .then(function(versionInfo) {
          expect(versionInfo).to.equal('20150623130922');
          return dbManager.migrateDb(dbManager.config.knex.connection.database);
        });
    });

    it('#populateDb should populate data from given directory', function() {
      return dbManager
        .populateDb(__dirname + '/populate/*-data.js')
        .then(function() {
          var knex = dbManager.knexInstance();
          return knex
            .select()
            .from('User')
            .then(function(result) {
              expect(parseInt(result[0].id)).to.equal(1);
            });
        });
    });

    it('#populateDb should populate data from given directory with seeding old export', function() {
      return dbManager
        .populateDb(__dirname + '/populate/*-old.js')
        .then(function() {
          var knex = dbManager.knexInstance();
          return Bluebird.all([
            knex
              .select()
              .from('User')
              .then(function(result) {
                expect(parseInt(result[0].id)).to.equal(1);
              }),
            knex
              .select()
              .from('Pet')
              .then(function(result) {
                expect(parseInt(result[0].id)).to.equal(1);
              }),
          ]);
        });
    });

    it('#copyDb should copy a database', function() {
      // CopyDB not implemented on MySqlDatabaseManager yet...
      if (dbManager.config.knex.client === 'mysql') {
        return;
      }
      return dbManager
        .copyDb(dbManager.config.knex.connection.database, dbCopyName)
        .then(function() {
          var knex = knexWithCustomDb(dbManager, dbCopyName);
          return Bluebird.resolve(
            knex
              .select()
              .from('User')
              .then(function(result) {
                expect(result[0].id).to.equal('1');
              })
          ).finally(function() {
            knex.destroy();
          });
        });
    });

    it('#truncateDb should truncate a database', function() {
      return dbManager
        .truncateDb([migrations.tableName, 'Ignoreme'])
        .then(function(result) {
          var knex = dbManager.knexInstance();

          return Bluebird.all([
            knex
              .select()
              .from('User')
              .then(function(result) {
                expect(result.length).to.equal(0);
              }),
            knex
              .select()
              .from('Ignoreme')
              .then(function(result) {
                expect(result.length).to.equal(1);
              }),
            dbManager
              .dbVersion(dbManager.config.knex.connection.database)
              .then(function(ver) {
                expect(ver).to.equal('20150623130922');
              }),
            knex('User')
              .insert({
                username: 'new',
                email: 'imtadmin@fake.invalid',
              })
              .then(function() {
                return knex.select().from('User');
              })
              .then(function(result) {
                expect(parseInt(result[0].id)).to.equal(1);
              }),
          ]);
        });
    });

    it('#updateIdSequences should update primary key sequences', function() {
      // UpdateIdSequences not implemented on MySqlDatabaseManager yet...
      if (dbManager.config.knex.client === 'mysql') {
        return;
      }

      var knex = dbManager.knexInstance();

      return knex('User')
        .insert([
          { id: 5, username: 'new1', email: 'new_1@example.com' },
          { id: 6, username: 'new2', email: 'new_2@example.com' },
          { id: 7, username: 'new3', email: 'new_3@example.com' },
        ])
        .then(function() {
          return dbManager.updateIdSequences();
        })
        .then(function() {
          return knex('User').insert({
            username: 'new4',
            email: 'new_4@example.com',
          });
        })
        .then(function() {
          return knex
            .select()
            .where('username', 'new4')
            .from('User');
        })
        .then(function(users) {
          expect(users.length).to.equal(1);
          expect(users[0].id).to.equal('8');
        });
    });

    it('#updateIdSequences should work with empty table and with minimum value other than 1', function() {
      // UpdateIdSequences not implemented on MySqlDatabaseManager yet...
      if (dbManager.config.knex.client === 'mysql') {
        return;
      }

      var knex = dbManager.knexInstance();

      return knex
        .select()
        .from('IdSeqTest')
        .then(function(result) {
          expect(result.length).to.equal(0);

          // Set min value of sequence to other than 1 (100),
          // and current value to some other value so we can detect that it has changed.
          return knex.raw(
            'ALTER SEQUENCE "IdSeqTest_id_seq" START 200 RESTART WITH 200 MINVALUE 100 '
          );
        })
        .then(function() {
          // DB manager caches the sequence names and min values,
          // so the cache needs to be reset.
          dbManager._cachedIdSequences = null;
          return dbManager.updateIdSequences();
        })
        .then(function() {
          return knex('IdSeqTest').insert({
            value: 'foo',
          });
        })
        .then(function() {
          return knex.select().from('IdSeqTest');
        })
        .then(function(result) {
          expect(result.length).to.equal(1);
          expect(result[0].id).to.equal('100');
        });
    });

    it('#dropDb should drop a database', function() {
      return Bluebird.all([
        dbManager.dropDb(dbManager.config.knex.connection.database),
        dbManager.dropDb(dbCopyName),
        dbManager.dropDb(dbCopyName), // this should not fail
      ])
        .then(function() {
          // test db was dropped
          var knex = dbManager.knexInstance();
          return knex
            .raw('SELECT 1')
            .then(function() {
              expect('Expected error from DB').to.fail();
            })
            .catch(function(err) {
              expect('All good!').to.be.string;
            });
        })
        .then(function() {
          var knex = knexWithCustomDb(dbManager, dbCopyName);
          return Bluebird.resolve(
            knex
              .raw('SELECT 1')
              .then(function() {
                expect('Expected error from DB').to.fail();
              })
              .catch(function() {
                expect(!!'All good!').to.be.string;
              })
          ).finally(function() {
            knex.destroy();
          });
        });
    });

    it('should reconnect if used after .close', function() {
      return dbManager
        .close()
        .then(function() {
          return dbManager.dropDb();
        })
        .then(function() {
          return dbManager.createDb();
        })
        .then(function() {
          return dbManager.migrateDb();
        });
    });

    it('should create database with default collate', function() {
      dbManager.config.dbManager.collate = null;
      return dbManager.dropDb().then(function() {
        return dbManager.createDb();
      });
    });
  });
});

describe('Postgresql only tests', () => {
  var manager1 = null;
  var manager2 = null;

  before(() => {
    manager1 = dbManagerFactory(postgresConf);
    manager2 = dbManagerFactory(postgresConf);
  });

  before(() => manager1.dropDb('testdb1'));
  before(() => manager1.dropDb('testdb2'));

  after(() => {
    return Bluebird.all([
      manager1.dropDb('testdb1'),
      manager1.dropDb('testdb2'),
    ]).then(() => {
      return Bluebird.all([manager1.close(), manager2.close()]);
    });
  });

  it('should be able to create 2 DBs with 2 clients at the same time', () => {
    return Bluebird.all([
      manager1.createDb('testdb1'),
      manager2.createDb('testdb2'),
    ]);
  });
});


================================================
FILE: tests/dialect-aliases.spec.js
================================================
const dbManagerFactory = require('../lib').databaseManagerFactory;
const expect = require('chai').expect;

describe('Testing dialect aliases', function() {
  const aliases = [
    'pg',
    'postgres',
    'postgresql',
    'mysql',
    'maria',
    'mariadb',
    'mariasql',
    'sqlite',
    'sqlite3',
  ];

  aliases.forEach((dialect) => {
    it(`should load client with alias "${dialect}"`, () => {
      const manager = dbManagerFactory({
        knex: {
          client: dialect,
        },
      });
    });
  });

  it(`should fail loading unknown client`, () => {
    try {
      const manager = dbManagerFactory({
        knex: {
          client: 'foobar',
        },
      });
      expect('should have thrown an exception').to.equal(false);
    } catch (err) {
      expect(err.message).to.contains('is not supported');
    }
  });
});


================================================
FILE: tests/migrations/20141024070315_test_schema.js
================================================
exports.up = function(knex) {
  return knex.schema
    .createTable('User', function(table) {
      table.bigincrements('id').primary();
      table
        .string('username')
        .index()
        .unique()
        .notNullable();
      table.string('email');
    })
    .createTable('Pet', function(table) {
      table.bigincrements('id').primary();
      table.string('name');
      table
        .biginteger('userid')
        .unsigned()
        .notNullable();
      table.foreign('userid').references('User.id');
    })
    .createTable('Ignoreme', function(table) {
      table.bigincrements('id').primary();
      table.string('description');
    })
    .then(function() {
      return knex('Ignoreme').insert({ description: 'wat' });
    });
};

exports.down = function(knex) {
  return knex.schema
    .dropTable('Pet')
    .dropTable('User')
    .dropTable('Ignoreme');
};


================================================
FILE: tests/migrations/20150623130922_id_sequence_test_table.js
================================================
exports.up = function(knex) {
  return knex.schema.createTable('IdSeqTest', function(table) {
    table.bigincrements('id').primary();
    table.string('value');
  });
};

exports.down = function(knex) {
  return knex.schema.dropTable('IdSeqTest');
};


================================================
FILE: tests/populate/01-user-data.js
================================================
module.exports = function(knex) {
  return new Promise(function(resolve) {
    setTimeout(function() {
      return knex('User')
        .insert({
          username: 'dummy',
          email: 'lol@fake.invalid',
        })
        .then(resolve);
    }, 100);
  });
};


================================================
FILE: tests/populate/02-pet-data.js
================================================
module.exports = function(knex) {
  return knex('Pet').insert({
    name: 'spot',
    userid: '1',
  });
};


================================================
FILE: tests/populate/sample-data-old.js
================================================
exports.seed = function(knex) {
  return knex('User').insert({
    username: 'dummy-old',
    email: 'lol-old@fake.invalid',
  });
};


================================================
FILE: wait-databases.sh
================================================


for i in $(seq 60); do 
  docker-compose exec postgresql psql postgres://postgres:postgresrootpassword@localhost -c "SELECT 1" && break; 
  sleep 1; 
done

for i in $(seq 60); do 
  docker-compose exec postgresql10 psql postgres://postgres:postgresrootpassword@localhost -c "SELECT 1" && break; 
  sleep 1; 
done

for i in $(seq 60); do 
  docker-compose exec mysql mysql -hmysql -uroot -pmysqlrootpassword -e "SELECT 1" && break; 
  sleep 1; 
done
Download .txt
gitextract_2zlax8t9/

├── .gitignore
├── .istanbul.yml
├── .npmignore
├── .prettierrc
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── docker-compose.yml
├── lib/
│   ├── DatabaseManager.js
│   ├── MySqlDatabaseManager.js
│   ├── PostgresDatabaseManager.js
│   ├── SqliteDatabaseManager.js
│   ├── class-utils.js
│   ├── index.js
│   └── multi-require.js
├── package.json
├── tests/
│   ├── database-manager.spec.js
│   ├── dialect-aliases.spec.js
│   ├── migrations/
│   │   ├── 20141024070315_test_schema.js
│   │   └── 20150623130922_id_sequence_test_table.js
│   └── populate/
│       ├── 01-user-data.js
│       ├── 02-pet-data.js
│       └── sample-data-old.js
└── wait-databases.sh
Download .txt
SYMBOL INDEX (7 symbols across 6 files)

FILE: lib/DatabaseManager.js
  function DatabaseManager (line 15) | function DatabaseManager(config) {

FILE: lib/MySqlDatabaseManager.js
  function MySqlDatabaseManager (line 17) | function MySqlDatabaseManager() {

FILE: lib/PostgresDatabaseManager.js
  function PostgresDatabaseManager (line 11) | function PostgresDatabaseManager() {

FILE: lib/SqliteDatabaseManager.js
  function SqliteDatabaseManager (line 8) | function SqliteDatabaseManager() {

FILE: lib/multi-require.js
  function RequireResult (line 31) | function RequireResult(module, filePath, fileName, fileExt) {
  function MultiRequire (line 42) | function MultiRequire(pathPattern) {

FILE: tests/database-manager.spec.js
  function knexWithCustomDb (line 129) | function knexWithCustomDb(dbManager, dbName) {
Condensed preview — 26 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (60K chars).
[
  {
    "path": ".gitignore",
    "chars": 1015,
    "preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directo"
  },
  {
    "path": ".istanbul.yml",
    "chars": 110,
    "preview": "instrumentation:\n  excludes: ['lib/multi-require.js', 'lib/class-utils.js']\n\nreporting:\n  dir: ./test-coverage"
  },
  {
    "path": ".npmignore",
    "chars": 12,
    "preview": ".*\n*\n!lib/*\n"
  },
  {
    "path": ".prettierrc",
    "chars": 79,
    "preview": "{\n  \"singleQuote\": true,\n  \"trailingComma\": \"es5\",\n  \"arrowParens\": \"always\"\n}\n"
  },
  {
    "path": ".travis.yml",
    "chars": 288,
    "preview": "# .travis.yml\nlanguage: node_js\n\nnode_js:\n  - '10'\n  - '12'\n  - '14'\n\ndist: trusty\n\nafter_script:\n  - npm run-script cov"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 1505,
    "preview": "# Changelog\n\n### 0.7.0\n\n- Dropped node 8 and added node 14 to tests and fixed 0.6.2 changes\n\n### 0.6.2\n\n- NOTE: THIS WAS"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 64,
    "preview": "# Installing development environment\n\nTDB\n\n# Running tests\n\nTDB\n"
  },
  {
    "path": "LICENSE",
    "chars": 776,
    "preview": "Copyright (c) 2015, Sami Koskimäki\nCopyright (c) 2016, Mikael Lepistö\n\nPermission to use, copy, modify, and/or distribut"
  },
  {
    "path": "README.md",
    "chars": 6702,
    "preview": "[![Build Status](https://travis-ci.org/Vincit/knex-db-manager.svg?branch=master)](https://travis-ci.org/Vincit/knex-db-m"
  },
  {
    "path": "docker-compose.yml",
    "chars": 936,
    "preview": "version: '3'\n\nservices:\n  mysql:\n    image: mysql:5.7\n    ports:\n      - '13306:3306'\n    environment:\n      - TZ=UTC\n  "
  },
  {
    "path": "lib/DatabaseManager.js",
    "chars": 4956,
    "preview": "var _ = require('lodash'),\n  Knex = require('knex'),\n  Promise = require('bluebird'),\n  multiRequire = require('./multi-"
  },
  {
    "path": "lib/MySqlDatabaseManager.js",
    "chars": 5721,
    "preview": "var DatabaseManager = require('./DatabaseManager').default,\n  classUtils = require('./class-utils'),\n  mysql = require('"
  },
  {
    "path": "lib/PostgresDatabaseManager.js",
    "chars": 8697,
    "preview": "var _ = require('lodash'),\n  pg = require('pg'),\n  escape = require('pg-escape'),\n  Promise = require('bluebird'),\n  cla"
  },
  {
    "path": "lib/SqliteDatabaseManager.js",
    "chars": 399,
    "preview": "var DatabaseManager = require('./DatabaseManager').default,\n  classUtils = require('./class-utils');\n\n/**\n * @constructo"
  },
  {
    "path": "lib/class-utils.js",
    "chars": 1172,
    "preview": "var _ = require('lodash'),\n  util = require('util');\n\n/**\n * Makes the `Constructor` inherit `SuperConstructor`.\n *\n * C"
  },
  {
    "path": "lib/index.js",
    "chars": 1238,
    "preview": "/**\n * Configuration is dodo-objection configuration.\n *\n * @param config Dodo-objection configuration.\n * @return {Data"
  },
  {
    "path": "lib/multi-require.js",
    "chars": 4522,
    "preview": "var _ = require('lodash'),\n  fs = require('fs'),\n  path = require('path'),\n  glob = require('glob');\n\n/**\n * Requires mu"
  },
  {
    "path": "package.json",
    "chars": 1477,
    "preview": "{\n  \"name\": \"knex-db-manager\",\n  \"version\": \"0.7.0\",\n  \"description\": \"Collection of administrative database operations "
  },
  {
    "path": "tests/database-manager.spec.js",
    "chars": 13928,
    "preview": "var _ = require('lodash'),\n  expect = require('chai').expect,\n  dbManagerFactory = require('../lib').databaseManagerFact"
  },
  {
    "path": "tests/dialect-aliases.spec.js",
    "chars": 853,
    "preview": "const dbManagerFactory = require('../lib').databaseManagerFactory;\nconst expect = require('chai').expect;\n\ndescribe('Tes"
  },
  {
    "path": "tests/migrations/20141024070315_test_schema.js",
    "chars": 889,
    "preview": "exports.up = function(knex) {\n  return knex.schema\n    .createTable('User', function(table) {\n      table.bigincrements("
  },
  {
    "path": "tests/migrations/20150623130922_id_sequence_test_table.js",
    "chars": 252,
    "preview": "exports.up = function(knex) {\n  return knex.schema.createTable('IdSeqTest', function(table) {\n    table.bigincrements('i"
  },
  {
    "path": "tests/populate/01-user-data.js",
    "chars": 270,
    "preview": "module.exports = function(knex) {\n  return new Promise(function(resolve) {\n    setTimeout(function() {\n      return knex"
  },
  {
    "path": "tests/populate/02-pet-data.js",
    "chars": 108,
    "preview": "module.exports = function(knex) {\n  return knex('Pet').insert({\n    name: 'spot',\n    userid: '1',\n  });\n};\n"
  },
  {
    "path": "tests/populate/sample-data-old.js",
    "chars": 134,
    "preview": "exports.seed = function(knex) {\n  return knex('User').insert({\n    username: 'dummy-old',\n    email: 'lol-old@fake.inval"
  },
  {
    "path": "wait-databases.sh",
    "chars": 451,
    "preview": "\n\nfor i in $(seq 60); do \n  docker-compose exec postgresql psql postgres://postgres:postgresrootpassword@localhost -c \"S"
  }
]

About this extraction

This page contains the full source code of the Vincit/knex-db-manager GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 26 files (55.2 KB), approximately 14.3k tokens, and a symbol index with 7 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!