Skip to content

New feature: support sql files #189

@luukvhoudt

Description

@luukvhoudt

Because 90% of all use cases involve SQL, or at least that's my suspicion, when using a migration tool like this package provides. That's why I think it might be good addition to include an utility function what adds support for doing something based on a list of (SQL) files their content.

Currently I'm using a function in one of my projects just like that and I thought why not share it?

...

All what is needed is a file structure like this (note as of v1.7.0, you can't have this structure in the same directory as where your migrations are stored, it should be on a higher level):

.
├── up
│   ├── 00-tables.sql
│   └── 123-triggers.sql
└── down
    ├── 00-tables.sql
    └── 123-triggers.sql

Then the next thing to do is to setup a single migration:

const {upFromFiles, downFromFiles} = require("migrate"); // Not available yet?
const database = require("./database");

const upHandler = async (fileContents) => {
  // Do something with the contents of the file.
  // This example is querying the database
  const pool = database.getPool(); // Setup a database connection
  await pool.query(fileContents); // Send a query to the database
};

// This function is needed if you maintain the same order in the down SQL files as you do in the up files.
// Example:
// - up file: create table posts (...); create table comments (..., post_id int references posts);
// - down file: drop table posts; drop table comments;
// This function reverses the order of those statements: drop table comments; drop table posts;
const downHandler = async (fileContents) => {
  const seperator = ";";
  const reversedContents = fileContents
    .split(seperator)
    .reverse()
    .join(seperator);
  await upHandler(reversedContents);
};

module.exports.up = async () => {
  await upFromFiles("./up", upHandler);
};

module.exports.down = async () => {
  await downFromFiles("./down", downHandler);
};

The functions upFromFiles and downFromFiles could be defined as following:

const fs = require("fs");
const path = require("path");
const chalk = require("chalk");

const log = (key: string, msg: string, last: boolean = false) => {
  const prefix = "  " + (last ? "└──" : "├──");
  console.log(`${prefix} ${chalk.grey(key)} : ${chalk.cyan(msg)}`);
};

const fromFiles = async (key, fileNames, handler) => {
  for (const index in fileNames) {
    const fileName = fileNames[index];
    const filePath = path.join(dir, path.sep, fileName);
    const contents = fs.readFileSync(filePath, "utf8").toString();
    log(key, fileName, Number(index) === fileNames.length - 1);
    await handler(contents);
  }
};

module.exports.upFromFiles = async (dir, handler) => {
  const fileNames = fs.readdirSync(dir);
  await fromFiles("up", fileNames, handler);
};

module.exports.downFromFiles = async (dir, handler) => {
  const fileNames = fs.readdirSync(dir).reverse();
  await fromFiles("down", fileNames, handler);
}

Maybe if the package author agrees this code can be integrated with this package. If not and people do prefer to have this around for the taking. Then I might consider to publish into a separate package.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions