Skip to content

Commit

Permalink
load stored procedures
Browse files Browse the repository at this point in the history
  • Loading branch information
tjwebb committed Oct 3, 2015
1 parent 174b93b commit 74fc079
Show file tree
Hide file tree
Showing 9 changed files with 209 additions and 66 deletions.
8 changes: 0 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,6 @@ module.exports.connections = {
pool: {
min: 2,
max: 20
},

features: {
/**
* Optionally use PostGIS-powered spatial interface. Set to 'false' to
* disable/ignore.
*/
spatial: true
}
}
}
Expand Down
52 changes: 37 additions & 15 deletions lib/adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,8 @@ const Adapter = {
},

pool: {
min: 10,
max: 30
},

extensions: {
storedprocedures: true,
transactional: true,
spatial: true
min: 4,
max: 16
}
},

Expand Down Expand Up @@ -94,13 +88,12 @@ const Adapter = {
st: KnexPostgis(knex)
}

return Adapter.getVersion(cxn)
.then(version => {
cxn.version = Util.validateVersion(version)
return Util.initializeConnection(cxn)
.then(() => {
Adapter.connections.set(connection.identity, cxn)
cb()
})
.catch(AdapterError.wrap(cb))
.catch(cb)
},

/**
Expand Down Expand Up @@ -278,7 +271,7 @@ const Adapter = {
_.isFunction(cb) && cb(null, result)
return result
})
.catch(AdapterError.wrap(cb))
.catch(AdapterError.wrap(cb, null, data))
},

/**
Expand All @@ -293,18 +286,22 @@ const Adapter = {
*/
update (connectionName, tableName, options, data, cb) {
let cxn = Adapter.connections.get(connectionName)
let schema = cxn.collections[tableName]
let wlsql = new WaterlineSequel(cxn.schema, Adapter.wlSqlOptions)
let spatialColumns = SpatialUtil.getSpatialColumns(schema.definition)

let updateData = _.omit(data, spatialColumns)

return new Promise((resolve, reject) => {
resolve(wlsql.update(tableName, options, data))
resolve(wlsql.update(tableName, options, updateData))
})
.then(({ query, values }) => {
return Adapter._query(cxn, query, values)
})
.then(({ rows }) => {
cb && cb(null, rows)
})
.catch(AdapterError.wrap(cb))
.catch(AdapterError.wrap(cb, null, data))
},

/**
Expand Down Expand Up @@ -439,6 +436,31 @@ const Adapter = {
})
},

/**
* Invoke a database function, aka "stored procedure"
*
* @param connectionName
* @param tableName
* @param procedureName the name of the stored procedure to invoke
* @param args An array of arguments to pass to the stored procedure
*/
procedure (connectionName, procedureName, args = [ ], cb = args) {
let cxn = Adapter.connections.get(connectionName)
let procedure = cxn.storedProcedures[procedureName.toLowerCase()]

if (!procedure) {
let error = new Error(`No stored procedure found with the name ${procedureName}`)
return (_.isFunction(cb) ? cb(error) : Promise.reject(error))
}

return procedure.invoke(args)
.then(result => {
_.isFunction(cb) && cb(null, result)
return result
})
.catch(AdapterError.wrap(cb))
},

/**
* Stream query results
*
Expand Down
4 changes: 3 additions & 1 deletion lib/error.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@ const PostgresErrorMapping = {
}

const AdapterError = {
wrap (cb, txn) {
wrap (cb, txn, payload) {
return function (pgError) {
let errorWrapper = PostgresErrorMapping[pgError.code]
let error = pgError

console.error('error payload', payload)

if (_.isFunction(errorWrapper)) {
error = errorWrapper(pgError)
}
Expand Down
61 changes: 61 additions & 0 deletions lib/procedures.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import _ from 'lodash'
import SQL from './sql'

export const Procedures = {

/**
* Return a collection of all stored procedures accessible to the current
* database connection
*/
describeAll (cxn) {
let sp = cxn.knex.raw(SQL.storedProcedures)

return sp
.then(({ rows }) => {
let procedures = _.map(rows, row => {
return Procedures.buildStoredProcedure(row, cxn)
})

procedures.push(Procedures.buildStoredProcedure({ name: 'version' }, cxn))

return _.isEmpty(procedures) ? { } : _.indexBy(procedures, 'name')
})
},

/**
* Build a function that invokes the SP with the required arguments
*/
buildStoredProcedure ({ schema, name, returntype, signature }, cxn) {
let argTemplate = Procedures.buildArgumentTemplate(signature)
let fullName = (!schema || (schema == 'public')) ? name : `${schema}.${name}`

return {
name: fullName,
signature: Procedures.parseSignature(signature),
invoke (args) {
if (!schema) {
return cxn.knex.raw(`select ${name}(${argTemplate})`, args)
}
else {
return cxn.knex.raw(`select ${schema}.${name}(${argTemplate})`, args)
}
}
}
},

buildArgumentTemplate (signature) {
if (!signature) return ''

let args = signature.split(', ')
return args.map(arg => '?').join(',')
},

parseSignature (signature = '') {
let parameters = signature.split(', ')
return _.map(parameters, param => {
return param.split(' ')[0]
})
}
}

export default Procedures
25 changes: 15 additions & 10 deletions lib/spatial.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import _ from 'lodash'

const SpatialUtil = {

spatialTypeRegex: /^(\w+)\((\w+), (\d+)\)$/,

/**
* Get the version of the installed postgis extension
*/
getPostgisVersion (cxn) {
return cxn.knex
.raw('select postgis_lib_version()')
.then(({ rows: [{ version }] }) => {
console.log('postgis version', version)
return version.split('.')
})
},
Expand All @@ -31,6 +32,7 @@ const SpatialUtil = {
return parseFloat(`${major}.${minor}`)
},

/*
addGeometryColumns (cxn, tableName, tableDefinition) {
let geometryColumns = _.chain(tableDefinition)
.pick(SpatialUtil.isSpatialColumn)
Expand All @@ -41,11 +43,11 @@ const SpatialUtil = {
return Promise.all(geometryColumns)
},
*/

/**
* Add a geometry column to a table
* http://postgis.net/docs/AddGeometryColumn.html
*/
addGeometryColumn (cxn, tableName, attributeName, definition) {
let columnName = attributeName || definition.columnName
let srid = definition.srid || 4326
Expand All @@ -54,6 +56,7 @@ const SpatialUtil = {
select AddGeometryColumn('${tableName}', '${columnName}', ${srid}, 'GEOMETRY', 2)
`)
},
*/

/**
* Convert geojson into postgis 'geometry' type. Re-project geometry if necessary.
Expand Down Expand Up @@ -82,11 +85,6 @@ const SpatialUtil = {
)
},

getSpatialType (attribute) {
let config = attribute.geometry
return `geometry(${config.wktType}, ${config.nativeSrid})`
},

/**
* Get "declared srid". This is the SRID that we're expecting of geometries
* that we're inserting into the database.
Expand All @@ -99,9 +97,13 @@ const SpatialUtil = {
/**
* Get "native srid". This is the SRID that we're using to store geometries
* in the database.
*
* examples:
* geometry(Point, 4326)
*/
getNativeSrid (definition) {
return definition.geometry.nativeSrid
let [ $, dbType, geoType, srid ] = SpatialUtil.spatialTypeRegex.exec(definition.dbType)
return srid || 0
},

buildSpatialSelect (tableDefinition, cxn) {
Expand All @@ -118,8 +120,11 @@ const SpatialUtil = {
return !!_.findWhere(tableDefinition, SpatialUtil.isSpatialColumn)
},

isSpatialColumn (attr) {
return attr.type == 'geometry' || _.isObject(attr.geometry)
isSpatialColumn (definition) {
if (!definition.dbType) return false

let [ $, dbType, geoType, srid ] = SpatialUtil.spatialTypeRegex.exec(definition.dbType)
return _.contains([ 'geometry', 'geography' ], dbType)
}
}

Expand Down
2 changes: 1 addition & 1 deletion lib/sql.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const SQL = {
where
pg_catalog.pg_function_is_visible(p.oid)
and n.nspname not in ('pg_catalog', 'information_schema')
and p.proname not like '\_%'
and p.proname not like '\\_%'
order by schema, name
`
}
Expand Down
Loading

0 comments on commit 74fc079

Please sign in to comment.