diff --git a/.gitignore b/.gitignore index 0e697bc..481d8e5 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ .nyc_output/**/* coverage/**/* tests/unit/runtime/ledger/**/* -examples/mssql.config.js \ No newline at end of file +examples/**/config.js \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index ee0651f..62c98c2 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,7 +7,16 @@ { "type": "pwa-node", "request": "launch", - "name": "Launch Program", + "name": "Mssql", + "skipFiles": [ + "/**" + ], + "program": "${workspaceFolder}/examples/mssql/index.js" + }, + { + "type": "pwa-node", + "request": "launch", + "name": "Postgres", "skipFiles": [ "/**" ], diff --git a/examples/example.mssql.config.js b/examples/mssql/example.config.js similarity index 91% rename from examples/example.mssql.config.js rename to examples/mssql/example.config.js index f120b00..d650b2e 100644 --- a/examples/example.mssql.config.js +++ b/examples/mssql/example.config.js @@ -3,7 +3,7 @@ module.exports = { server: 'localhost', user: 'sa', password: '***', - database: 'some-test-example-mssql', + database: 'some-test-example', pool: { max: 10, min: 0, diff --git a/examples/postgresql/example.config.js b/examples/postgresql/example.config.js new file mode 100644 index 0000000..0224f76 --- /dev/null +++ b/examples/postgresql/example.config.js @@ -0,0 +1,12 @@ +module.exports = { + connect: { + server: 'localhost', + user: 'sa', + password: '***', + database: 'some-test-example' + }, + link: { + gluePrefix: '.', + schema: ['a', 'b'] // schemas allowed + } +}; \ No newline at end of file diff --git a/examples/postgresql/example.sql b/examples/postgresql/example.sql new file mode 100644 index 0000000..d03af38 --- /dev/null +++ b/examples/postgresql/example.sql @@ -0,0 +1,32 @@ +CREATE TYPE LessonType AS TABLE (LessonId INT, LessonName VARCHAR(100)) + +CREATE TABLE Lesson ( + Id INT PRIMARY KEY, + LName VARCHAR(50) +) +------------------------- +CREATE PROCEDURE Usp_InsertLesson + @ParLessonType LessonType READONLY, + @ParLessonType2 LessonType READONLY +AS + INSERT INTO Lesson + SELECT * FROM @ParLessonType; + +CREATE PROCEDURE Usp_InsertLesson2 + @ParLessonType LessonType READONLY, + @ParLessonType2 LessonType READONLY +AS + INSERT INTO Lesson + SELECT * FROM @ParLessonType +------------------------- +DECLARE @VarLessonType AS LessonType + +INSERT INTO @VarLessonType VALUES ( 1, 'Math') +INSERT INTO @VarLessonType VALUES ( 2, 'Science') +INSERT INTO @VarLessonType VALUES ( 3, 'Geometry') + + +EXECUTE Usp_InsertLesson @VarLessonType +------------------------- +------------------------- +------------------------- \ No newline at end of file diff --git a/examples/postgresql/index.js b/examples/postgresql/index.js new file mode 100644 index 0000000..ae181ce --- /dev/null +++ b/examples/postgresql/index.js @@ -0,0 +1,10 @@ +(async() => { + const methods = await require('../../lib/mssql/index.js')( + require('../mssql.config.js') + ); + try { + await methods['dbo.Usp_InsertLesson']({ParLessonType: [{LessonId: 100, LessonName: 'example lesson'}]}); + } catch (e) { + console.error(e); + } +})(); \ No newline at end of file diff --git a/lib/postgresql/index.js b/lib/postgresql/index.js new file mode 100644 index 0000000..2eceff7 --- /dev/null +++ b/lib/postgresql/index.js @@ -0,0 +1,68 @@ +const driver = require('postgresql'); +const {SError, WError} = require('error'); +const SqlSe = (() => (class Sql extends SError {}))(); +const SqlWe = (() => (class Sql extends WError {}))(); + + +const predefinedSql = require('./sqls/index.js'); + +const conn = (() => { + let connection = null; + return async(config) => { + if (connection === null) { + connection = await driver.connect(config); + return connection; + } + return connection; + }; +})(); + + +const Link = async(config) => { + const { + server, + user, + password, + database, + pool, + options + } = config; + const cPool = await conn({ + server, + user, + password, + pool, + database, + options + }); + return { + query: async(q) => { + try { + return await cPool.request().query(q); + } catch (e) { + console.warn(q); + console.error(e); + throw e; + } + }, + request: () => cPool.request() + }; +}; + + +module.exports = async(config) => { + const {gluePrefix = '.'} = config; + const link = await Link(config.connect); + + const predefinedQuery = async(key) => { + if (!key) { + throw SqlSe.create('noSuchSqlHelperFile'); + } + const q = (await predefinedSql[key]).toString('utf8'); + return link.query( + q + ); + } + + return {}; +}; diff --git a/lib/postgresql/sqls/index.js b/lib/postgresql/sqls/index.js new file mode 100644 index 0000000..b5e9fdb --- /dev/null +++ b/lib/postgresql/sqls/index.js @@ -0,0 +1,5 @@ +const read = require('util').promisify(require('fs').readFile); +module.exports = { + internalMethod: read([__dirname, 'internalMethod.sql'].join('/')), + tableTypes: read([__dirname, 'tableTypes.sql'].join('/')) +}; diff --git a/lib/postgresql/sqls/internalMethod.sql b/lib/postgresql/sqls/internalMethod.sql new file mode 100644 index 0000000..362d13a --- /dev/null +++ b/lib/postgresql/sqls/internalMethod.sql @@ -0,0 +1,20 @@ +SELECT + OBJECT_SCHEMA_NAME([SYSPROC].object_id) [schema], + [SYSPROC].name name, + [SYSPROC].object_id objectId, + [SYSPARAMS].name AS [column], + TYPE_NAME([SYSPARAMS].user_type_id) AS [type], + [SYSPARAMS].max_length AS length, + CASE + WHEN TYPE_NAME([SYSPARAMS].system_type_id) = 'uniqueidentifier' + THEN [SYSPARAMS].PRECISION + ELSE OdbcPrec([SYSPARAMS].system_type_id, [SYSPARAMS].max_length, [SYSPARAMS].PRECISION) + END AS [precision], + OdbcScale([SYSPARAMS].system_type_id, [SYSPARAMS].scale) AS [scale], + [SYSPARAMS].parameter_id AS [order], + [SYSPARAMS].user_type_id AS userTypeId, + [SYSPARAMS].is_output AS isOutput +FROM + sys.procedures [SYSPROC] +LEFT JOIN + sys.parameters [SYSPARAMS] ON [SYSPARAMS].object_id = [SYSPROC].object_id diff --git a/lib/postgresql/sqls/tableTypes.sql b/lib/postgresql/sqls/tableTypes.sql new file mode 100644 index 0000000..a256bbe --- /dev/null +++ b/lib/postgresql/sqls/tableTypes.sql @@ -0,0 +1,27 @@ +SELECT + SCHEMA_NAME(types.schema_id) + '.' + types.name name, + c.name [column], + st.name type, + CASE + WHEN st.name IN ('decimal', 'numeric') THEN CAST(c.[precision] AS VARCHAR) + WHEN st.name IN ('datetime2', 'time', 'datetimeoffset') THEN CAST(c.[scale] AS VARCHAR) + WHEN st.name IN ('varchar', 'varbinary', 'char', 'binary') AND c.max_length >= 0 THEN CAST(c.max_length AS VARCHAR) + WHEN st.name IN ('nvarchar', 'nchar') AND c.max_length >= 0 THEN CAST(c.max_length / 2 AS VARCHAR) + WHEN st.name IN ('varchar', 'varbinary', 'char', 'binary', 'nvarchar', 'nchar') AND c.max_length < 0 THEN 'max' + END [length], + CASE + WHEN st.name IN ('decimal', 'numeric') THEN c.scale + END scale, + OBJECT_DEFINITION(c.default_object_id) [default], + types.user_type_id AS userTypeId, + c.is_nullable AS isNullable +FROM + sys.table_types types +JOIN + sys.columns c ON types.type_table_object_id = c.object_id +JOIN + sys.systypes AS st ON st.xtype = c.system_type_id +WHERE + types.is_user_defined = 1 AND st.name <> 'sysname' +ORDER BY + 1, c.column_id