From f441b56fbf2d4350bd9598623848eb4f325923ce Mon Sep 17 00:00:00 2001 From: deostroll Date: Mon, 24 Feb 2020 16:30:16 +0530 Subject: [PATCH 01/16] added depedency to oracledb and script for clearing db for tests --- package.json | 10 ++++-- test/oracle-utility.js | 82 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 test/oracle-utility.js diff --git a/package.json b/package.json index fbed094..85c374d 100644 --- a/package.json +++ b/package.json @@ -3,20 +3,24 @@ "version": "2.0.1", "main": "index.js", "scripts": { - "lint": "eslint ." + "lint": "eslint .", + "test": "mocha test/*.spec.js" }, "engines": { "node": ">=4.0.0" }, "dependencies": { "async": "2.6.1", - "debug": "4.1.1" + "debug": "4.1.1", + "oracledb": "^4.2.0" }, "description": "The oracle connector for the oe-cloud framework.", "devDependencies": { "bluebird": "3.5.3", "eslint": "4.10.0", - "mocha": "5.2.0" + "loopback-datasource-juggler": "^4.18.1", + "mocha": "5.2.0", + "should": "^13.2.3" }, "optionalDependencies": {}, "directories": {}, diff --git a/test/oracle-utility.js b/test/oracle-utility.js new file mode 100644 index 0000000..7d7d921 --- /dev/null +++ b/test/oracle-utility.js @@ -0,0 +1,82 @@ +/** + * + * ©2016-2017 EdgeVerve Systems Limited (a fully owned Infosys subsidiary), + * Bangalore, India. All Rights Reserved. + * + */ + +/* eslint-disable */ + +/* +* This script clears the oracle database. +* +* To be run before executing tests +* +* Requires following environment variables: +* ORACLE_DATABASE +* ORACLE_PASSWORD +* ORACLE_HOST +* ORACLE_USER +* ORACLE_PORT +*/ + +var oracledb = require('oracledb'); +var async = require('async'); +var fs = require('fs'); +var os = require('os'); + +var oracleHost = process.env.ORACLE_HOST || 'localhost'; +var oraclePort = process.env.ORACLE_PORT ? parseInt(process.env.ORACLE_PORT) : 1521; +var oracleSID = process.env.ORACLE_DATABASE || 'ORCLCDB'; + +var oracleConnectSettings = { + 'password': process.env.ORACLE_PASSWORD || 'manager1', + 'user': process.env.ORACLE_USER || 'sys', + 'connectString': oracleHost + ':' + oraclePort + '/' + oracleSID +}; + +function fetchUserTables(cxn, cb) { + let fetchTablesQuery = 'SELECT table_name from USER_TABLES'; + cxn.execute(fetchTablesQuery, {}, function(err, results){ + cb(err, results); + }); +} + +function connectOracle(settings, cb) { + oracledb.getConnection(settings, function(err, cxn) { + cb(err, cxn); + }); +} + +function dropTable(cxn, table, cb) { + let deleteQuery = `DROP TABLE ${table}`; + cxn.execute(deleteQuery, function(err) { + cb(err) + }); +} + +connectOracle(oracleConnectSettings, function onConnect(err, cxn) { + if(err) { + console.error('Connect Error:', err); + process.exit(1); + } + else { + fetchUserTables(cxn, function(err, results) { + if(err) { + console.error('Fetch Error:', err); + process.exit(1); + } + else { + async.eachSeries(results.rows, function asyncDropOperation(record, done){ + let table = record[0]; + dropTable(cxn, table, done); + }, function asyncDropCb(err) { + if(err) { + console.error(err); + process.exit(1); + } + }); + } + }); + } +}); \ No newline at end of file From 3831f1b3ee69be3ca04fb0dab360e773cc816a07 Mon Sep 17 00:00:00 2001 From: deostroll Date: Mon, 24 Feb 2020 16:31:27 +0530 Subject: [PATCH 02/16] interim: added code and modified createTable code for creating sequence --- lib/migration.js | 258 +++++++++++++++++++++++++++++++---- test/init/init.js | 42 ++++++ test/mocha.opts | 3 + test/oracle.sequence.spec.js | 168 +++++++++++++++++++++++ 4 files changed, 448 insertions(+), 23 deletions(-) create mode 100644 test/init/init.js create mode 100644 test/mocha.opts create mode 100644 test/oracle.sequence.spec.js diff --git a/lib/migration.js b/lib/migration.js index 39ccde3..e1e737f 100644 --- a/lib/migration.js +++ b/lib/migration.js @@ -11,11 +11,173 @@ var g = SG(); var async = require('async'); // var uuid = require('uuid'); +const SEQ_TYPE_SIMPLE = 'simple'; +const SEQ_TYPE_COMPLEX = 'complex'; + +const SEQ_SIMPLE = { + name: null, // sequence name - required + incrementBy: 1, + minValue: false, // number or boolean + maxValue: false, // number or boolean + startFrom: 1, // number + cache: 1, // no. of sequences to cache, boolean (false) or a number. 1 is no cache. Min value - 1 + cycle: false, // restart once the seq reaches its upper bound. +}; + +// complex sequence is an extension of a simple sequence +const SEQ_COMPLEX = Object.assign({}, SEQ_SIMPLE, { + name: null, + length: 0, + prefix: null +}); + +function checkSequence(self, seqDef, cb) { + let seqQuery = `SELECT count(*) from user_sequences where sequence_name = '${seqDef.name}'`; + self.executeSQL(seqQuery, function(err, results) { + if(err) { + cb(err); + } + else if(results.rows.length === 1) { + cb(null, true) + } + else { + cb(null, false); + } + }); +} + +function createSequence(self, modelName, seqDef, cb) { -function getNewNumberCtr(){ + let createSequenceStatement = 'CREATE SEQUENCE '; + + if (seqDef.name === 'null' && typeof seqDef.name !== 'string') { + return cb(new Error('Invalid sequence definition. "name" is required and must be "string" for COMPLEX postgres sequence')); + } + else { + let qualifiedSeqName = `"${self.schema(modelName)}"."${seqDef.name}"`; + createSequenceStatement += qualifiedSeqName + NEWLINE; + } + + if (typeof seqDef.incrementBy === 'number') { + createSequenceStatement += `INCREMENT BY ${seqDef.incrementBy}` + NEWLINE; + } + else { + return cb(new Error('Invalid sequence defintion. "incrementBy" should be a number for COMPLEX postgres sequence')); + } + + let { minValue } = seqDef; + let typeOfMinVal = typeof seqDef.minValue; + if (typeOfMinVal === 'boolean' && minValue === false) { + createSequenceStatement += 'NO MINVALUE' + NEWLINE; + } + else if (typeOfMinVal === 'number') { + createSequenceStatement += `MINVALUE ${minValue}` + NEWLINE; + } + else { + return cb(new Error('Invalid sequence definition. "minValue" should be a number or boolean')) + } + + let { maxValue } = seqDef; + let typeOfMaxVal = typeof seqDef.maxValue; + if (typeOfMaxVal === 'boolean' && maxValue === false) { + createSequenceStatement += 'NO MAXVALUE' + NEWLINE; + } + else if (typeOfMaxVal === 'number') { + createSequenceStatement += `MAXVALUE ${maxValue}` + NEWLINE; + } + else { + return cb(new Error('Invalid sequence definition. "maxValue" should be a number or boolean')); + } + + let { startFrom } = seqDef; + let typeOfStartFrom = typeof startFrom; + + if (typeOfStartFrom === 'number') { + createSequenceStatement += `START ${startFrom}` + NEWLINE; + } + else { + return cb(new Error('Invalid sequence definition, "startFrom" should be a number')) + } + + //cache + let { cache } = seqDef; + let typeOfCache = typeof cache; + if (typeOfCache === 'number' && cache > 0) { + createSequenceStatement += `CACHE ${cache}` + NEWLINE; + } + else { + return cb(new Error('Invalid sequence definition, "cache" should be a number, and greater than 0')); + } + + //cycle + let { cycle } = seqDef; + let typeOfCycle = typeof cycle; + if (typeOfCycle === 'boolean' && cycle) { + createSequenceStatement += 'CYCLE' + NEWLINE; + } + else if (typeOfCycle === 'boolean') { + createSequenceStatement += 'NO CYCLE' + NEWLINE; + } + else { + return cb(new Error('Invalid sequence definition, "cycle" should be boolean value')); + } + + self.executeSQL(createSequenceStatement, null, {}, err => { + cb(err); + }); +} + +function doSimpleSequence(connector, modelName, key, propDef, cb) { + if (propDef.type.name !== 'Number') { + let msg = `Invalid sequence definition. Property ${key} has to be of type "number" for STANDARD postgres sequence`; + cb(new Error(msg)) + } + else { + let self = connector; + let { oracle: { sequence } } = propDef; + let seqDef = Object.assign({}, SEQ_SIMPLE, sequence); + checkSequence(self, seqDef, function(err, hasSequence) { + if(err) { + cb(err) + } + else if(!hasSequence) { + createSequence(self, modelName, seqDef, cb); + } + else { + cb(); + } + }); + + } +} + +function doComplexSequence(connector, modelName, key, propDef, cb) { + if (propDef.type.name !== 'String') { + let msg = `Invalid sequence definition. Property ${key} has to be of type "string" for COMPLEX postgres sequence`; + cb(new Error(msg)); + } + else { + let self = connector; + let { oracle: { sequence } } = propDef; + let seqDef = Object.assign({}, sequence, SEQ_COMPLEX); + checkSequence(self, seqDef, function(err, hasSequence) { + if(err) { + cb(err) + } + else if(!hasSequence) { + createSequence(self, modelName, seqDef, cb); + } + else { + cb(); + } + }); + } +} + +function getNewNumberCtr() { var num = 0; - return function(){ + return function () { ++num; return num; } @@ -216,8 +378,8 @@ function mixinMigration(Oracle) { var sql = []; var self = this; var additions = self.getColumnsToAdd(model, actualFields); - if(additions && additions.length > 0) { - sql.push('ADD (' + additions.join(',') + ')' ); + if (additions && additions.length > 0) { + sql.push('ADD (' + additions.join(',') + ')'); } sql = sql.concat(); var drops = self.getPropertiesToModify(model, actualFields); @@ -459,18 +621,68 @@ function mixinMigration(Oracle) { Oracle.prototype.createTable = function (model, cb) { var self = this; var name = self.tableEscaped(model); + var modelDef = self.getModelDefinition(model); + var { properties } = modelDef; + + var sequenceSupportedProperties = Object.keys(properties).reduce((carrier, key) => { + var { items } = carrier; + var currentEntry = properties[key]; + if (typeof currentEntry === 'object' && 'oracle' in currentEntry && 'sequence' in currentEntry.oracle) { + items.push([key, currentEntry]); + } + return carrier; + }, { items: [] }); + + if (sequenceSupportedProperties.items.length) { + async.eachSeries(sequenceSupportedProperties.items, function seqCreaterFn([key, propDef], done) { + let { oracle: { sequence } } = propDef; + + if ('type' in sequence) { + switch (sequence.type) { + case SEQ_TYPE_SIMPLE: + doSimpleSequence(self, model, key, propDef, done); + break; + + case SEQ_TYPE_COMPLEX: + doComplexSequence(self, model, key, propDef, done); + break; - self.executeSQL('CREATE TABLE ' + name + ' (\n ' + - self.propertiesSQL(model) + '\n)', [], {}, - function (err, info) { - //Atul : Ignore error if table already exists. .there is no support 'if exists' in Oracle. - if (err && err.message && err.message.indexOf('Error: ORA-00955') !== 0) { - // console.log('CREATE TABLE ERROR ', err); - return cb(err, info); + default: + break; + } } - self.addIndexes(model, undefined, cb); - } - ); + else { + done(new Error('Invalid Sequence Definition given: sequence type is not defined')); + } + }, function seqCreatedCallback(err) { + if (err) { + return cb(err); + } + + let statement = `CREATE TABLE IF NOT EXISTS ${name} (\n`; + statement += self.propertiesSQL(model) + '\n)\n'; + self.executeSQL(statement, null, {}, err => { + if (err) { + return cb(err) + } + cb(); + }); + }); + } + else { + self.executeSQL('CREATE TABLE ' + name + ' (\n ' + + self.propertiesSQL(model) + '\n)', [], {}, + function (err, info) { + //Atul : Ignore error if table already exists. .there is no support 'if exists' in Oracle. + if (err && err.message && err.message.indexOf('Error: ORA-00955') !== 0) { + // console.log('CREATE TABLE ERROR ', err); + return cb(err, info); + } + self.addIndexes(model, undefined, cb); + } + ); + } + }; @@ -598,7 +810,7 @@ function mixinMigration(Oracle) { case "object": default: var constraintName = model + "_" + property; - if(constraintName.length>30){ + if (constraintName.length > 30) { /** Simply truncating to 30 characters, sometimes results in duplicate constraint names * ProcessInstanceHistory._processTokens and ProcessInstanceHistory._processVariables * Both result in 'ProcessInstanceHistory_proces' as constraint name @@ -606,12 +818,12 @@ function mixinMigration(Oracle) { */ constraintName = model.substr(0, 25) + '_' + getNewNumber(); //uuid.v4(); constraintName = constraintName.substr(0, 30); - } + } return "CLOB CONSTRAINT \"" + constraintName + "\" CHECK (\"" + property.toUpperCase() + "\" IS JSON FORMAT JSON)"; } } else { var constraintName = model + "_" + property; - if(constraintName.length>30){ + if (constraintName.length > 30) { /** Simply truncating to 30 characters, sometimes results in duplicate constraint names * ProcessInstanceHistory._processTokens and ProcessInstanceHistory._processVariables * Both result in 'ProcessInstanceHistory_proces' as constraint name @@ -619,7 +831,7 @@ function mixinMigration(Oracle) { */ constraintName = model.substr(0, 25) + '_' + getNewNumber(); //uuid.v4(); constraintName = constraintName.substr(0, 30); - } + } return "CLOB CONSTRAINT \"" + constraintName + "\" CHECK (\"" + property.toUpperCase() + "\" IS JSON FORMAT JSON)"; } case 'String': @@ -629,7 +841,7 @@ function mixinMigration(Oracle) { case 'Object': case 'JSON': var constraintName = model + "_" + property; - if(constraintName.length>30){ + if (constraintName.length > 30) { /** Simply truncating to 30 characters, sometimes results in duplicate constraint names * ProcessInstanceHistory._processTokens and ProcessInstanceHistory._processVariables * Both result in 'ProcessInstanceHistory_proces' as constraint name @@ -637,7 +849,7 @@ function mixinMigration(Oracle) { */ constraintName = model.substr(0, 25) + '_' + getNewNumber(); //uuid.v4(); constraintName = constraintName.substr(0, 30); - } + } return "CLOB CONSTRAINT \"" + constraintName + "\" CHECK (\"" + property.toUpperCase() + "\" IS JSON FORMAT JSON)"; case 'Text': return 'CLOB'; @@ -650,7 +862,7 @@ function mixinMigration(Oracle) { case 'Point': return 'POINT'; case 'Boolean': - return 'CHAR(1)'; // PostgreSQL doesn't have built-in boolean + return 'CHAR(1)'; // oracle doesn't have built-in boolean case 'uuid': return 'uuid'; } @@ -842,7 +1054,7 @@ function mixinMigration(Oracle) { // The index name used should match the default naming scheme // by postgres: __idx var iName = [self.table(model), self.column(model, propName), 'idx'].join('_'); - if(iName.length>30){ + if (iName.length > 30) { /** Simply truncating to 30 characters, sometimes results in duplicate index names * ProcessInstanceHistory._processTokens and ProcessInstanceHistory._processVariables * Both result in 'ProcessInstanceHistory_proces' as index name @@ -886,7 +1098,7 @@ function mixinMigration(Oracle) { if (!found && deletedIndexes.indexOf(indexName) === -1) { i = normalizeIndexDefinition(i); var iName = self.escapeName(indexName); - if(iName.length>30){ + if (iName.length > 30) { iName = iName.substr(0, 25) + '_' + getNewNumber(); } var columns = i.keys.map(function (key) { diff --git a/test/init/init.js b/test/init/init.js new file mode 100644 index 0000000..40c0a21 --- /dev/null +++ b/test/init/init.js @@ -0,0 +1,42 @@ +// Copyright IBM Corp. 2013,2019. All Rights Reserved. +// Node module: loopback-connector-oracle +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + +'use strict'; + +const juggler = require('loopback-datasource-juggler'); +let DataSource = juggler.DataSource; + +const config = {}; +config.maxConn = 64; +config.user = process.env.ORACLE_USER || 'test'; +config.password = process.env.ORACLE_PASSWORD || 'testuser123'; +config.database = process.env.ORACLE_DATABASE || 'db'; +config.host = process.env.ORACLE_HOST || 'localhost'; +config.port = process.env.ORACLE_PORT || 1521; + +let db; + +global.getDataSource = global.getSchema = function() { + if (db) { + return db; + } + db = new DataSource(require('../../'), config); + db.log = function(a) { + // console.log(a); + }; + return db; +}; + +global.resetDataSourceClass = function(ctor) { + DataSource = ctor || juggler.DataSource; + const promise = db ? db.disconnect() : Promise.resolve(); + db = undefined; + return promise; +}; + +global.connectorCapabilities = { + ilike: false, + nilike: false, +}; \ No newline at end of file diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 0000000..9fa432a --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1,3 @@ +--timeout 120000 +--UV_THREADPOOL_SIZE=100 +--require ./test/init/init.js \ No newline at end of file diff --git a/test/oracle.sequence.spec.js b/test/oracle.sequence.spec.js new file mode 100644 index 0000000..926b360 --- /dev/null +++ b/test/oracle.sequence.spec.js @@ -0,0 +1,168 @@ +const should = require('should'); +let db = null; +describe('Auto-create schema with sequence support', function() { + before(function(done) { + db = global.getDataSource(); + + // simple sequence + db.define('TestSchema2', { + reservationId: { + type: 'number', + oracle: { + sequence: { + type: 'simple', + name: 'reservation_sequence' + } + }, + id: true + }, + firstName: "string", + "lastName":"string" + }); + + // complex sequence + db.define('TestSchema3', { + reservationId : { + type: 'string', + postgresql: { + sequence: { + type: 'complex', + prefix: 'LMB', + name: 'reservation_sequence', + length: 10, + } + }, + id: true + }, + firstName: 'string', + lastName: 'string' + }); + + + // var p = db.automigrate(); + // p.then(function(){ + // return done(); + // }).catch(function(error){ + // return done(error) + // }); + db.automigrate(function(error){ + console.log(error) + return done(error) + }); + }); + + describe('simple sequence', function() { + + it('asserts that the reservationid is a column created and it has sequence suppport in testschema2', function(done){ + // let connector = db.connector; + let query = 'SELECT column_default FROM INFORMATION_SCHEMA.columns WHERE table_name = \'testschema2\' and column_name = \'reservationid\''; + + db.connector.executeSQL(query, null, {}, function(err, results) { + if(err) { + done(err) + } + else { + results.length.should.equal(1); + results[0].column_default.includes('reservation_sequence').should.be.true(); + done(); + } + }); + }); + + it('asserts that the sequence object is created in database', done => { + let query = 'select * from information_schema.sequences where sequence_name = \'reservation_sequence\''; + db.connector.executeSQL(query, null, {}, function(err, results) { + if(err) { + done(err); + } + else { + // console.dir(results); + results.length.should.equal(1); + // results[0].sequence_name.should.exist; + should.exist(results[0].sequence_name); + results[0].sequence_name.should.equal('reservation_sequence'); + done(); + } + }); + }); + + it('asserts that the created sequence increments by 1', done => { + var Model = db.models['TestSchema2']; + var data = [ + { firstName: 'John', lastName: 'Doe' }, + { firstName: 'Jane', lastName: 'Contoso' } + ]; + + Model.create(data, function(err) { + if(err) { + done(err); + } + else { + db.connector.executeSQL('select last_value from reservation_sequence', null, {}, function(err, result){ + if(err) { + done(err); + } + else { + result[0].last_value.should.equal(2); + done(); + } + }); + } + }); + }); + }); + + describe('complex sequence', function(){ + it('should have created the table in the db', done => { + let query = 'select count(*) from information_schema.tables where table_name = \'testschema3\''; + db.connector.executeSQL(query, null, {}, function(err, result) { + if(err) { + done(err); + } + else { + // console.dir(result); + result.length.should.equal(1); + result[0].count.should.equal(1); + done(); + } + }); + }); + + it('should insert the record with the correct sequence pattern', done => { + let data = [ + { + firstName: 'John', lastName: 'Doe' + }, + { + firstName: 'Jane', lastName: 'Contoso' + } + ]; + + let model = db.models['TestSchema3']; + + model.create(data, (err, results) => { + if(err) { + done(err); + } + else { + results.length.should.equal(2); + let query = `select last_value from reservation_sequence;`; + db.connector.executeSQL(query, null, {}, (err, result) => { + if(err) { + done(err) + } + else { + result[0].last_value.should.equal(4); + done(); + } + }); + } + }); + }); + + }); + + + + +}); \ No newline at end of file From 72ab6f0b8a84aa7d41c0eafc31babb47bf5209d1 Mon Sep 17 00:00:00 2001 From: deostroll Date: Tue, 25 Feb 2020 10:16:07 +0530 Subject: [PATCH 03/16] modified util script for sequence deletions --- test/oracle-utility.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/test/oracle-utility.js b/test/oracle-utility.js index 7d7d921..e725e38 100644 --- a/test/oracle-utility.js +++ b/test/oracle-utility.js @@ -42,6 +42,20 @@ function fetchUserTables(cxn, cb) { }); } +function fetchSequenceObjects(cxn, cb) { + let fetchTablesQuery = 'SELECT sequence_name from USER_sequences'; + cxn.execute(fetchTablesQuery, {}, function(err, results){ + cb(err, results); + }); +} + +function dropSequence(cxn, seqName, cb) { + let deleteQuery = `DROP SEQUENCE ${seqName}`; + cxn.execute(deleteQuery, function(err) { + cb(err) + }); +} + function connectOracle(settings, cb) { oracledb.getConnection(settings, function(err, cxn) { cb(err, cxn); @@ -75,6 +89,25 @@ connectOracle(oracleConnectSettings, function onConnect(err, cxn) { console.error(err); process.exit(1); } + else { + fetchSequenceObjects(cxn, function(err, results) { + if(err) { + console.error('Seq fetch error:', err); + process.exit(1); + } + else { + async.eachSeries(results.rows, function asyncDropSeq(record, done){ + let [name] = record; + dropSequence(cxn, name, done); + }, function dropSeqAsyncCallback(err) { + if(err) { + console.error('Seq delete error:', err); + process.exit(1); + } + }); + } + }); + } }); } }); From fafb78268984df6cece5689f69f6e804a997fb4a Mon Sep 17 00:00:00 2001 From: deostroll Date: Tue, 25 Feb 2020 16:48:07 +0530 Subject: [PATCH 04/16] interim: code for sequence creation & dropping tables --- lib/migration.js | 62 ++++++++++++++++++++++++---------- lib/oracle.js | 21 ++++++++++++ test/oracle.sequence.spec.js | 65 +++++++++++++++++------------------- 3 files changed, 96 insertions(+), 52 deletions(-) diff --git a/lib/migration.js b/lib/migration.js index e1e737f..0817646 100644 --- a/lib/migration.js +++ b/lib/migration.js @@ -10,7 +10,7 @@ var SG = require('strong-globalize'); var g = SG(); var async = require('async'); // var uuid = require('uuid'); - +const NEWLINE = '\n'; const SEQ_TYPE_SIMPLE = 'simple'; const SEQ_TYPE_COMPLEX = 'complex'; @@ -20,8 +20,13 @@ const SEQ_SIMPLE = { minValue: false, // number or boolean maxValue: false, // number or boolean startFrom: 1, // number - cache: 1, // no. of sequences to cache, boolean (false) or a number. 1 is no cache. Min value - 1 + cache: false, // Specify (integer) how many values of the sequence the database preallocates and keeps in memory for faster access. + // Must be >=2. The integer should have less than or equal to 28 digits. + // Alternatively specify boolean. + // A boolean true means cache value adopts 2. + // A boolean false means no caching (default) cycle: false, // restart once the seq reaches its upper bound. + order: false // guarantee sequence numbers are generated in order of request. Default false }; // complex sequence is an extension of a simple sequence @@ -32,12 +37,12 @@ const SEQ_COMPLEX = Object.assign({}, SEQ_SIMPLE, { }); function checkSequence(self, seqDef, cb) { - let seqQuery = `SELECT count(*) from user_sequences where sequence_name = '${seqDef.name}'`; - self.executeSQL(seqQuery, function(err, results) { + let seqQuery = `SELECT count(*) as hasSequence from user_sequences where sequence_name = '${seqDef.name}'`; + self.executeSQL(seqQuery, [], {}, function(err, results) { if(err) { cb(err); } - else if(results.rows.length === 1) { + else if(results.length === 1 && results[0]['HASSEQUENCE']) { // --__(*.*)__-- cb(null, true) } else { @@ -54,9 +59,8 @@ function createSequence(self, modelName, seqDef, cb) { if (seqDef.name === 'null' && typeof seqDef.name !== 'string') { return cb(new Error('Invalid sequence definition. "name" is required and must be "string" for COMPLEX postgres sequence')); } - else { - let qualifiedSeqName = `"${self.schema(modelName)}"."${seqDef.name}"`; - createSequenceStatement += qualifiedSeqName + NEWLINE; + else { + createSequenceStatement += seqDef.name + NEWLINE; } if (typeof seqDef.incrementBy === 'number') { @@ -69,7 +73,7 @@ function createSequence(self, modelName, seqDef, cb) { let { minValue } = seqDef; let typeOfMinVal = typeof seqDef.minValue; if (typeOfMinVal === 'boolean' && minValue === false) { - createSequenceStatement += 'NO MINVALUE' + NEWLINE; + createSequenceStatement += 'NOMINVALUE' + NEWLINE; } else if (typeOfMinVal === 'number') { createSequenceStatement += `MINVALUE ${minValue}` + NEWLINE; @@ -81,7 +85,7 @@ function createSequence(self, modelName, seqDef, cb) { let { maxValue } = seqDef; let typeOfMaxVal = typeof seqDef.maxValue; if (typeOfMaxVal === 'boolean' && maxValue === false) { - createSequenceStatement += 'NO MAXVALUE' + NEWLINE; + createSequenceStatement += 'NOMAXVALUE' + NEWLINE; } else if (typeOfMaxVal === 'number') { createSequenceStatement += `MAXVALUE ${maxValue}` + NEWLINE; @@ -94,7 +98,7 @@ function createSequence(self, modelName, seqDef, cb) { let typeOfStartFrom = typeof startFrom; if (typeOfStartFrom === 'number') { - createSequenceStatement += `START ${startFrom}` + NEWLINE; + createSequenceStatement += `START WITH ${startFrom}` + NEWLINE; } else { return cb(new Error('Invalid sequence definition, "startFrom" should be a number')) @@ -103,11 +107,17 @@ function createSequence(self, modelName, seqDef, cb) { //cache let { cache } = seqDef; let typeOfCache = typeof cache; - if (typeOfCache === 'number' && cache > 0) { + if (typeOfCache === 'number' && cache >= 2) { createSequenceStatement += `CACHE ${cache}` + NEWLINE; } + else if (typeOfCache === 'boolean' && cache === false) { + createSequenceStatement += 'NOCACHE' + NEWLINE; + } + else if (typeOfCache === 'boolean') { + createSequenceStatement += 'CACHE 2' + NEWLINE; + } else { - return cb(new Error('Invalid sequence definition, "cache" should be a number, and greater than 0')); + return cb(new Error('Invalid sequence definition, "cache" should be a number, and greater than or equals to 2, OR, a boolean')); } //cycle @@ -117,13 +127,26 @@ function createSequence(self, modelName, seqDef, cb) { createSequenceStatement += 'CYCLE' + NEWLINE; } else if (typeOfCycle === 'boolean') { - createSequenceStatement += 'NO CYCLE' + NEWLINE; + createSequenceStatement += 'NOCYCLE' + NEWLINE; } else { return cb(new Error('Invalid sequence definition, "cycle" should be boolean value')); } - self.executeSQL(createSequenceStatement, null, {}, err => { + //order + let { order } = seqDef; + let typeOfOrder = typeof order; + if(typeOfOrder === 'boolean' && order) { + createSequenceStatement += 'ORDER' + NEWLINE; + } + else if(typeOfOrder === 'boolean') { + createSequenceStatement += 'NOORDER' + NEWLINE; + } + else { + return cb(new Error('Invalid sequence definition, "order" should be boolean value')); + } + + self.executeSQL(createSequenceStatement, [], {}, err => { cb(err); }); } @@ -589,6 +612,11 @@ function mixinMigration(Oracle) { var self = this; var modelDef = this.getModelDefinition(model); var prop = modelDef.properties[propName]; + + if('oracle' in prop && 'sequence' in prop.oracle && prop.oracle.sequence.type === 'simple') { + let seqDef = prop.oracle.sequence; + return `NUMBER DEFAULT ${seqDef.name}.nextval`; + } //Atul : ids are autogenerated with sys_buid() as default if (prop.id && prop.generated) { return 'varchar2(50) default sys_guid() not null'; @@ -659,9 +687,9 @@ function mixinMigration(Oracle) { return cb(err); } - let statement = `CREATE TABLE IF NOT EXISTS ${name} (\n`; + let statement = `CREATE TABLE ${name} (\n`; statement += self.propertiesSQL(model) + '\n)\n'; - self.executeSQL(statement, null, {}, err => { + self.executeSQL(statement, [], {}, err => { if (err) { return cb(err) } diff --git a/lib/oracle.js b/lib/oracle.js index 158cc91..929dec6 100644 --- a/lib/oracle.js +++ b/lib/oracle.js @@ -1901,3 +1901,24 @@ Oracle.prototype._buildAggregationExpression = } // require('./lock')(Oracle); + +// overriding as we have to check if table exists manually +Oracle.prototype.dropTable = function(model, cb) { + let existsQuery = `SELECT COUNT(*) AS has_table from USER_TABLES where TABLE_NAME = '${model}'`; + var self = this; + self.execute(existsQuery, [], {}, function(err, results) { + if(err) { + cb(err); + } + else { + let [record] = results; + if(record.HAS_TABLE) { + let dropQuery = `DROP TABLE ${model}`; + self.execute(dropQuery, [], {}, cb); + } + else { + cb(); + } + } + }); +} \ No newline at end of file diff --git a/test/oracle.sequence.spec.js b/test/oracle.sequence.spec.js index 926b360..52b9dbc 100644 --- a/test/oracle.sequence.spec.js +++ b/test/oracle.sequence.spec.js @@ -21,22 +21,22 @@ describe('Auto-create schema with sequence support', function() { }); // complex sequence - db.define('TestSchema3', { - reservationId : { - type: 'string', - postgresql: { - sequence: { - type: 'complex', - prefix: 'LMB', - name: 'reservation_sequence', - length: 10, - } - }, - id: true - }, - firstName: 'string', - lastName: 'string' - }); + // db.define('TestSchema3', { + // reservationId : { + // type: 'string', + // postgresql: { + // sequence: { + // type: 'complex', + // prefix: 'LMB', + // name: 'reservation_sequence', + // length: 10, + // } + // }, + // id: true + // }, + // firstName: 'string', + // lastName: 'string' + // }); // var p = db.automigrate(); @@ -45,9 +45,8 @@ describe('Auto-create schema with sequence support', function() { // }).catch(function(error){ // return done(error) // }); - db.automigrate(function(error){ - console.log(error) - return done(error) + db.automigrate(function(error){ + done(error) }); }); @@ -55,23 +54,24 @@ describe('Auto-create schema with sequence support', function() { it('asserts that the reservationid is a column created and it has sequence suppport in testschema2', function(done){ // let connector = db.connector; - let query = 'SELECT column_default FROM INFORMATION_SCHEMA.columns WHERE table_name = \'testschema2\' and column_name = \'reservationid\''; + // let query = 'SELECT column_default FROM INFORMATION_SCHEMA.columns WHERE table_name = \'testschema2\' and column_name = \'reservationid\''; + let query = 'SELECT data_default FROM USER_TAB_COLS WHERE table_name = \'TESTSCHEMA2\' and COLUMN_NAME = \'RESERVATIONID\''; - db.connector.executeSQL(query, null, {}, function(err, results) { + db.connector.executeSQL(query, [], {}, function(err, results) { if(err) { done(err) } else { results.length.should.equal(1); - results[0].column_default.includes('reservation_sequence').should.be.true(); + results[0].DATA_DEFAULT.includes('RESERVATION_SEQUENCE').should.be.true(); done(); } }); }); it('asserts that the sequence object is created in database', done => { - let query = 'select * from information_schema.sequences where sequence_name = \'reservation_sequence\''; - db.connector.executeSQL(query, null, {}, function(err, results) { + let query = 'select * from USER_sequences where sequence_name = \'RESERVATION_SEQUENCE\''; + db.connector.executeSQL(query, [], {}, function(err, results) { if(err) { done(err); } @@ -79,8 +79,8 @@ describe('Auto-create schema with sequence support', function() { // console.dir(results); results.length.should.equal(1); // results[0].sequence_name.should.exist; - should.exist(results[0].sequence_name); - results[0].sequence_name.should.equal('reservation_sequence'); + should.exist(results[0].SEQUENCE_NAME); + results[0].SEQUENCE_NAME.should.equal('RESERVATION_SEQUENCE'); done(); } }); @@ -98,12 +98,12 @@ describe('Auto-create schema with sequence support', function() { done(err); } else { - db.connector.executeSQL('select last_value from reservation_sequence', null, {}, function(err, result){ + db.connector.executeSQL('select reservation_sequence.currval as last_value FROM dual', null, {}, function(err, result){ if(err) { done(err); } else { - result[0].last_value.should.equal(2); + result[0].LAST_VALUE.should.equal(2); done(); } }); @@ -112,7 +112,7 @@ describe('Auto-create schema with sequence support', function() { }); }); - describe('complex sequence', function(){ + xdescribe('complex sequence', function(){ it('should have created the table in the db', done => { let query = 'select count(*) from information_schema.tables where table_name = \'testschema3\''; db.connector.executeSQL(query, null, {}, function(err, result) { @@ -159,10 +159,5 @@ describe('Auto-create schema with sequence support', function() { } }); }); - - }); - - - - + }); }); \ No newline at end of file From e6302d0aa3a0eb09942ab77af3f4871f0b6dfda1 Mon Sep 17 00:00:00 2001 From: deostroll Date: Wed, 26 Feb 2020 11:59:44 +0530 Subject: [PATCH 05/16] test setup for complex sequence --- test/oracle.sequence.spec.js | 56 ++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/test/oracle.sequence.spec.js b/test/oracle.sequence.spec.js index 52b9dbc..e7d3d4c 100644 --- a/test/oracle.sequence.spec.js +++ b/test/oracle.sequence.spec.js @@ -5,39 +5,39 @@ describe('Auto-create schema with sequence support', function() { db = global.getDataSource(); // simple sequence - db.define('TestSchema2', { - reservationId: { - type: 'number', - oracle: { - sequence: { - type: 'simple', - name: 'reservation_sequence' - } - }, - id: true - }, - firstName: "string", - "lastName":"string" - }); - - // complex sequence - // db.define('TestSchema3', { - // reservationId : { - // type: 'string', - // postgresql: { + // db.define('TestSchema2', { + // reservationId: { + // type: 'number', + // oracle: { // sequence: { - // type: 'complex', - // prefix: 'LMB', - // name: 'reservation_sequence', - // length: 10, + // type: 'simple', + // name: 'reservation_sequence' // } // }, // id: true // }, - // firstName: 'string', - // lastName: 'string' + // firstName: "string", + // "lastName":"string" // }); + // complex sequence + db.define('TestSchema3', { + reservationId : { + type: 'string', + postgresql: { + sequence: { + type: 'complex', + prefix: 'LMB', + name: 'reservation_sequence', + length: 10, + } + }, + id: true + }, + firstName: 'string', + lastName: 'string' + }); + // var p = db.automigrate(); // p.then(function(){ @@ -50,7 +50,7 @@ describe('Auto-create schema with sequence support', function() { }); }); - describe('simple sequence', function() { + xdescribe('simple sequence', function() { it('asserts that the reservationid is a column created and it has sequence suppport in testschema2', function(done){ // let connector = db.connector; @@ -98,7 +98,7 @@ describe('Auto-create schema with sequence support', function() { done(err); } else { - db.connector.executeSQL('select reservation_sequence.currval as last_value FROM dual', null, {}, function(err, result){ + db.connector.executeSQL('select reservation_sequence.currval as last_value FROM dual', [], {}, function(err, result){ if(err) { done(err); } From 3b584d32cfef7cb7e28c9769548a46e0410216e0 Mon Sep 17 00:00:00 2001 From: deostroll Date: Wed, 26 Feb 2020 15:28:00 +0530 Subject: [PATCH 06/16] tests randomly failing --- lib/migration.js | 23 ++++++--------- lib/oracle.js | 42 ++++++++++++++++++--------- test/oracle.sequence.spec.js | 55 ++++++++++++++++-------------------- 3 files changed, 60 insertions(+), 60 deletions(-) diff --git a/lib/migration.js b/lib/migration.js index 0817646..62969d6 100644 --- a/lib/migration.js +++ b/lib/migration.js @@ -37,7 +37,7 @@ const SEQ_COMPLEX = Object.assign({}, SEQ_SIMPLE, { }); function checkSequence(self, seqDef, cb) { - let seqQuery = `SELECT count(*) as hasSequence from user_sequences where sequence_name = '${seqDef.name}'`; + let seqQuery = `SELECT count(*) as hasSequence from user_sequences where sequence_name = '${self.dbName(seqDef.name)}'`; self.executeSQL(seqQuery, [], {}, function(err, results) { if(err) { cb(err); @@ -159,19 +159,15 @@ function doSimpleSequence(connector, modelName, key, propDef, cb) { else { let self = connector; let { oracle: { sequence } } = propDef; - let seqDef = Object.assign({}, SEQ_SIMPLE, sequence); - checkSequence(self, seqDef, function(err, hasSequence) { - if(err) { + let seqDef = Object.assign({}, SEQ_SIMPLE, sequence); + createSequence(self, modelName, seqDef, function(err){ + if(err && err.errorNum !== 955) { cb(err) } - else if(!hasSequence) { - createSequence(self, modelName, seqDef, cb); - } else { cb(); } - }); - + }); } } @@ -183,14 +179,11 @@ function doComplexSequence(connector, modelName, key, propDef, cb) { else { let self = connector; let { oracle: { sequence } } = propDef; - let seqDef = Object.assign({}, sequence, SEQ_COMPLEX); - checkSequence(self, seqDef, function(err, hasSequence) { - if(err) { + let seqDef = Object.assign({}, SEQ_COMPLEX, sequence); + createSequence(self, modelName, seqDef, function(err){ + if(err && err.errorNum !== 955) { cb(err) } - else if(!hasSequence) { - createSequence(self, modelName, seqDef, cb); - } else { cb(); } diff --git a/lib/oracle.js b/lib/oracle.js index 929dec6..51c1fac 100644 --- a/lib/oracle.js +++ b/lib/oracle.js @@ -532,6 +532,22 @@ Oracle.prototype._buildFieldsForKeys = function (model, data, keys, excludeIds) columnValues: [], // an array of ParameterizedSQL properties: [], // model properties }; + // Object.keys(props).reduce((carrier, key) => { + // let propDef = props[key]; + // if('oracle' in propDef && 'sequence' in propDef.oracle && ) + // }); + let propNames = Object.keys(props); + let complexSeqFieldIndex = propNames.findIndex(key => { + let propDef = props[key]; + return 'oracle' in propDef && 'sequence' in propDef.oracle && propDef.oracle.sequence.type === 'complex'; + }); + if(complexSeqFieldIndex > -1) { + let fieldName = propNames[complexSeqFieldIndex] + let def = props[fieldName]; + fields.names.push(fieldName); + fields.columnValues.push(this.toColumnValue(def, null)); + fields.properties.push(props[fieldName]) + } for (var i = 0, n = keys.length; i < n; i++) { var key = keys[i]; var p = props[key]; @@ -662,6 +678,12 @@ function dateToOracle(val, dateOnly) { Oracle.prototype.toColumnValue = function (prop, val) { if (val == null) { + + if(prop.oracle && prop.oracle.sequence && prop.oracle.sequence.type === 'complex') { + let { length, prefix, name } = prop.oracle.sequence; + let strLength = length - prefix.length; + return new ParameterizedSQL(`'${prefix}' || LPAD( TO_CHAR(${name}.nextval), ${strLength}, '0')`) + } // Oracle complains with NULLs in not null columns // If we have an autoincrement value, return DEFAULT instead if (prop.autoIncrement || prop.id) { @@ -1903,22 +1925,14 @@ Oracle.prototype._buildAggregationExpression = // require('./lock')(Oracle); // overriding as we have to check if table exists manually -Oracle.prototype.dropTable = function(model, cb) { - let existsQuery = `SELECT COUNT(*) AS has_table from USER_TABLES where TABLE_NAME = '${model}'`; - var self = this; - self.execute(existsQuery, [], {}, function(err, results) { - if(err) { +Oracle.prototype.dropTable = function(model, cb) { + let tableName = this.dbName(model); + this.execute(`DROP TABLE ${tableName}`, [], {}, function(err){ + if(err && err.errorNum !== 942) { cb(err); - } + } else { - let [record] = results; - if(record.HAS_TABLE) { - let dropQuery = `DROP TABLE ${model}`; - self.execute(dropQuery, [], {}, cb); - } - else { - cb(); - } + cb(); } }); } \ No newline at end of file diff --git a/test/oracle.sequence.spec.js b/test/oracle.sequence.spec.js index e7d3d4c..7c481e5 100644 --- a/test/oracle.sequence.spec.js +++ b/test/oracle.sequence.spec.js @@ -5,26 +5,26 @@ describe('Auto-create schema with sequence support', function() { db = global.getDataSource(); // simple sequence - // db.define('TestSchema2', { - // reservationId: { - // type: 'number', - // oracle: { - // sequence: { - // type: 'simple', - // name: 'reservation_sequence' - // } - // }, - // id: true - // }, - // firstName: "string", - // "lastName":"string" - // }); + db.define('TestSchema2', { + reservationId: { + type: 'number', + oracle: { + sequence: { + type: 'simple', + name: 'reservation_sequence' + } + }, + id: true + }, + firstName: "string", + "lastName":"string" + }); // complex sequence db.define('TestSchema3', { reservationId : { type: 'string', - postgresql: { + oracle: { sequence: { type: 'complex', prefix: 'LMB', @@ -38,19 +38,12 @@ describe('Auto-create schema with sequence support', function() { lastName: 'string' }); - - // var p = db.automigrate(); - // p.then(function(){ - // return done(); - // }).catch(function(error){ - // return done(error) - // }); db.automigrate(function(error){ done(error) }); }); - xdescribe('simple sequence', function() { + describe('simple sequence', function() { it('asserts that the reservationid is a column created and it has sequence suppport in testschema2', function(done){ // let connector = db.connector; @@ -112,17 +105,17 @@ describe('Auto-create schema with sequence support', function() { }); }); - xdescribe('complex sequence', function(){ + describe('complex sequence', function(){ it('should have created the table in the db', done => { - let query = 'select count(*) from information_schema.tables where table_name = \'testschema3\''; - db.connector.executeSQL(query, null, {}, function(err, result) { + let query = 'select count(*) as has_table from user_tables where table_name = \'TESTSCHEMA3\''; + db.connector.executeSQL(query, [], {}, function(err, result) { if(err) { done(err); } else { // console.dir(result); result.length.should.equal(1); - result[0].count.should.equal(1); + result[0].HAS_TABLE.should.equal(1); done(); } }); @@ -145,14 +138,14 @@ describe('Auto-create schema with sequence support', function() { done(err); } else { - results.length.should.equal(2); - let query = `select last_value from reservation_sequence;`; - db.connector.executeSQL(query, null, {}, (err, result) => { + results.length.should.equal(2,'Expected 2 successful inserts'); + let query = 'select reservation_sequence.currval as last_value from DUAL' + db.connector.executeSQL(query, [], {}, (err, result) => { if(err) { done(err) } else { - result[0].last_value.should.equal(4); + result[0].LAST_VALUE.should.equal(3); done(); } }); From a85786fbede4942e604f72c48c71d2ab6e296241 Mon Sep 17 00:00:00 2001 From: deostroll Date: Fri, 28 Feb 2020 10:43:50 +0530 Subject: [PATCH 07/16] modified tests to output data to console --- test/oracle.sequence.spec.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/test/oracle.sequence.spec.js b/test/oracle.sequence.spec.js index 7c481e5..2017fef 100644 --- a/test/oracle.sequence.spec.js +++ b/test/oracle.sequence.spec.js @@ -86,17 +86,20 @@ describe('Auto-create schema with sequence support', function() { { firstName: 'Jane', lastName: 'Contoso' } ]; - Model.create(data, function(err) { + Model.create(data, function(err, results) { if(err) { done(err); } else { - db.connector.executeSQL('select reservation_sequence.currval as last_value FROM dual', [], {}, function(err, result){ + results.length.should.equal(2); + console.log('fetch1:', results); + db.connector.executeSQL('select reservation_sequence.currval as last_value FROM dual', [], {}, function(err, res){ if(err) { done(err); } else { - result[0].LAST_VALUE.should.equal(2); + console.log('fetch2:', res); + res[0].LAST_VALUE.should.equal(2); done(); } }); @@ -139,13 +142,15 @@ describe('Auto-create schema with sequence support', function() { } else { results.length.should.equal(2,'Expected 2 successful inserts'); + console.log('fetch1:',results); let query = 'select reservation_sequence.currval as last_value from DUAL' - db.connector.executeSQL(query, [], {}, (err, result) => { + db.connector.executeSQL(query, [], {}, (err, res) => { if(err) { done(err) } else { - result[0].LAST_VALUE.should.equal(3); + console.log('fetch2:',res); + res[0].LAST_VALUE.should.equal(4); done(); } }); From 4799a028f782fe3205a7672207e57d42abc98353 Mon Sep 17 00:00:00 2001 From: deostroll Date: Fri, 28 Feb 2020 12:27:52 +0530 Subject: [PATCH 08/16] oracle sequence support --- test/oracle.sequence.spec.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/oracle.sequence.spec.js b/test/oracle.sequence.spec.js index 2017fef..febbbb3 100644 --- a/test/oracle.sequence.spec.js +++ b/test/oracle.sequence.spec.js @@ -92,14 +92,14 @@ describe('Auto-create schema with sequence support', function() { } else { results.length.should.equal(2); - console.log('fetch1:', results); - db.connector.executeSQL('select reservation_sequence.currval as last_value FROM dual', [], {}, function(err, res){ + // console.log('fetch1:', results); + db.connector.executeSQL('select reservation_sequence.nextval as last_value FROM dual', [], {}, function(err, res){ if(err) { done(err); } else { - console.log('fetch2:', res); - res[0].LAST_VALUE.should.equal(2); + // console.log('fetch2:', res); + res[0].LAST_VALUE.should.equal(3); done(); } }); @@ -107,7 +107,6 @@ describe('Auto-create schema with sequence support', function() { }); }); }); - describe('complex sequence', function(){ it('should have created the table in the db', done => { let query = 'select count(*) as has_table from user_tables where table_name = \'TESTSCHEMA3\''; @@ -118,6 +117,7 @@ describe('Auto-create schema with sequence support', function() { else { // console.dir(result); result.length.should.equal(1); + result[0].HAS_TABLE.should.equal(1); done(); } @@ -142,15 +142,15 @@ describe('Auto-create schema with sequence support', function() { } else { results.length.should.equal(2,'Expected 2 successful inserts'); - console.log('fetch1:',results); - let query = 'select reservation_sequence.currval as last_value from DUAL' + // console.log('fetch1:',results); + let query = 'select reservation_sequence.nextval as last_value from DUAL' db.connector.executeSQL(query, [], {}, (err, res) => { if(err) { done(err) } else { - console.log('fetch2:',res); - res[0].LAST_VALUE.should.equal(4); + // console.log('fetch2:',res); + res[0].LAST_VALUE.should.equal(6); done(); } }); From 12a149a5ecc20d8a17e33f72c690d31a476a431f Mon Sep 17 00:00:00 2001 From: deostroll Date: Fri, 28 Feb 2020 14:28:10 +0530 Subject: [PATCH 09/16] updated docs, bumped version --- README.md | 87 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/migration.js | 6 ++-- package.json | 2 +- 3 files changed, 91 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9d6acea..f9c2cb7 100644 --- a/README.md +++ b/README.md @@ -56,3 +56,90 @@ This will install the module and add's it to application’s package.json file. ``` * Any filter query on the properties which are not actually part of model definition properties will be ignored by default. This will give you unexpected results based on your query which will not be same as mongodb. For example, if your model has property ```foo``` and your filter query is ```bar:1```. Then mongo will return ```[]``` (Empty array). where as Oracle will return all the records because the bar:1 filter will be ignored because its not a defined property. + +## Sequence support + +As of version 2.1.0 of the connector, support for consuming sequence objects is provisioned. Unlike `oe-connector-postgresql` module, this connector has only two kinds of sequences - **simple** and **complex**. + +Only one property in a model can be defined to support an oracle sequence object. The property should be such that it uniquely identifies the instance. Hence it should be an `id ` field. + +### Simple Sequence + +Supports a simple sequence - it is similar to having the sequence object created in the database and a corresponding table consuming it through a column whose default value is appropriately set. For e.g. below code (i.e. model definition) creates a sequence with name `reservation_sequence` and a table named `testschema2` + +```json +{ + "name": "testschema2", + "properties": { + "reservationId" : { + "id" : true, + "oracle": { + "sequence" : { + "type": "simple", + "name": "reservation_sequence" + } + } + }, + "firstName": "string", + "lastName": "string" + } +} +``` + +> Note: Only one property in the model can consume a sequence, and, it also must uniquely identify an instance. (Therefore `"id" : true` is part of the corresponding property definition) + +Below strech of code describes the configuration required for defining a **simple** sequence. + +```js +const SEQ_SIMPLE = { + name: null, // sequence name - required + incrementBy: 1, + minValue: false, // number or boolean + maxValue: false, // number or boolean + startFrom: 1, // number + cache: false, // Specify (integer) how many values of the sequence the database preallocates and keeps in memory for faster access. + // Must be >=2. The integer should have less than or equal to 28 digits. + // Alternatively specify boolean. + // A boolean true means cache value adopts 2. + // A boolean false means no caching (default) + cycle: false, // restart once the seq reaches its upper bound. + order: false // guarantee sequence numbers are generated in order of request. Default false +}; +``` + +> Note: Please refer oracle documentation for more details about each parameter. + +### Complex sequence + +This connector also supports prefix based sequences. A `prefix` is a string which is prefixed to a padded sequence number. This makes it possible to generate sequences such as `LMB00001`, `LMB00002`, `LMB00003`, etc. + +It has all the configuration of a simple sequence, and, the following parameters: + +```js +const SEQ_COMPLEX = Object.assign({}, SEQ_SIMPLE, { + name: null, // sequence name - required + length: 0, // final length of prefix-ed sequence - required + prefix: null // the prefix to appear before the padded sequence - required +}); +``` +Example: +```json +{ + "name": "testschema3", + "properties": { + "reservationId" : { + "id" : true, + "oracle": { + "sequence" : { + "type": "complex", + "name": "reservation_sequence", + "prefix": "LMB", + "length": 10 + } + } + }, + "firstName": "string", + "lastName": "string" + } +} +``` \ No newline at end of file diff --git a/lib/migration.js b/lib/migration.js index 62969d6..9762b21 100644 --- a/lib/migration.js +++ b/lib/migration.js @@ -31,9 +31,9 @@ const SEQ_SIMPLE = { // complex sequence is an extension of a simple sequence const SEQ_COMPLEX = Object.assign({}, SEQ_SIMPLE, { - name: null, - length: 0, - prefix: null + name: null, // sequence name - required + length: 0, // final length of prefix-ed sequence + prefix: null // the prefix to appear before the padded sequence }); function checkSequence(self, seqDef, cb) { diff --git a/package.json b/package.json index 85c374d..d3c11d6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "oe-connector-oracle", - "version": "2.0.1", + "version": "2.1.0", "main": "index.js", "scripts": { "lint": "eslint .", From 54d8c788f935d4f4f092d199b8bbee9f1336348c Mon Sep 17 00:00:00 2001 From: deostroll Date: Fri, 28 Feb 2020 14:31:23 +0530 Subject: [PATCH 10/16] updated docs, bumped version --- lib/migration.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/migration.js b/lib/migration.js index 9762b21..8ceee81 100644 --- a/lib/migration.js +++ b/lib/migration.js @@ -32,8 +32,8 @@ const SEQ_SIMPLE = { // complex sequence is an extension of a simple sequence const SEQ_COMPLEX = Object.assign({}, SEQ_SIMPLE, { name: null, // sequence name - required - length: 0, // final length of prefix-ed sequence - prefix: null // the prefix to appear before the padded sequence + length: 0, // final length of prefix-ed sequence - required + prefix: null // the prefix to appear before the padded sequence - required }); function checkSequence(self, seqDef, cb) { From 38bcfdb4d49a71a4c9c3504aa4dc39b220b929be Mon Sep 17 00:00:00 2001 From: deostroll Date: Mon, 2 Mar 2020 14:53:00 +0530 Subject: [PATCH 11/16] Version bump: 2.1.1 - fixed version of oracledb dependency --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d3c11d6..76e7c73 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "oe-connector-oracle", - "version": "2.1.0", + "version": "2.1.1", "main": "index.js", "scripts": { "lint": "eslint .", @@ -12,7 +12,7 @@ "dependencies": { "async": "2.6.1", "debug": "4.1.1", - "oracledb": "^4.2.0" + "oracledb": "4.2.0" }, "description": "The oracle connector for the oe-cloud framework.", "devDependencies": { From 2dced3cd0fcf8861d93bd5941071c0ff8eeccc9f Mon Sep 17 00:00:00 2001 From: vamsee Date: Mon, 2 Mar 2020 15:37:41 +0530 Subject: [PATCH 12/16] update package version to 2.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 76e7c73..f23b4dd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "oe-connector-oracle", - "version": "2.1.1", + "version": "2.2.0", "main": "index.js", "scripts": { "lint": "eslint .", From dc66915a2c10e0f11dad592b3b1ac6126dc03ddb Mon Sep 17 00:00:00 2001 From: vamsee Date: Wed, 4 Mar 2020 16:26:20 +0530 Subject: [PATCH 13/16] oracledb version to 4.1.0 for oe-cloud test cases --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f23b4dd..1d38d88 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "dependencies": { "async": "2.6.1", "debug": "4.1.1", - "oracledb": "4.2.0" + "oracledb": "4.1.0" }, "description": "The oracle connector for the oe-cloud framework.", "devDependencies": { From cf10a2524cdcecca93bfdf8847ffcd6c42fd5510 Mon Sep 17 00:00:00 2001 From: Sumeet Date: Thu, 2 Apr 2020 14:27:19 +0530 Subject: [PATCH 14/16] added indexing for like operator --- README.md | 14 +++++++++++++- lib/oracle.js | 4 ++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f9c2cb7..d6efeae 100644 --- a/README.md +++ b/README.md @@ -142,4 +142,16 @@ Example: "lastName": "string" } } -``` \ No newline at end of file +``` + +## Index usage with like operator + +Like operator can be used in 4 ways in our queries: + +1) Search-String% +2) %Search-String +3) %Search-String% +4) Search%String + +Index range scan is only done in for cases like Search-String% and Search%String. +While using %Search-String and %Search-String% full table scan is done. \ No newline at end of file diff --git a/lib/oracle.js b/lib/oracle.js index 51c1fac..6552752 100644 --- a/lib/oracle.js +++ b/lib/oracle.js @@ -434,7 +434,7 @@ Oracle.prototype._buildWhere = function (model, where) { var operator = Object.keys(expression)[0]; // Get the expression without the operator if (operator === 'like' || operator === 'nlike' || operator === 'ilike' || operator === 'nilike') { - this._expression = expression = new RegExp(expression[operator]); + this._expression = expression = expression[operator]; } else if (expression.constructor === RegExp) { operator = 'regexp'; } else { @@ -1655,7 +1655,7 @@ Oracle.prototype._buildHavingClause = function (model, having) { } else if (expression && (expression.constructor === Object || expression.constructor === RegExp)) { var operator = Object.keys(expression)[0]; if (operator === 'like' || operator === 'nlike' || operator === 'ilike' || operator === 'nilike') { - this._expression = expression = new RegExp(expression[operator]); + this._expression = expression = expression[operator]; } else if (expression.constructor === RegExp) { operator = 'regexp'; } else { From fb2ac4018ba73db812f4a641f9e4767e2e36e311 Mon Sep 17 00:00:00 2001 From: Sumeet Date: Thu, 2 Apr 2020 14:34:43 +0530 Subject: [PATCH 15/16] modified readme.md file --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d6efeae..e145d2f 100644 --- a/README.md +++ b/README.md @@ -148,10 +148,10 @@ Example: Like operator can be used in 4 ways in our queries: -1) Search-String% -2) %Search-String -3) %Search-String% -4) Search%String +1. Search-String% +2. %Search-String +3. %Search-String% +4. Search%String Index range scan is only done in for cases like Search-String% and Search%String. -While using %Search-String and %Search-String% full table scan is done. \ No newline at end of file +While using %Search-String and %Search-String% full table scan is done. From 6a551936c4623d4d902edbc54fe7a75739b37097 Mon Sep 17 00:00:00 2001 From: vamsee Date: Mon, 3 Aug 2020 00:16:14 +0530 Subject: [PATCH 16/16] commit for version 2.3.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1d38d88..8334efe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "oe-connector-oracle", - "version": "2.2.0", + "version": "2.3.0", "main": "index.js", "scripts": { "lint": "eslint .",