databridge
Version:
Data bridging software to modularize, automate and schedule the transfer of data between different sources and destinations.
168 lines (163 loc) • 5.34 kB
JavaScript
module.exports = (opt, columns, moduleCallback) => {
const mssql = require('mssql')
const readline = require('readline')
const creds = require(opt.cfg.dirs.creds + opt.destination)
const async = require('async')
let log = opt.log
let opfile = opt.opfile
let db = opt.source
//use default dbo unless schema in filenamenode_modules
let table = opt.table.indexOf('.') > -1 ? opt.table : 'dbo.' + opt.table
let schema = table.split('.')[0]
function sqlTable() {
let cols = []
let ndxs = []
columns.forEach((c) => {
cols.push(` ${c.name} ${c.type} NULL `)
if (c.index) ndxs.push(` INDEX ${c.name} ( ${c.name} ) `)
})
let sql = 'CREATE TABLE ' + table + ' ( ' + cols.join(', ')
if (ndxs.length) sql += ', ' + ndxs.join(',')
sql += ' )'
return sql
}
async.waterfall([
//connect
(cb) => {
mssql.connect(creds, (err) => {
if (err) return cb('Connect error: ' + err)
cb(null)
})
},
//create database if necessary
(cb) => {
let s = `if not exists(select * from sys.databases where name = '${db}') create database ${db}`
new mssql
.Request()
.query(s, (err) => {
if (err) return cb(err)
cb(null)
})
},
//create schema if necessary
(cb) => {
let s = `USE ${db} IF (SCHEMA_ID('${schema}') IS NULL ) BEGIN EXEC ('CREATE SCHEMA [${schema}] AUTHORIZATION [dbo]') END`
new mssql
.Request()
.query(s, (e) => {
if (e) return cb(e)
cb(null)
})
},
//drop table if exists
(cb) => {
//don't drop table if update or truncate
if (opt.update || opt.truncate) return cb(null)
let sql = `USE ${db} IF OBJECT_ID('${table}') IS NOT NULL DROP TABLE ${table}`
new mssql.Request().query(sql, (e) => {
if (e) return cb('Drop table error: ' + e)
cb(null)
})
},
//create table
(cb) => {
//don't drop table if update or truncate
if (opt.update || opt.truncate) return cb(null)
new mssql.Request().query(`USE ${db} ` + sqlTable(), (e) => {
if (e) return cb('Create table error: ' + e)
cb(null)
})
},
//truncate table if specified
(cb) => {
if (!opt.truncate) return cb(null)
new mssql.Request().query('TRUNCATE TABLE ' + table, (e) => {
if (e instanceof Error && e.toString().indexOf('because it does not exist') !== -1) {
//if table doesn't exist, make it
console.log('creating missing table')
new mssql.Request().query(`USE ${db} ` + sqlTable(), (e) => {
if (e) return cb('Create table that didnt exist to truncate error: ' + e)
cb(null)
})
} else {
if (e) return cb(e)
cb(null)
}
})
},
//create insert statement
(cb) => {
let sql = `USE ${db} INSERT INTO ${table} `
let cs = []
let first = true
columns.forEach((c) => {
cs.push(c.name)
})
sql += ` ( ${cs.join(', ')} ) VALUES `
let lineReader = readline.createInterface({
input: opfile.readStream
})
lineReader.on('error', (err) => {
return cb('Readline error: ' + err)
})
let insertLines = []
lineReader.on('line', (line) => {
if (first) {
first = false
} else {
let l = ' (\'' + line.replace(/\'/g, '').split('\t').join('\', \'') + '\') '
l = l.replace(/\'\'/g, 'NULL').replace(/(\'[0-9]+\.[0-9]+\'|\'[0-9]\')/g, '$1')
insertLines.push(l)
}
})
lineReader.on('close', () => {
cb(null, sql, insertLines)
})
},
//insert lines
(sql, lines, cb) => {
let batchCount = Math.ceil(lines.length / 500)
let arr = []
for (let i = 0; i < batchCount; i++) {
arr.push(i + 1)
}
async.map(arr, (i, callback) => {
let stmt = sql + lines.slice(i * 500 - 500, i * 500).join(', ')
new mssql.Request().query(stmt, (err) => {
if (err) return callback('Insert values error: ' + err)
callback(null)
})
}, (err) => {
if (err) return cb(err)
cb(null)
})
},
//check number of inserted rows
(cb) => {
new mssql.Request().query(`USE ${db} SELECT count(*) as rows FROM ${table}`, (err, recordset) => {
if (err) return cb('Check row number error: ' + err)
cb(null, recordset[0].rows)
})
},
(rows, cb) => {
let sql = `USE ${db} SELECT COLUMN_NAME col FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '${table.split('.')[1]}' AND TABLE_SCHEMA = '${schema}'`
new mssql.Request()
.query(sql, (err, recordset) => {
if (err) return cb(err)
let columns = []
recordset.forEach((row) => {
columns.push(row.col)
})
cb(null, rows, columns)
})
}
], (err, rows, columns) => {
try {
mssql.close()
} catch (e) {
log.error(e)
}
if (err) return moduleCallback(err)
moduleCallback(null, rows, columns)
})
}