cli icon indicating copy to clipboard operation
cli copied to clipboard

SQL files in migrations

Open remiroyc opened this issue 8 years ago • 13 comments

Feature Request

I would like to develop a sql helper for migrate with sequelize-cli and run directly sql files (like a sql backup for example).

I don't know if it's a good idea so i post here for know if it was a common request.

In my mind, i would like a developer friendly syntax like : migrator.executeFile("") to integrate at your migration (on up and down functions).

Imagine, i would like to create a migration which add tables and execute a sql file (my backup) and two days later i would like to undo this. Actually it is too constraining...

If you are interested by this feature, i'll try to propose a pull request.

remiroyc avatar Sep 11 '16 19:09 remiroyc

You could do it like this:

function up() {
  return fs.readFile('up.sql').then(sql => sequelize.query(sql))
}
function down() {
  return fs.readFile('down.sql').then(sql => sequelize.query(sql))
}

I don't get 100% what benefit your migrator.executeFile() API provides

felixfbecker avatar Sep 11 '16 19:09 felixfbecker

Some possible answers: check sql syntax, catch errors, add options for create a query for each line or execute all in one transaction, etc.

remiroyc avatar Sep 11 '16 19:09 remiroyc

check sql syntax

thats the job of the DB, I don't think this should belong inside the migration CLI

catch errors

in my example a failed query would result in a failed migration, as you would expect

add options for create a query for each line

I don't get this one, queries are seperated by semicolons?

execute all in one transaction

sequelize.query('BEGIN;' + sql + 'COMMIT;')

should do it. But that is dangerous because there are DBMS that don't support transactions for schema commands, like MySQL.

felixfbecker avatar Sep 11 '16 19:09 felixfbecker

I agree with felix here, its just a matter of getting SQL, sequelize.query can execute it

sushantdhiman avatar Sep 21 '17 17:09 sushantdhiman

@felixfbecker With your code I'm getting: ERROR: Callback must be a function UP: Can not migrate! Any idea?

alatras avatar Feb 18 '19 11:02 alatras

That was using a Promise version of fs. See https://nodejs.org/dist/latest-v10.x/docs/api/fs.html#fs_fs_promises_api or https://www.npmjs.com/package/mz

felixfbecker avatar Feb 18 '19 11:02 felixfbecker

@felixfbecker Thanks!

alatras avatar Feb 18 '19 13:02 alatras

how did you fix this ? am getting the same error call back must be a function

Graig123git avatar Nov 21 '19 16:11 Graig123git

@Graig123git use const fs = require("fs").promises;

AdamBilisic avatar Jan 31 '20 10:01 AdamBilisic

A few other notes here. You will need to use sql.toString() to convert the buffer returned from readFile to a string.

function up() {
  return fs.readFile('up.sql').then(sql => sequelize.query(sql.toString()))
}
function down() {
  return fs.readFile('down.sql').then(sql => sequelize.query(sql.toString()))
}

Also, if your sql file has multiple statements in it (for example a dump file) you will need to split the string up by semicolons.

function up() {
  return fs.readFile('up.sql').then(sql => 
  {
    var promises = []
    var statements = sql.toString().split(';')
    for (var statement of statements)
        if (statement.trim() != '') promises.push(sequelize.query(statement))
    return Promise.all(promises)
   })
}
function down() {
  return fs.readFile('down.sql').then(sql =>
  {
    var promises = []
    var statements = sql.toString().split(';')
    for (var statement of statements)
        if (statement.trim() != '') promises.push(sequelize.query(statement))
    return Promise.all(promises)
   })
}

Seth10001 avatar Feb 07 '20 18:02 Seth10001

But if I have the semicolons inside my data values, then I get an error after trying to split the dump file by semicolon symbol. Any idea how to distinguish semicolons inside values from statement separators?

udovichenko avatar Sep 07 '22 23:09 udovichenko

umzug is able to execute raw sql files, I think we should support it: https://github.com/sequelize/umzug/tree/main/examples/3.raw-sql. Maybe with a different naming scheme: migrations/2020.11.24T18.00.40.users/up.sql & migrations/2020.11.24T18.00.40.users/down.sql?

ephys avatar Sep 17 '22 11:09 ephys

@udovichenko

But if I have the semicolons inside my data values, then I get an error after trying to split the dump file by semicolon symbol. Any idea how to distinguish semicolons inside values from statement separators?

Ahh good point. In that case you want to ignore any semicolons inside single/double qoutes. You can do so with the following regex:

https://regexr.com/6u7h4

Screen Shot 2022-09-17 at 8 52 50 AM

Modified answer:

function up() {
  return fs.readFile('up.sql').then(sql => 
  {
    var promises = []
    var statements = sql.toString().split(/(?!\B"[^"]*)(?!\B'[^']*);(?![^"]*"\B)(?![^']*'\B)/)
    for (var statement of statements)
        if (statement.trim() != '') promises.push(sequelize.query(statement))
    return Promise.all(promises)
   })
}
function down() {
  return fs.readFile('down.sql').then(sql =>
  {
    var promises = []
    var statements = sql.toString().split(/(?!\B"[^"]*)(?!\B'[^']*);(?![^"]*"\B)(?![^']*'\B)/)
    for (var statement of statements)
        if (statement.trim() != '') promises.push(sequelize.query(statement))
    return Promise.all(promises)
   })
}

Based my regex on this SO post: https://stackoverflow.com/a/21106122

Seth10001 avatar Sep 17 '22 15:09 Seth10001