Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Jeff Kunkle
committed
Jul 2, 2012
0 parents
commit 9a757de
Showing
13 changed files
with
406 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.idea | ||
.DS_Store | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
module.exports = Column; | ||
|
||
function Column() {}; | ||
|
||
Column.iface = [ | ||
'getName', | ||
'isNullable', | ||
'getDataType', | ||
'getMaxLength' | ||
]; | ||
|
||
Column.iface.forEach(function(method) { | ||
Column.prototype[method] = function() { | ||
throw new Error(method + ' not yet implemented'); | ||
}; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/** | ||
* This file serves as a reference for the strings that each | ||
* driver implementation should return for valid data types. | ||
*/ | ||
module.exports = { | ||
integer: 'INTEGER', | ||
boolean: 'BOOLEAN', | ||
date: 'DATE', | ||
text: 'TEXT', | ||
varchar: 'VARCHAR', | ||
float: 'FLOAT', | ||
double: 'DOUBLE', | ||
time: 'TIME', | ||
timestamp: 'TIMESTAMP' | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
var path = require('path'); | ||
|
||
module.exports = function (driverName, options, callback) { | ||
if (arguments.length < 3) { | ||
callback = options; | ||
options = {}; | ||
} | ||
|
||
try { | ||
var driverPath = path.join(__dirname, driverName, 'driver'); | ||
var driver = require(driverPath); | ||
driver.connect(options, callback); | ||
} catch (e) { | ||
callback(new Error('Unsupported driver: ' + driverName)); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
module.exports = Column; | ||
|
||
var util = require('util'); | ||
var BaseColumn = require('../column'); | ||
var dataType = require('../data-type'); | ||
|
||
function Column(props) { | ||
this.meta = props; | ||
} | ||
util.inherits(Column, BaseColumn); | ||
|
||
Column.prototype.getName = function () { | ||
return this.meta.column_name; | ||
}; | ||
|
||
Column.prototype.isNullable = function () { | ||
return this.meta.is_nullable === 'YES'; | ||
}; | ||
|
||
Column.prototype.getMaxLength = function () { | ||
return this.meta.character_maximum_length; | ||
}; | ||
|
||
Column.prototype.getDataType = function() { | ||
switch (this.meta.data_type) { | ||
case 'integer': | ||
case 'int': | ||
case 'int4': | ||
return dataType.integer; | ||
case 'boolean': | ||
case 'bool': | ||
return dataType.boolean; | ||
case 'text': | ||
return dataType.text; | ||
case 'varchar': | ||
case 'character varying': | ||
return dataType.varchar; | ||
case 'real': | ||
case 'float4': | ||
return dataType.float; | ||
case 'double precision': | ||
case 'float8': | ||
return dataType.double; | ||
case 'time': | ||
case 'timetz': | ||
return dataType.time; | ||
case 'timestamp': | ||
case 'timestamptz': | ||
return dataType.timestamp; | ||
default: | ||
return this.meta.data_type.toUpperCase(); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
var util = require('util'); | ||
var pg = require('pg'); | ||
var Table = require('./table'); | ||
var Column = require('./column'); | ||
|
||
exports.connect = function (options, callback) { | ||
var client = new pg.Client(options); | ||
client.connect(onConnect); | ||
|
||
function onConnect(err) { | ||
callback(err, new Driver(client)); | ||
} | ||
}; | ||
|
||
function Driver(client) { | ||
this.client = client; | ||
} | ||
|
||
Driver.prototype.getVersion = function (callback) { | ||
this.client.query('select version()', onResult); | ||
|
||
function onResult(err, result) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
|
||
callback(null, result.rows[0].version); | ||
} | ||
}; | ||
|
||
Driver.prototype.getTables = function (callback) { | ||
var handler = handleResults.bind(this, Table, callback); | ||
this.client.query("SELECT * FROM information_schema.tables WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema');", handler); | ||
}; | ||
|
||
Driver.prototype.getColumns = function (tableName, callback) { | ||
var handler = handleResults.bind(this, Column, callback); | ||
this.client.query("SELECT * FROM information_schema.columns WHERE table_name = $1", [tableName], handler); | ||
}; | ||
|
||
Driver.prototype.close = function(callback) { | ||
this.client.end(); | ||
callback(); | ||
}; | ||
|
||
function handleResults(obj, callback, err, result) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
|
||
var objects = result.rows.map(function (row) { | ||
return new obj(row); | ||
}); | ||
|
||
callback(null, objects); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
module.exports = Table; | ||
|
||
var util = require('util'); | ||
var BaseTable = require('../table'); | ||
|
||
function Table(props) { | ||
this.meta = props; | ||
} | ||
util.inherits(Table, BaseTable); | ||
|
||
Table.prototype.getName = function() { | ||
return this.meta.table_name; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
module.exports = Table; | ||
|
||
function Table() {}; | ||
|
||
Table.iface = ['getName']; | ||
|
||
Table.iface.forEach(function(method) { | ||
Table.prototype[method] = function() { | ||
throw new Error(method + ' not yet implemented'); | ||
}; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
{ | ||
"author": "Jeff Kunkle <jkunkle@nearinfinity.com>", | ||
"name": "db-meta", | ||
"keywords": [ | ||
"database", | ||
"db", | ||
"metadata", | ||
"postgres" | ||
], | ||
"description": "Relational database metadata extraction library", | ||
"version": "0.1.0", | ||
"license": "MIT", | ||
"main": "./lib/db-meta.js", | ||
"bugs": { | ||
"url": "https://github.com/nearinfinity/node-db-meta/issues" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/nearinfinity/node-db-meta.git" | ||
}, | ||
"dependencies": {}, | ||
"devDependencies": { | ||
"chai": "~1.1.0", | ||
"mocha": "~1.2.1" | ||
}, | ||
"optionalDependencies": {}, | ||
"engines": { | ||
"node": ">=0.6" | ||
}, | ||
"scripts": { | ||
"test": "mocha" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
var expect = require('chai').expect; | ||
var dbmeta = require('../lib/db-meta'); | ||
|
||
describe('db-meta', function() { | ||
it('should return an error for an unknown driver', function(done) { | ||
dbmeta('unknown', {}, expectError); | ||
|
||
function expectError(err) { | ||
expect(err).to.exist; | ||
done(); | ||
} | ||
}); | ||
|
||
it('should not return an error for a known driver', function(done) { | ||
dbmeta('pg', { database: 'db-meta-test' }, done); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
var expect = require('chai').expect; | ||
var Column = require('../../lib/pg/column'); | ||
var iface = require('../../lib/column').iface; | ||
|
||
describe('pg column', function () { | ||
it('should implement all the methods defined in the base column interface', function (done) { | ||
var c = new Column({ column_name: 'col', data_type: 'integer' }); | ||
iface.forEach(function (method) { | ||
c[method].call(c); | ||
}); | ||
done(); | ||
}); | ||
|
||
it('should create an internal meta property for constructor argument', function (done) { | ||
var t = new Column({ column_name: 'col' }); | ||
expect(t.meta).not.to.be.null; | ||
expect(t.meta.column_name).to.equal('col'); | ||
done(); | ||
}) | ||
|
||
it('should implement the getName method', function (done) { | ||
var c = new Column({ column_name: 'col' }); | ||
expect(c.getName()).to.equal('col'); | ||
done(); | ||
}); | ||
|
||
it('should implement the isNullable method', function (done) { | ||
var c = new Column({ column_name: 'col', is_nullable: 'YES' }); | ||
expect(c.isNullable()).to.be.true; | ||
|
||
c = new Column({ column_name: 'col', is_nullable: 'NO' }); | ||
expect(c.isNullable()).to.be.false; | ||
|
||
done(); | ||
}); | ||
|
||
it('should implement the getMaxLength method', function (done) { | ||
var c = new Column({ column_name: 'col', character_maximum_length: 255 }); | ||
expect(c.getMaxLength()).to.equal(255); | ||
done(); | ||
}); | ||
|
||
it('should implement the getDataType method', function(done) { | ||
expectDataType('INTEGER', ['integer', 'int', 'int4']); | ||
expectDataType('BOOLEAN', ['boolean', 'bool']); | ||
expectDataType('TEXT', ['text']); | ||
expectDataType('VARCHAR', ['varchar', 'character varying']); | ||
expectDataType('FLOAT', ['real', 'float4']); | ||
expectDataType('DOUBLE', ['double precision', 'float8']); | ||
expectDataType('TIME', ['time', 'timetz']); | ||
expectDataType('TIMESTAMP', ['timestamp', 'timestamptz']); | ||
expectDataType('FOO', ['foo']); | ||
done(); | ||
}); | ||
}); | ||
|
||
function expectDataType(outputType, metaTypes) { | ||
metaTypes.forEach(function (type) { | ||
var c = new Column({ column_name: 'col', data_type: type }); | ||
expect(c.getDataType()).to.equal(outputType); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
var expect = require('chai').expect; | ||
var pg = require('../../lib/pg/driver'); | ||
|
||
var driver = null; | ||
|
||
describe('pg driver', function() { | ||
before(function(done) { | ||
pg.connect({ database: 'db-meta-test' }, onConnect); | ||
|
||
function onConnect(err, dbDriver) { | ||
driver = dbDriver; | ||
driver.client.query('CREATE TABLE person (id INTEGER PRIMARY KEY, name VARCHAR(255) NOT NULL, email VARCHAR(100), age INTEGER);', done); | ||
} | ||
}); | ||
|
||
after(function(done) { | ||
driver.client.query('DROP TABLE person', driver.close.bind(driver, done)); | ||
}); | ||
|
||
it('should return the database version', function(done) { | ||
driver.getVersion(onResult); | ||
|
||
function onResult(err, version) { | ||
expect(err).to.be.null; | ||
expect(version).to.be.present; | ||
done(); | ||
} | ||
}); | ||
|
||
it('should return all database tables', function(done) { | ||
driver.getTables(onResult); | ||
|
||
function onResult(err, tables) { | ||
expect(err).to.be.null; | ||
expect(tables).not.to.be.empty; | ||
expect(tables.length).to.equal(1); | ||
expect(tables[0].getName()).to.equal('person'); | ||
expect(tables[0].meta).not.to.be.empty; | ||
done(); | ||
} | ||
}); | ||
|
||
it('should return all columns for a given table', function(done) { | ||
driver.getColumns('person', onResult); | ||
|
||
function onResult(err, columns) { | ||
expect(err).to.be.null; | ||
expect(columns).not.to.be.empty; | ||
expect(columns.length).to.equal(4); | ||
|
||
var idColumn = getColumnByName(columns, 'id'); | ||
expect(idColumn).not.to.be.null; | ||
expect(idColumn.meta).not.to.be.empty; | ||
expect(idColumn.isNullable()).to.be.false; | ||
expect(idColumn.getDataType()).to.equal('INTEGER'); | ||
expect(idColumn.getMaxLength()).to.be.null; | ||
|
||
var nameColumn = getColumnByName(columns, 'name'); | ||
expect(nameColumn).not.to.be.null; | ||
expect(nameColumn.meta).not.to.be.empty; | ||
expect(nameColumn.isNullable()).to.be.false; | ||
expect(nameColumn.getMaxLength()).to.equal(255); | ||
expect(nameColumn.getDataType()).to.equal('VARCHAR'); | ||
|
||
var emailColumn = getColumnByName(columns, 'email'); | ||
expect(emailColumn).not.to.be.null; | ||
expect(emailColumn.meta).not.to.be.empty; | ||
expect(emailColumn.isNullable()).to.be.true; | ||
expect(emailColumn.getMaxLength()).to.equal(100); | ||
expect(emailColumn.getDataType()).to.equal('VARCHAR'); | ||
|
||
done(); | ||
} | ||
}); | ||
}); | ||
|
||
function getColumnByName(columns, name) { | ||
for (var i = 0; i < columns.length; i++) { | ||
if (columns[i].getName() === name) { | ||
return columns[i]; | ||
} | ||
} | ||
return null; | ||
} |
Oops, something went wrong.