├── .gitignore ├── LICENSE ├── README.md ├── TODO ├── driver ├── exports.sh └── reinstall.sh ├── lib ├── Column.js ├── ORM.js ├── Query.js ├── Row.js ├── Table.js ├── helpers.js ├── index.js ├── types.js └── unittests.js ├── package.json ├── recompile.sh ├── src ├── Column.coffee ├── ORM._coffee ├── Query._coffee ├── Row._coffee ├── Table._coffee ├── helpers.coffee ├── index.coffee ├── types.coffee └── unittests._coffee └── testDB.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Simon Grondin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | oracle-orm 2 | ========== 3 | 4 | Because the other options are worse. 5 | 6 | There's only one other ORM for Node.js that supports Oracle. It says a lot about the cultures of both Node and Oracle. 7 | 8 | The other ORM has over a year of open issues on Github, it sometimes generates invalid Oracle SQL statements and does absurd things like creating a table `PEOPLE` for a model named `PERSON` (and `PEOPLES` for `PEOPLE`, ha). I don't like ORMs, I'm forced to use one for a project. I don't want my tools to do unexpected things in the database and that other ORM is full of surprises. 9 | 10 | Oracle-ORM is safe against SQL injections. 11 | 12 | This ORM only supports the features I need. It doesn't support connection pooling, error recovery and all sorts of basic things you'd expect from an ORM. If you want those features added, open an issue or send a pull request. 13 | 14 | 15 | # Install 16 | 17 | Don't install from npm yet! 18 | 19 | First, make sure libaio is installed: `sudo apt-get install libaio1` or `sudo yum install libaio` 20 | 21 | Then, go to the [Oracle driver page](http://www.oracle.com/technetwork/database/features/instant-client/index-097480.html) and select your architecture. Download `instantclient-basic-linux.x64-12.1.***.zip` and `instantclient-sdk-linux.x64-12.1.***.zip`. 22 | 23 | Unzip both into `YOUR_PROJECT/instantclient_12_1`. It won't work if the driver can't be found. 24 | 25 | Run `npm install oracle-orm` in your project's directory. It'll prompt for sudo because the Oracle driver needs environment variables. 26 | 27 | Finally, run `source /etc/environment` (or just logout and log back in). All done! 28 | 29 | 30 | #### Run the tests 31 | 32 | Put the database credentials in `node_modules/oracle-orm/testDB.json` and run `npm test oracle-orm`. 33 | 34 | 35 | # Usage 36 | 37 | ```javascript 38 | var ORM = require("oracle-orm"); 39 | var oracleConnectData = { 40 | driver: "oracle" 41 | hostname: "hostname or IP" 42 | port: 1521 43 | database: "database name (SID)" 44 | user: "username" 45 | password: "password123" 46 | }; 47 | var debug = true; 48 | 49 | new ORM(oracleConnectData, debug, function(err, orm){ 50 | orm.getModels(function(err, models){ 51 | // do stuff with the models 52 | }); 53 | }); 54 | ``` 55 | 56 | Due to the very callback-heavy nature of SQL, it's recommended to use a tool to deal with that. Promises, Generators, Streamline, Q, Async, etc. are all good options. 57 | 58 | 59 | # Documentation 60 | 61 | Oracle-ORM works by building one Model object per table in the database. Operations on that Model affect the whole table. Operations GET and ALL on a Model return new copies of Units. A Unit maps to a database row. There can be more than one Unit per row. Operations on a Unit only affect that row. 62 | 63 | ## ORM 64 | 65 | #### Getting the Models 66 | ```javascript 67 | orm.getModels(function(err, models){ 68 | //do something with the models 69 | }); 70 | ``` 71 | 72 | #### Running arbitrary SQL 73 | ```javascript 74 | orm.execute(sql, paramsArray, function(err, results){ ... }); 75 | ``` 76 | 77 | # Model 78 | 79 | In these examples, USER is a Model. 80 | 81 | The USER.columns object contains information about the field types. 82 | 83 | #### add 84 | ```javascript 85 | USER.add({"USER_ID":5, "NAME":"JOE SMITH"}, function(err, results){ ... }); 86 | ``` 87 | 88 | #### get 89 | ```javascript 90 | // USER_ID between 5 and 20 91 | USER.get({"USER_ID":">5", "USER_ID":"<20"}, [], function(err, results){ ... }); 92 | 93 | // All users, ordered by USER_ID ascending and NAME descending 94 | USER.get({}, ["USER_ID ASC", "NAME DESC"], function(err, results){ ... }); 95 | ``` 96 | 97 | #### all 98 | ```javascript 99 | // Shortcut for .get({}, [], cb); 100 | // all() returns Units in a non-deterministic order 101 | USER.all(function(err, results){ ... }); 102 | ``` 103 | 104 | #### update 105 | ```javascript 106 | // Apply change to the whole table 107 | // Change all "JOE SMITH" with USER_ID greater than 10 to "BOB SMITH" 108 | USER.update({"NAME":"BOB SMITH"}, {"NAME":"='JOE SMITH'", "USER_ID":">10"}, function(err, results){ ... }); 109 | ``` 110 | 111 | #### del 112 | ```javascript 113 | // Delete everything with a USER_ID smaller than 5 114 | USER.del({"USER_ID":"<5"}, function(err, results){ ... }); 115 | ``` 116 | 117 | #### count 118 | ```javascript 119 | USER.count(function(err, results){ ... }); 120 | ``` 121 | 122 | #### empty 123 | ```javascript 124 | USER.empty(function(err, results){ ... }); 125 | ``` 126 | 127 | 128 | # Unit 129 | 130 | #### data 131 | user.data contains the fields and values of a Unit. 132 | 133 | #### isDirty 134 | Returns true if the object was modified 135 | 136 | #### save 137 | Writes the dirty (modified) fields to the database. 138 | 139 | #### sync 140 | Refreshes all fields with fresh values from the database. 141 | 142 | #### reset 143 | Brings all fields back to the state they were in when the Unit was created or after the last sync if sync was called at some point. 144 | 145 | ### Examples 146 | 147 | In these examples, user55 is a Unit. Suppose they are executed sequentially. 148 | 149 | ```javascript 150 | user55.isDirty(); // false 151 | user55.data.NAME = "WILLIAM CAMPBELL"; 152 | user55.isDirty(); // true 153 | user55.save(function(err, results){ ... }); 154 | user55.isDirty(); // false 155 | console.log(user55.data.NAME); // "WILLIAM CAMPBELL" 156 | 157 | user55.data.NAME = "JAMES SMITH"; 158 | user55.isDirty(); // true 159 | console.log(user55.data.NAME); // "JAMES SMITH" 160 | user55.sync(function(err, results){ ... }); 161 | console.log(user55.data.NAME); // "WILLIAM CAMPBELL" 162 | user55.isDirty(); // false 163 | 164 | user55.data.NAME = "JAMES SMITH"; 165 | user55.isDirty(); // true 166 | console.log(user55.data.NAME); // "JAMES SMITH" 167 | user55.reset(function(err, results){ ... }); 168 | console.log(user55.data.NAME); // "WILLIAM CAMPBELL" 169 | user55.isDirty(); // false 170 | 171 | 172 | user55.del(function(err, results){ ... }); // Deleted from the database 173 | user55.del(function(err, results){ ... }); // Uncaught Exception: 'Unit USER was deleted and doesn't exist anymore' 174 | ``` 175 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | returning primarys into :1 2 | validate fields 3 | fks graph -------------------------------------------------------------------------------- /driver/exports.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | module=$(pwd) 4 | pushd $module/../.. > /dev/null 5 | project=$(pwd) 6 | popd > /dev/null 7 | 8 | export OCI_HOME=$project/instantclient_12_1 9 | export OCI_LIB_DIR=$OCI_HOME 10 | export OCI_INCLUDE_DIR=$project/instantclient_12_1/sdk/include 11 | export OCI_VERSION=12 12 | export NLS_LANG=AMERICAN_AMERICA.UTF8 13 | -------------------------------------------------------------------------------- /driver/reinstall.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | module=$(pwd) 4 | 5 | if [[ $ORACLE_ORM_INSTALLED == 'TRUE' ]]; then # Stop the npm install recursion 6 | exit 7 | fi 8 | if [[ ! -d $module/../../instantclient_12_1 ]]; then 9 | echo 'Fatal error: cannot find ../../instantclient_12_1' 10 | exit -1 11 | fi 12 | 13 | source $module/driver/exports.sh 14 | 15 | pushd $OCI_LIB_DIR > /dev/null 16 | unlink libocci.so 2> /dev/null 17 | unlink libclntsh.so 2> /dev/null 18 | ln -s libocci.so.12.1 libocci.so 19 | ln -s libclntsh.so.12.1 libclntsh.so 20 | popd > /dev/null 21 | 22 | sleep 5 # Make sure sudo prompt has time to pop up 23 | echo 'oracle-orm needs sudo to set the Oracle environment variables.' 24 | echo $OCI_HOME/ | sudo tee /etc/ld.so.conf.d/oracle_instant_client.conf 25 | sudo ldconfig 26 | 27 | # Write to /etc/environment because instantclient needs it 28 | nlslang=$(cat /etc/environment | grep "^NLS_LANG=" | wc -l) 29 | if [[ $nlslang == 0 ]]; then 30 | echo 'NLS_LANG='$NLS_LANG | sudo tee -a /etc/environment > /dev/null 31 | fi 32 | 33 | export ORACLE_ORM_INSTALLED=TRUE # Stop the npm install recursion 34 | npm install # DevDependencies 35 | -------------------------------------------------------------------------------- /lib/Column.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var Column; 3 | 4 | Column = (function() { 5 | function Column(name, type, length) { 6 | this.name = name; 7 | this.type = type; 8 | this.length = length; 9 | this; 10 | } 11 | 12 | return Column; 13 | 14 | })(); 15 | 16 | module.exports = Column; 17 | 18 | }).call(this); 19 | -------------------------------------------------------------------------------- /lib/ORM.js: -------------------------------------------------------------------------------- 1 | /*** Generated by streamline 0.10.10 (callbacks) - DO NOT EDIT ***/ var __rt=require('streamline/lib/callbacks/runtime').runtime(__filename, false),__func=__rt.__func,__cb=__rt.__cb; (function() { 2 | var Column, ORM, Table, oracle, types, util, __slice = [].slice; 3 | 4 | 5 | oracle = require("oracle"); 6 | 7 | Table = require("./Table"); 8 | 9 | Column = require("./Column"); 10 | 11 | util = require("util"); 12 | 13 | types = require("./types"); 14 | 15 | ORM = (function() { 16 | function ORM(connectData, debug, _) { var instance, __this = this; var __frame = { name: "ORM", line: 16 }; return __func(_, this, arguments, ORM, 2, __frame, function __$ORM() { 17 | 18 | __this.debug = ((debug != null) ? debug : false); 19 | return oracle.connect(connectData, __cb(_, __frame, 3, 25, function ___(__0, __1) { __this.link = __1; 20 | instance = __this; 21 | __this.connection = { 22 | execute: function(sql, args, cb) { 23 | if (instance.debug) { 24 | console.log((" " + sql), args); } ; 25 | 26 | return instance.link.execute(sql, args, function() { 27 | var err, results; 28 | err = arguments[0], results = ((2 <= arguments.length) ? __slice.call(arguments, 1) : []); 29 | if ((((err != null)) && !instance.debug)) { 30 | console.log(">>>>>", sql, args); } ; 31 | 32 | return cb.apply({ }, [err,].concat(results)); }); }, 33 | 34 | 35 | driver: oracle }; _(); }, true)); }); }; 36 | 37 | 38 | 39 | ORM.prototype.getModels = function ORM_prototype_getModels__1(_) { var ret, tables, __this = this; var __frame = { name: "ORM_prototype_getModels__1", line: 39 }; return __func(_, this, arguments, ORM_prototype_getModels__1, 0, __frame, function __$ORM_prototype_getModels__1() { 40 | 41 | ret = { }; 42 | return __this.connection.execute("SELECT TABLE_NAME FROM USER_TABLES", [], __cb(_, __frame, 3, 31, function ___(__0, __1) { tables = __1; 43 | return tables.forEach_(__cb(_, __frame, 4, 13, function __$ORM_prototype_getModels__1() { 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | return _(null, ret); }, true), -1, (function(_this) { return function __1(_, table) { var columns, objColumns, primary; var __frame = { name: "__1", line: 44 }; return __func(_, this, arguments, __1, 0, __frame, function __$__1() { return _this.connection.execute("SELECT COLUMN_NAME, DATA_TYPE, DATA_LENGTH FROM USER_TAB_COLUMNS WHERE TABLE_NAME=:1 ORDER BY COLUMN_ID", [table.TABLE_NAME,], __cb(_, __frame, 2, 37, function ___(__0, __1) { columns = __1; objColumns = { }; columns.forEach(function(c) { return objColumns[c.COLUMN_NAME] = new Column(c.COLUMN_NAME, types.getType(c.DATA_TYPE), c.DATA_LENGTH); }); return _this.connection.execute(((("" + "SELECT T.CONSTRAINT_NAME AS CONSTRAINT_NAME, C.COLUMN_NAME AS COLUMN_NAME ") + "FROM ALL_CONSTRAINTS T, ALL_CONS_COLUMNS C WHERE T.OWNER = C.OWNER AND T.CONSTRAINT_NAME = C.CONSTRAINT_NAME AND ") + "T.TABLE_NAME = :1 AND T.CONSTRAINT_TYPE='P'"), [table.TABLE_NAME,], __cb(_, __frame, 7, 37, function ___(__0, __2) { primary = __2; primary = primary.map(function(c) { return objColumns[c.COLUMN_NAME]; }); return _(null, ret[table.TABLE_NAME] = new Table(_this.connection, table.TABLE_NAME, objColumns, primary)); }, true)); }, true)); }); }; })(__this)); }, true)); }); }; 59 | 60 | 61 | ORM.prototype.execute = function ORM_prototype_execute__2(sql, args, _) { var __this = this; var __frame = { name: "ORM_prototype_execute__2", line: 61 }; return __func(_, this, arguments, ORM_prototype_execute__2, 2, __frame, function __$ORM_prototype_execute__2() { 62 | return __this.connection.execute(sql, args, __cb(_, __frame, 1, 29, _, true)); }); }; 63 | 64 | 65 | ORM.prototype.disconnect = function() { 66 | return this.link.close(); }; 67 | 68 | 69 | return ORM; 70 | 71 | })(); 72 | 73 | module.exports = ORM; 74 | 75 | }).call(this); 76 | -------------------------------------------------------------------------------- /lib/Query.js: -------------------------------------------------------------------------------- 1 | /*** Generated by streamline 0.10.10 (callbacks) - DO NOT EDIT ***/ var __rt=require('streamline/lib/callbacks/runtime').runtime(__filename, false),__func=__rt.__func,__cb=__rt.__cb; (function() { 2 | var Query, Row, getOrderBy, helpers, types; 3 | 4 | Row = require("./Row"); 5 | 6 | types = require("./types"); 7 | 8 | helpers = require("./helpers"); 9 | 10 | getOrderBy = function(orderBy) { 11 | if ((((orderBy != null)) && (orderBy.length > 0))) { 12 | orderBy = orderBy.map(function(a) { 13 | if ((a[0] === "-")) { 14 | return (("\"" + a.slice(1)) + "\" DESC"); } 15 | else { 16 | return (("\"" + a) + "\" ASC"); } ; }); 17 | 18 | 19 | return ("ORDER BY " + orderBy.join(", ")); } 20 | else { 21 | return ""; } ; }; 22 | 23 | 24 | 25 | Query = (function() { 26 | function Query(connection, table, safe) { 27 | this.connection = connection; 28 | this.table = table; 29 | if ((safe == null)) { 30 | safe = true; } ; 31 | 32 | this.getWhere = (safe ? function(obj) { 33 | return helpers.getWhere(obj, "="); 34 | } : helpers.getWhereNoPlaceholders); }; 35 | 36 | 37 | Query.prototype.select = function Query_prototype_select__1(where, orderBy, _) { var order, values, _ref, __this = this; var __frame = { name: "Query_prototype_select__1", line: 37 }; return __func(_, this, arguments, Query_prototype_select__1, 2, __frame, function __$Query_prototype_select__1() { 38 | 39 | order = getOrderBy(orderBy); 40 | _ref = __this.getWhere(where), where = _ref[0], values = _ref[1]; 41 | return (__this.connection.execute(((((("SELECT * FROM \"" + __this.table.name) + "\" WHERE ") + where) + " ") + order), values, __cb(_, __frame, 4, 30, function ___(__0, __2) { var __1 = __2.map((function(_this) { 42 | return function(line) { 43 | return new Row(_this.connection, _this.table, line); }; 44 | 45 | })(__this)); return _(null, __1); }, true))); }); }; 46 | 47 | 48 | Query.prototype.update = function Query_prototype_update__2(pairs, where, _) { var update, values1, values2, _ref, _ref1, __this = this; var __frame = { name: "Query_prototype_update__2", line: 48 }; return __func(_, this, arguments, Query_prototype_update__2, 2, __frame, function __$Query_prototype_update__2() { 49 | 50 | _ref = helpers.getWhere(pairs, "="), update = _ref[0], values1 = _ref[1]; 51 | _ref1 = __this.getWhere(where), where = _ref1[0], values2 = _ref1[1]; 52 | return __this.connection.execute(((((("UPDATE \"" + __this.table.name) + "\" SET ") + update.join(", ")) + " WHERE ") + where.join(" AND ")), values1.concat(values2), __cb(_, __frame, 4, 29, _, true)); }); }; 53 | 54 | 55 | Query.prototype.insert = function Query_prototype_insert__3(pairs, _) { var col, columns, i, params, placeholders, primaries, returnPlaceholders, values, __this = this; var __frame = { name: "Query_prototype_insert__3", line: 55 }; return __func(_, this, arguments, Query_prototype_insert__3, 1, __frame, function __$Query_prototype_insert__3() { 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | return (function ___closure(_) { var _i, _len, _ref, _results; _ref = Object.keys(pairs); _results = []; for (_i = 0, _len = _ref.length; (_i < _len); _i++) { col = _ref[_i]; _results.push((("\"" + col) + "\"")); }; return _(null, _results); })(__cb(_, __frame, 2, 16, function ___(__0, __1) { columns = __1; 67 | values = helpers.getValues(pairs); 68 | placeholders = (helpers.getPlaceholders(values.length)).join(", "); 69 | primaries = __this.table.primary.map(function(a) { 70 | return a.name; 71 | }).join(", "); 72 | returnPlaceholders = (helpers.getPlaceholders(__this.table.primary.length)).join(", "); 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | return (function ___closure(_) { var _i, _len, _ref, _results; _ref = __this.table.primary; _results = []; for (_i = 0, _len = _ref.length; (_i < _len); _i++) { i = _ref[_i]; _results.push(new __this.connection.driver.OutParam(types.typeToOCCI[i.type])); }; return _(null, _results); })(__cb(_, __frame, 18, 33, function ___(__0, __2) { params = [].concat(values, __2); 83 | return __this.connection.execute(((((((((("INSERT INTO \"" + __this.table.name) + "\"(") + columns.join(", ")) + ") VALUES(") + placeholders) + ") RETURNING ") + primaries) + " INTO ") + returnPlaceholders), params, __cb(_, __frame, 28, 29, _, true)); }, true)); }, true)); }); }; 84 | 85 | 86 | Query.prototype.del = function Query_prototype_del__4(where, _) { var values, _ref, __this = this; var __frame = { name: "Query_prototype_del__4", line: 86 }; return __func(_, this, arguments, Query_prototype_del__4, 1, __frame, function __$Query_prototype_del__4() { 87 | 88 | _ref = __this.getWhere(where), where = _ref[0], values = _ref[1]; 89 | return __this.connection.execute(((("DELETE FROM \"" + __this.table.name) + "\" WHERE ") + where.join(" AND ")), values, __cb(_, __frame, 3, 29, _, true)); }); }; 90 | 91 | 92 | return Query; 93 | 94 | })(); 95 | 96 | module.exports = Query; 97 | 98 | }).call(this); 99 | -------------------------------------------------------------------------------- /lib/Row.js: -------------------------------------------------------------------------------- 1 | /*** Generated by streamline 0.10.10 (callbacks) - DO NOT EDIT ***/ var __rt=require('streamline/lib/callbacks/runtime').runtime(__filename, false),__func=__rt.__func,__cb=__rt.__cb; (function() { 2 | var Row, helpers, __hasProp = { 3 | }.hasOwnProperty; 4 | 5 | helpers = require("./helpers"); 6 | 7 | Row = (function() { 8 | function Row(connection, table, data) { 9 | this.connection = connection; 10 | this.table = table; 11 | this.data = data; 12 | this.deleted = false; 13 | this.makeBackData(); }; 14 | 15 | 16 | Row.prototype.makeBackData = function() { 17 | var k, v, _ref; 18 | this.backdata = { }; 19 | _ref = this.data; 20 | for (k in _ref) { 21 | if (!__hasProp.call(_ref, k)) { continue; }; 22 | v = _ref[k]; 23 | this.backdata[k] = v; }; 24 | 25 | return this.backdata; }; 26 | 27 | 28 | Row.prototype.getDirty = function() { 29 | var dirty, k, v, _ref; 30 | dirty = { }; 31 | _ref = this.backdata; 32 | for (k in _ref) { 33 | v = _ref[k]; 34 | if ((v !== this.data[k])) { 35 | dirty[k] = this.data[k]; } ; }; 36 | 37 | 38 | return dirty; }; 39 | 40 | 41 | Row.prototype.isDirty = function() { 42 | return (Object.keys(this.getDirty()).length > 0); }; 43 | 44 | 45 | Row.prototype.save = function Row_prototype_save__1(_) { var dirty, r, update, values1, values2, where, _ref, _ref1, __this = this; var __frame = { name: "Row_prototype_save__1", line: 45 }; return __func(_, this, arguments, Row_prototype_save__1, 0, __frame, function __$Row_prototype_save__1() { 46 | 47 | if (__this.deleted) { 48 | return _(new Error((("Unit " + __this.table.name) + " was deleted and doesn't exist anymore"))); } ; 49 | 50 | dirty = __this.getDirty(); 51 | if ((Object.keys(dirty).length === 0)) { 52 | return _(null, []); } ; 53 | 54 | _ref = helpers.getWhere(dirty, "="), update = _ref[0], values1 = _ref[1]; 55 | _ref1 = helpers.getWhere(helpers.getPKPairs(__this), "="), where = _ref1[0], values2 = _ref1[1]; 56 | return __this.connection.execute(((((("UPDATE \"" + __this.table.name) + "\" SET ") + update.join(", ")) + " WHERE ") + where.join(" AND ")), values1.concat(values2), __cb(_, __frame, 11, 26, function ___(__0, __1) { r = __1; 57 | if ((r.updateCount === 0)) { 58 | __this.deleted = true; 59 | __this.data = { }; 60 | return _(new Error((("Unit " + __this.table.name) + " was deleted and doesn't exist anymore"))); } 61 | else { 62 | __this.makeBackData(); } ; return _(null, __this); }, true)); }); }; 63 | 64 | 65 | 66 | 67 | Row.prototype.sync = function Row_prototype_sync__2(_) { var r, values, where, _ref, __this = this; var __frame = { name: "Row_prototype_sync__2", line: 67 }; return __func(_, this, arguments, Row_prototype_sync__2, 0, __frame, function __$Row_prototype_sync__2() { 68 | 69 | if (__this.deleted) { 70 | return _(new Error((("Unit " + __this.table.name) + " was deleted and doesn't exist anymore"))); } ; 71 | 72 | _ref = helpers.getWhere(helpers.getPKPairs(__this), "="), where = _ref[0], values = _ref[1]; 73 | return __this.connection.execute(((("SELECT * FROM \"" + __this.table.name) + "\" WHERE ") + where.join(" AND ")), values, __cb(_, __frame, 6, 26, function ___(__0, __1) { r = __1; 74 | if ((r.length === 0)) { 75 | __this.deleted = true; 76 | __this.data = { }; 77 | return _(new Error((("Unit " + __this.table.name) + " was deleted and doesn't exist anymore"))); } 78 | else { 79 | __this.data = r[0]; } ; 80 | 81 | __this.makeBackData(); return _(null, __this); }, true)); }); }; 82 | 83 | 84 | 85 | Row.prototype.reset = function Row_prototype_reset__3(_) { var __this = this; var __frame = { name: "Row_prototype_reset__3", line: 85 }; return __func(_, this, arguments, Row_prototype_reset__3, 0, __frame, function __$Row_prototype_reset__3() { 86 | __this.data = __this.backdata; 87 | __this.makeBackData(); 88 | return _(null, __this.data); }); }; 89 | 90 | 91 | Row.prototype.del = function Row_prototype_del__4(_) { var values, where, _ref, __this = this; var __frame = { name: "Row_prototype_del__4", line: 91 }; return __func(_, this, arguments, Row_prototype_del__4, 0, __frame, function __$Row_prototype_del__4() { 92 | 93 | if (__this.deleted) { 94 | return _(new Error((("Unit " + __this.table.name) + " was deleted and doesn't exist anymore"))); } ; 95 | 96 | _ref = helpers.getWhere(helpers.getPKPairs(__this), "="), where = _ref[0], values = _ref[1]; 97 | __this.deleted = true; 98 | return __this.connection.execute(((("DELETE FROM \"" + __this.table.name) + "\" WHERE ") + where.join(" AND ")), values, __cb(_, __frame, 7, 29, _, true)); }); }; 99 | 100 | 101 | return Row; 102 | 103 | })(); 104 | 105 | module.exports = Row; 106 | 107 | }).call(this); 108 | -------------------------------------------------------------------------------- /lib/Table.js: -------------------------------------------------------------------------------- 1 | /*** Generated by streamline 0.10.10 (callbacks) - DO NOT EDIT ***/ var __rt=require('streamline/lib/callbacks/runtime').runtime(__filename, false),__func=__rt.__func,__cb=__rt.__cb; (function() { 2 | var Query, Row, Table; 3 | 4 | Row = require("./Row"); 5 | 6 | Query = require("./Query"); 7 | 8 | Table = (function() { 9 | function Table(connection, name, columns, primary) { 10 | this.connection = connection; 11 | this.name = name; 12 | this.columns = columns; 13 | this.primary = ((primary != null) ? primary : []); }; 14 | 15 | 16 | Table.prototype.validateColummns = function(columnNames) { 17 | var invalids; 18 | if ((columnNames == null)) { 19 | columnNames = []; } ; 20 | 21 | invalids = columnNames.map((function(_this) { 22 | return function(c) { 23 | if ((_this.columns[c] == null)) { 24 | return c; } 25 | else { 26 | return null; } ; }; 27 | 28 | 29 | })(this)).filter(function(a) { 30 | return (a != null); }); 31 | 32 | if (!((invalids.length === 0))) { 33 | throw new Error(("Invalid column(s): " + invalids.join(", "))); } ; }; 34 | 35 | 36 | 37 | Table.prototype.add = function Table_prototype_add__1(pairs, _) { var query, __this = this; var __frame = { name: "Table_prototype_add__1", line: 37 }; return __func(_, this, arguments, Table_prototype_add__1, 1, __frame, function __$Table_prototype_add__1() { 38 | 39 | __this.validateColummns(Object.keys(pairs)); 40 | query = new Query(__this.connection, __this); 41 | return query.insert(pairs, __cb(_, __frame, 4, 19, _, true)); }); }; 42 | 43 | 44 | Table.prototype.get = function Table_prototype_get__2(where, orderBy, _) { var query, __this = this; var __frame = { name: "Table_prototype_get__2", line: 44 }; return __func(_, this, arguments, Table_prototype_get__2, 2, __frame, function __$Table_prototype_get__2() { 45 | 46 | if ((where == null)) { 47 | where = { }; } ; 48 | 49 | if ((orderBy == null)) { 50 | orderBy = null; } ; 51 | 52 | __this.validateColummns(Object.keys(where)); 53 | __this.validateColummns(((orderBy != null) ? orderBy.map(function(a) { 54 | if ((a[0] === "-")) { 55 | return a.slice(1); } 56 | else { 57 | return a; } ; 58 | 59 | }) : void 0)); 60 | query = new Query(__this.connection, __this); 61 | return query.select(where, orderBy, __cb(_, __frame, 17, 19, _, true)); }); }; 62 | 63 | 64 | Table.prototype.getUnsafe = function Table_prototype_getUnsafe__3(where, orderBy, _) { var query, __this = this; var __frame = { name: "Table_prototype_getUnsafe__3", line: 64 }; return __func(_, this, arguments, Table_prototype_getUnsafe__3, 2, __frame, function __$Table_prototype_getUnsafe__3() { 65 | 66 | if ((where == null)) { 67 | where = { }; } ; 68 | 69 | if ((orderBy == null)) { 70 | orderBy = null; } ; 71 | 72 | __this.validateColummns(Object.keys(where)); 73 | __this.validateColummns(((orderBy != null) ? orderBy.map(function(a) { 74 | if ((a[0] === "-")) { 75 | return a.slice(1); } 76 | else { 77 | return a; } ; 78 | 79 | }) : void 0)); 80 | query = new Query(__this.connection, __this, false); 81 | return query.select(where, orderBy, __cb(_, __frame, 17, 19, _, true)); }); }; 82 | 83 | 84 | Table.prototype.all = function Table_prototype_all__4(_) { var __this = this; var __frame = { name: "Table_prototype_all__4", line: 84 }; return __func(_, this, arguments, Table_prototype_all__4, 0, __frame, function __$Table_prototype_all__4() { 85 | return __this.get(null, null, __cb(_, __frame, 1, 18, _, true)); }); }; 86 | 87 | 88 | Table.prototype.update = function Table_prototype_update__5(columns, where, _) { var query, __this = this; var __frame = { name: "Table_prototype_update__5", line: 88 }; return __func(_, this, arguments, Table_prototype_update__5, 2, __frame, function __$Table_prototype_update__5() { 89 | 90 | if ((columns == null)) { 91 | columns = { }; } ; 92 | 93 | if ((where == null)) { 94 | where = { }; } ; 95 | 96 | __this.validateColummns(Object.keys(columns)); 97 | __this.validateColummns(Object.keys(where)); 98 | query = new Query(__this.connection, __this); 99 | return query.update(columns, where, __cb(_, __frame, 11, 19, _, true)); }); }; 100 | 101 | 102 | Table.prototype.updateUnsafe = function Table_prototype_updateUnsafe__6(columns, where, _) { var query, __this = this; var __frame = { name: "Table_prototype_updateUnsafe__6", line: 102 }; return __func(_, this, arguments, Table_prototype_updateUnsafe__6, 2, __frame, function __$Table_prototype_updateUnsafe__6() { 103 | 104 | if ((columns == null)) { 105 | columns = { }; } ; 106 | 107 | if ((where == null)) { 108 | where = { }; } ; 109 | 110 | __this.validateColummns(Object.keys(columns)); 111 | __this.validateColummns(Object.keys(where)); 112 | query = new Query(__this.connection, __this, false); 113 | return query.update(columns, where, __cb(_, __frame, 11, 19, _, true)); }); }; 114 | 115 | 116 | Table.prototype.del = function Table_prototype_del__7(where, _) { var query, __this = this; var __frame = { name: "Table_prototype_del__7", line: 116 }; return __func(_, this, arguments, Table_prototype_del__7, 1, __frame, function __$Table_prototype_del__7() { 117 | 118 | if ((where == null)) { 119 | where = { }; } ; 120 | 121 | __this.validateColummns(Object.keys(where)); 122 | query = new Query(__this.connection, __this); 123 | return query.del(where, __cb(_, __frame, 7, 19, _, true)); }); }; 124 | 125 | 126 | Table.prototype.delUnsafe = function Table_prototype_delUnsafe__8(where, _) { var query, __this = this; var __frame = { name: "Table_prototype_delUnsafe__8", line: 126 }; return __func(_, this, arguments, Table_prototype_delUnsafe__8, 1, __frame, function __$Table_prototype_delUnsafe__8() { 127 | 128 | if ((where == null)) { 129 | where = { }; } ; 130 | 131 | __this.validateColummns(Object.keys(where)); 132 | query = new Query(__this.connection, __this, false); 133 | return query.del(where, __cb(_, __frame, 7, 19, _, true)); }); }; 134 | 135 | 136 | Table.prototype.count = function Table_prototype_count__9(_) { var r, __this = this; var __frame = { name: "Table_prototype_count__9", line: 136 }; return __func(_, this, arguments, Table_prototype_count__9, 0, __frame, function __$Table_prototype_count__9() { 137 | 138 | return __this.connection.execute((("SELECT COUNT(*) AS C FROM \"" + __this.name) + "\""), [], __cb(_, __frame, 2, 26, function ___(__0, __1) { r = __1; 139 | return _(null, r[0].C); }, true)); }); }; 140 | 141 | 142 | Table.prototype.empty = function Table_prototype_empty__10(_) { var __this = this; var __frame = { name: "Table_prototype_empty__10", line: 142 }; return __func(_, this, arguments, Table_prototype_empty__10, 0, __frame, function __$Table_prototype_empty__10() { 143 | return __this.connection.execute((("TRUNCATE TABLE \"" + __this.name) + "\""), [], __cb(_, __frame, 1, 29, _, true)); }); }; 144 | 145 | 146 | return Table; 147 | 148 | })(); 149 | 150 | module.exports = Table; 151 | 152 | }).call(this); 153 | -------------------------------------------------------------------------------- /lib/helpers.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var getListNoPlaceholders, getListPlaceholders, getPKPairs, getPlaceholders, getValues, getWhere, getWhereNoPlaceholders, placeCounter; 3 | 4 | placeCounter = 0; 5 | 6 | getPlaceholders = function(nb) { 7 | var i, _i, _results; 8 | _results = []; 9 | for (i = _i = 1; 1 <= nb ? _i <= nb : _i >= nb; i = 1 <= nb ? ++_i : --_i) { 10 | _results.push(":" + (placeCounter++ % 100)); 11 | } 12 | return _results; 13 | }; 14 | 15 | getWhere = function(pairs, separator) { 16 | var values, where; 17 | if (separator == null) { 18 | separator = ""; 19 | } 20 | where = getListPlaceholders(pairs, separator); 21 | values = getValues(pairs); 22 | return [where, values]; 23 | }; 24 | 25 | getWhereNoPlaceholders = function(pairs, separator) { 26 | var where; 27 | if (separator == null) { 28 | separator = ""; 29 | } 30 | where = getListNoPlaceholders(pairs, separator); 31 | return [where, []]; 32 | }; 33 | 34 | getListPlaceholders = function(obj, separator) { 35 | if (separator == null) { 36 | separator = "="; 37 | } 38 | if (Object.keys(obj).length === 0) { 39 | return ["1=1"]; 40 | } else { 41 | return Object.keys(obj).map(function(k) { 42 | return "\"" + k + "\"" + separator + (getPlaceholders(1)); 43 | }); 44 | } 45 | }; 46 | 47 | getListNoPlaceholders = function(obj, separator) { 48 | if (separator == null) { 49 | separator = "="; 50 | } 51 | if (Object.keys(obj).length === 0) { 52 | return ["1=1"]; 53 | } else { 54 | return Object.keys(obj).map(function(k) { 55 | return "\"" + k + "\"" + separator + obj[k]; 56 | }); 57 | } 58 | }; 59 | 60 | getValues = function(obj) { 61 | return Object.keys(obj).map(function(a) { 62 | return obj[a]; 63 | }); 64 | }; 65 | 66 | getPKPairs = function(row) { 67 | var pairs; 68 | pairs = {}; 69 | row.table.primary.forEach(function(c) { 70 | return pairs[c.name] = row.backdata[c.name]; 71 | }); 72 | return pairs; 73 | }; 74 | 75 | module.exports = { 76 | getPlaceholders: getPlaceholders, 77 | getWhere: getWhere, 78 | getWhereNoPlaceholders: getWhereNoPlaceholders, 79 | getValues: getValues, 80 | getPKPairs: getPKPairs 81 | }; 82 | 83 | }).call(this); 84 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var ORM; 3 | 4 | ORM = require("./ORM"); 5 | 6 | module.exports = ORM; 7 | 8 | }).call(this); 9 | -------------------------------------------------------------------------------- /lib/types.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var getType, oracle, typeFromOracle, typeToOCCI, types; 3 | 4 | oracle = require("oracle"); 5 | 6 | types = { 7 | STRING: 0, 8 | NUMBER: 1, 9 | DATE: 2 10 | }; 11 | 12 | typeFromOracle = { 13 | "VARCHAR2": types.STRING, 14 | "CHAR": types.STRING, 15 | "CLOB": types.STRING, 16 | "NUMBER": types.NUMBER, 17 | "DATE": types.DATE 18 | }; 19 | 20 | typeToOCCI = [oracle.OCCISTRING, oracle.OCCINUMBER]; 21 | 22 | getType = function(str) { 23 | if (typeFromOracle[str] == null) { 24 | throw new Error("Invalid type: " + str); 25 | } 26 | return typeFromOracle[str]; 27 | }; 28 | 29 | module.exports = { 30 | types: types, 31 | getType: getType, 32 | typeToOCCI: typeToOCCI 33 | }; 34 | 35 | }).call(this); 36 | -------------------------------------------------------------------------------- /lib/unittests.js: -------------------------------------------------------------------------------- 1 | /*** Generated by streamline 0.10.10 (callbacks) - DO NOT EDIT ***/ var __rt=require('streamline/lib/callbacks/runtime').runtime(__filename, false),__func=__rt.__func,__cb=__rt.__cb,__trap=__rt.__trap,__catch=__rt.__catch,__tryCatch=__rt.__tryCatch,__construct=__rt.__construct; (function main(_) { var __this = this; var __frame = { name: "main", line: 1 }; return __func(_, this, arguments, main, 0, __frame, function __$main() { 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | return (function ___closure(_) { var ORM, apple, assert, dbParams, dirty, e, err, fs, models, ok, oracleConnectData, orders, orm, rows, sessionParams, utfData, utfString, util, _ref, _ref1; util = require("util"); fs = require("fs"); global.con = function(v) { return util.puts(util.inspect(v)); }; global.stack = function(err) { util.puts(("__" + err.message)); return util.puts(err.stack); }; assert = function(a, b, number) { if ((a === b)) { return console.log((((((">" + number) + " OK ") + a) + " === ") + b)); } else { console.log((((((">" + number) + " FAILED!! ") + a) + " !== ") + b)); throw new Error(("Test " + number)); } ; }; oracleConnectData = JSON.parse((fs.readFileSync((__dirname + "/../testDB.json"))).toString("utf8")); ORM = require("oracle-orm"); return (function ___(__then) { (function ___(_) { __tryCatch(_, function __$___closure() { return __construct(ORM, 2)(oracleConnectData, true, __cb(_, __frame, NaN, NaN, function ___(__0, __3) { orm = __3; return (function ___(__then) { (function ___(_) { __tryCatch(_, function __$___closure() { return orm.execute("DROP TABLE PERSON", [], __cb(_, __frame, 32, 10, __then, true)); }); })(function ___(_error, __result) { __catch(function __$___closure() { if (_error) { err = _error; __then(); } else { _(null, __result); } ; }); }); })(function ___() { __tryCatch(_, function __$___closure() { return (function ___(__then) { (function ___(_) { __tryCatch(_, function __$___closure() { return orm.execute("DROP TABLE \"ORDER\"", [], __cb(_, __frame, 37, 10, __then, true)); }); })(function ___(_error, __result) { __catch(function __$___closure() { if (_error) { err = _error; __then(); } else { _(null, __result); } ; }); }); })(function ___() { __tryCatch(_, function __$___closure() { return orm.execute("CREATE TABLE PERSON (AAA NUMBER, BBB VARCHAR2(50), F_ORDER_ID NUMBER)", [], __cb(_, __frame, 41, 8, function __$___closure() { return orm.execute((("ALTER TABLE \"" + oracleConnectData.user) + "\".\"PERSON\" ADD CONSTRAINT PK_PERSON PRIMARY KEY(\"AAA\",\"F_ORDER_ID\")"), [], __cb(_, __frame, 42, 8, function __$___closure() { return orm.execute("CREATE TABLE \"ORDER\" (ORDER_ID NUMBER, \"NAME\" VARCHAR2(50), NOTE VARCHAR2(50))", [], __cb(_, __frame, 43, 8, function __$___closure() { return orm.execute((("ALTER TABLE \"" + oracleConnectData.user) + "\".\"ORDER\" ADD CONSTRAINT PK_ORDER PRIMARY KEY(\"ORDER_ID\")"), [], __cb(_, __frame, 44, 8, function __$___closure() { return orm.execute((("ALTER TABLE \"" + oracleConnectData.user) + "\".\"PERSON\" ADD CONSTRAINT FK_ORDER FOREIGN KEY(\"F_ORDER_ID\") REFERENCES \"ORDER\"(\"ORDER_ID\")"), [], __cb(_, __frame, 45, 8, function __$___closure() { return orm.getModels(__cb(_, __frame, 46, 17, function ___(__0, __4) { models = __4; return models.PERSON.count(__cb(_, __frame, 47, 25, function ___(__0, __5) { assert(__5, 0, "1"); return models.ORDER.add({ ORDER_ID: 1, NAME: "avocado" }, __cb(_, __frame, 48, 17, function __$___closure() { return models.ORDER.add({ ORDER_ID: 2, NAME: "banana" }, __cb(_, __frame, 52, 17, function __$___closure() { return models.ORDER.add({ ORDER_ID: 3, NAME: "coconut" }, __cb(_, __frame, 56, 17, function __$___closure() { return models.ORDER.add({ ORDER_ID: 4, NAME: "date" }, __cb(_, __frame, 60, 17, function __$___closure() { return models.ORDER.add({ ORDER_ID: 5, NAME: "eggplant" }, __cb(_, __frame, 64, 17, function __$___closure() { return models.ORDER.add({ ORDER_ID: 6, NAME: "fig" }, __cb(_, __frame, 68, 17, function __$___closure() { return models.ORDER.add({ ORDER_ID: 7, NAME: "grapefruit" }, __cb(_, __frame, 72, 17, function __$___closure() { return models.PERSON.add({ AAA: 10, BBB: "abc", F_ORDER_ID: 1 }, __cb(_, __frame, 76, 18, function __$___closure() { return models.PERSON.add({ AAA: 20, BBB: "xyz", F_ORDER_ID: 2 }, __cb(_, __frame, 81, 18, function __$___closure() { return models.PERSON.add({ AAA: 30, BBB: "def", F_ORDER_ID: 3 }, __cb(_, __frame, 86, 18, function __$___closure() { return models.PERSON.add({ AAA: 40, BBB: "def", F_ORDER_ID: 3 }, __cb(_, __frame, 91, 18, function __$___closure() { return models.PERSON.add({ AAA: 50, F_ORDER_ID: 5 }, __cb(_, __frame, 96, 18, function __$___closure() { return models.PERSON.count(__cb(_, __frame, 100, 25, function ___(__0, __6) { assert(__6, 5, "2"); return models.PERSON.all(__cb(_, __frame, 101, 25, function ___(__0, __7) { rows = __7; assert(rows.length, 5, "3"); return (function ___(__then) { (function ___(_) { __tryCatch(_, function __$___closure() { return (function ___(__then) { (function ___(_) { __tryCatch(_, function __$___closure() { ok = false; return models.PERSON({ AAA: ">35" }, ["AAA",], __cb(_, __frame, 105, 20, function ___(__0, __8) { rows = __8; __then(); }, true)); }); })(function ___(_error, __result) { __catch(function __$___closure() { if (_error) { e = _error; ok = true; __then(); } else { _(null, __result); } ; }); }); })(function ___() { __tryCatch(_, function __$___closure() { _(null, null, true); }); }); }); })(function ___(__e, __r, __cont) { (function ___(__then) { __tryCatch(_, function __$___closure() { assert(ok, true, "3.1"); __then(); }); })(function ___() { __tryCatch(_, function ___() { if (__cont) { __then(); } else { _(__e, __r); }; }); }); }); })(function ___() { __tryCatch(_, function __$___closure() { return models.PERSON.getUnsafe({ AAA: ">35" }, ["AAA",], __cb(_, __frame, 114, 25, function ___(__0, __9) { rows = __9; assert(rows.length, 2, "4"); assert((((((_ref = rows[0].connection) != null) ? _ref.execute : void 0)) != null), true, "5"); assert(rows[0].table.name, "PERSON", "6"); assert(rows[0].data.AAA, 40, "7"); assert(rows[0].data.BBB, "def", "8"); assert(rows[1].data.AAA, 50, "9"); assert(rows[1].data.BBB, null, "10"); return (function ___(__then) { (function ___(_) { __tryCatch(_, function __$___closure() { return (function ___(__then) { (function ___(_) { __tryCatch(_, function __$___closure() { ok = false; return models.PERSON.updateUnsafe({ AAA: -100 }, { BBB: "abc" }, __cb(_, __frame, 126, 20, __then, true)); }); })(function ___(_error, __result) { __catch(function __$___closure() { if (_error) { e = _error; ok = true; __then(); } else { _(null, __result); } ; }); }); })(function ___() { __tryCatch(_, function __$___closure() { _(null, null, true); }); }); }); })(function ___(__e, __r, __cont) { (function ___(__then) { __tryCatch(_, function __$___closure() { assert(ok, true, "10.1"); __then(); }); })(function ___() { __tryCatch(_, function ___() { if (__cont) { __then(); } else { _(__e, __r); }; }); }); }); })(function ___() { __tryCatch(_, function __$___closure() { return models.PERSON.update({ AAA: -100 }, { BBB: "abc" }, __cb(_, __frame, 137, 18, function __$___closure() { return models.PERSON.get({ }, ["F_ORDER_ID","AAA",], __cb(_, __frame, 142, 25, function ___(__0, __10) { rows = __10; assert(rows[0].data.AAA, -100, "11"); assert(rows[0].deleted, false, "12"); return rows[0].del(__cb(_, __frame, 145, 12, function __$___closure() { assert(rows[0].deleted, true, "13"); return (function ___(__then) { (function ___(_) { __tryCatch(_, function __$___closure() { return (function ___(__then) { (function ___(_) { __tryCatch(_, function __$___closure() { ok = false; return rows[0].del(__cb(_, __frame, 149, 14, __then, true)); }); })(function ___(_error, __result) { __catch(function __$___closure() { if (_error) { e = _error; ok = true; __then(); } else { _(null, __result); } ; }); }); })(function ___() { __tryCatch(_, function __$___closure() { _(null, null, true); }); }); }); })(function ___(__e, __r, __cont) { (function ___(__then) { __tryCatch(_, function __$___closure() { assert(ok, true, "14"); __then(); }); })(function ___() { __tryCatch(_, function ___() { if (__cont) { __then(); } else { _(__e, __r); }; }); }); }); })(function ___() { __tryCatch(_, function __$___closure() { return models.PERSON.all(__cb(_, __frame, 156, 25, function ___(__0, __11) { rows = __11; return models.PERSON.count(__cb(_, __frame, 157, 25, function ___(__0, __12) { assert(__12, 4, "15"); return models.PERSON.get({ }, ["AAA",], __cb(_, __frame, 158, 25, function ___(__0, __13) { rows = __13; assert(rows[1].backdata.AAA, rows[1].data.AAA, "16"); rows[1].data.AAA++; dirty = rows[1].getDirty(); assert(Object.keys(dirty).length, 1, "17"); assert(rows[1].isDirty(), true, "18"); assert(dirty.AAA, 31, "19"); return rows[1].save(__cb(_, __frame, 165, 12, function __$___closure() { dirty = rows[1].getDirty(); assert(Object.keys(dirty).length, 0, "20"); assert(rows[1].isDirty(), false, "21"); rows[2].data.BBB = "xyz"; dirty = rows[2].getDirty(); assert(Object.keys(dirty).length, 1, "22"); assert(rows[2].isDirty(), true, "23"); return rows[2].sync(__cb(_, __frame, 173, 12, function __$___closure() { dirty = rows[2].getDirty(); assert(Object.keys(dirty).length, 0, "25"); assert(rows[2].isDirty(), false, "26"); return rows[2].save(__cb(_, __frame, 177, 12, function __$___closure() { rows[2].data.BBB = "xyz"; assert(rows[2].isDirty(), true, "27"); return rows[2].reset(__cb(_, __frame, 180, 12, function __$___closure() { assert(rows[2].data.BBB, "def", "28"); assert(rows[2].isDirty(), false, "29"); return models.ORDER.get({ }, ["-NAME",], __cb(_, __frame, 183, 26, function ___(__0, __14) { orders = __14; assert(orders[0].data.NAME, "grapefruit", "30"); return (function ___(__then) { (function ___(_) { __tryCatch(_, function __$___closure() { return (function ___(__then) { (function ___(_) { __tryCatch(_, function __$___closure() { ok = false; return models.ORDER.update({ NOTE: "nothing" }, { ORDER_ID: ">=3" }, __cb(_, __frame, 187, 19, __then, true)); }); })(function ___(_error, __result) { __catch(function __$___closure() { if (_error) { e = _error; ok = true; __then(); } else { _(null, __result); } ; }); }); })(function ___() { __tryCatch(_, function __$___closure() { _(null, null, true); }); }); }); })(function ___(__e, __r, __cont) { (function ___(__then) { __tryCatch(_, function __$___closure() { assert(ok, true, "30.1"); __then(); }); })(function ___() { __tryCatch(_, function ___() { if (__cont) { __then(); } else { _(__e, __r); }; }); }); }); })(function ___() { __tryCatch(_, function __$___closure() { return models.ORDER.updateUnsafe({ NOTE: "nothing" }, { ORDER_ID: ">=3" }, __cb(_, __frame, 198, 17, function __$___closure() { assert((orders.filter(function(a) { return (a.data.NOTE === null); })).length, 7, "31"); return orders.forEach_(__cb(_, __frame, 206, 11, function __$___closure() { assert((orders.filter(function(a) { return (a.data.NOTE === null); })).length, 2, "32"); return models.ORDER.count(__cb(_, __frame, 212, 24, function ___(__0, __15) { assert(__15, 7, "33"); return (function ___(__then) { (function ___(_) { __tryCatch(_, function __$___closure() { return (function ___(__then) { (function ___(_) { __tryCatch(_, function __$___closure() { ok = false; return models.ORDER.del({ ORDER_ID: "=4" }, __cb(_, __frame, 215, 19, __then, true)); }); })(function ___(_error, __result) { __catch(function __$___closure() { if (_error) { e = _error; ok = true; __then(); } else { _(null, __result); } ; }); }); })(function ___() { __tryCatch(_, function __$___closure() { _(null, null, true); }); }); }); })(function ___(__e, __r, __cont) { (function ___(__then) { __tryCatch(_, function __$___closure() { assert(ok, true, "33.1"); __then(); }); })(function ___() { __tryCatch(_, function ___() { if (__cont) { __then(); } else { _(__e, __r); }; }); }); }); })(function ___() { __tryCatch(_, function __$___closure() { return models.ORDER.del({ ORDER_ID: "4" }, __cb(_, __frame, 224, 17, function __$___closure() { return models.ORDER.count(__cb(_, __frame, 227, 24, function ___(__0, __16) { assert(__16, 6, "34"); return orders.map_(__cb(_, __frame, 228, 20, function ___(__0, __17) { orders = __17; assert(orders[4].isDirty(), false, "35"); orders[4].data.NOTE = "APPLE"; assert(orders[4].isDirty(), true, "36"); return orders[4].save(__cb(_, __frame, 238, 14, function __$___closure() { return orm.execute("SELECT * FROM \"ORDER\" WHERE \"ORDER_ID\" = 3", [], __cb(_, __frame, 239, 16, function ___(__0, __18) { apple = __18; assert(apple[0].NOTE, "APPLE", "36.1"); assert(orders[4].isDirty(), false, "37"); return orm.execute("DELETE FROM \"ORDER\" WHERE \"ORDER_ID\"=7", [], __cb(_, __frame, 242, 8, function __$___closure() { orders[0].data.NOTE = "OOPS"; return (function ___(__then) { (function ___(_) { __tryCatch(_, function __$___closure() { return (function ___(__then) { (function ___(_) { __tryCatch(_, function __$___closure() { ok = false; return orders[0].save(__cb(_, __frame, 246, 16, __then, true)); }); })(function ___(_error, __result) { __catch(function __$___closure() { if (_error) { e = _error; ok = true; __then(); } else { _(null, __result); } ; }); }); })(function ___() { __tryCatch(_, function __$___closure() { _(null, null, true); }); }); }); })(function ___(__e, __r, __cont) { (function ___(__then) { __tryCatch(_, function __$___closure() { assert(ok, true, "38"); __then(); }); })(function ___() { __tryCatch(_, function ___() { if (__cont) { __then(); } else { _(__e, __r); }; }); }); }); })(function ___() { __tryCatch(_, function __$___closure() { assert((orders[0].data.NOTE != null), false, "39"); return models.ORDER.get({ }, ["NAME",], __cb(_, __frame, 254, 26, function ___(__0, __19) { orders = __19; assert(orders[4].data.NAME, "fig", "40"); assert(orders[4].isDirty(), false, "41"); orders[4].data.NAME = "CHANGED"; assert(orders[4].isDirty(), true, "42"); return orders[4].reset(__cb(_, __frame, 259, 14, function __$___closure() { assert(orders[4].isDirty(), false, "43"); assert(orders[4].deleted, false, "44"); return orders[4].del(__cb(_, __frame, 262, 14, function __$___closure() { assert(orders[4].deleted, true, "45"); return (function ___(__then) { (function ___(_) { __tryCatch(_, function __$___closure() { return (function ___(__then) { (function ___(_) { __tryCatch(_, function __$___closure() { ok = false; return orders[2].del(__cb(_, __frame, 266, 16, __then, true)); }); })(function ___(_error, __result) { __catch(function __$___closure() { if (_error) { e = _error; ok = true; __then(); } else { _(null, __result); } ; }); }); })(function ___() { __tryCatch(_, function __$___closure() { _(null, null, true); }); }); }); })(function ___(__e, __r, __cont) { (function ___(__then) { __tryCatch(_, function __$___closure() { assert(ok, true, "45.1"); __then(); }); })(function ___() { __tryCatch(_, function ___() { if (__cont) { __then(); } else { _(__e, __r); }; }); }); }); })(function ___() { __tryCatch(_, function __$___closure() { return (function ___(__then) { (function ___(_) { __tryCatch(_, function __$___closure() { return (function ___(__then) { (function ___(_) { __tryCatch(_, function __$___closure() { ok = false; return models.ORDER.add({ ORDER_ID: 100, NAME: "zebra", SOMETHING: 123 }, __cb(_, __frame, 275, 19, __then, true)); }); })(function ___(_error, __result) { __catch(function __$___closure() { if (_error) { e = _error; ok = true; __then(); } else { _(null, __result); } ; }); }); })(function ___() { __tryCatch(_, function __$___closure() { _(null, null, true); }); }); }); })(function ___(__e, __r, __cont) { (function ___(__then) { __tryCatch(_, function __$___closure() { assert(ok, true, "45.2"); __then(); }); })(function ___() { __tryCatch(_, function ___() { if (__cont) { __then(); } else { _(__e, __r); }; }); }); }); })(function ___() { __tryCatch(_, function __$___closure() { return (function ___(__then) { (function ___(_) { __tryCatch(_, function __$___closure() { return (function ___(__then) { (function ___(_) { __tryCatch(_, function __$___closure() { ok = false; return models.ORDER.get({ }, ["-NAMEAAA",], __cb(_, __frame, 288, 19, __then, true)); }); })(function ___(_error, __result) { __catch(function __$___closure() { if (_error) { e = _error; ok = true; __then(); } else { _(null, __result); } ; }); }); })(function ___() { __tryCatch(_, function __$___closure() { _(null, null, true); }); }); }); })(function ___(__e, __r, __cont) { (function ___(__then) { __tryCatch(_, function __$___closure() { assert(ok, true, "45.3"); __then(); }); })(function ___() { __tryCatch(_, function ___() { if (__cont) { __then(); } else { _(__e, __r); }; }); }); }); })(function ___() { __tryCatch(_, function __$___closure() { return models.PERSON.empty(__cb(_, __frame, 295, 18, function __$___closure() { return models.PERSON.count(__cb(_, __frame, 296, 25, function ___(__0, __20) { assert(__20, 0, "46"); return orm.execute("DROP TABLE PERSON", [], __cb(_, __frame, 297, 8, function __$___closure() { return (function ___(__then) { (function ___(_) { __tryCatch(_, function __$___closure() { return (function ___(__then) { (function ___(_) { __tryCatch(_, function __$___closure() { ok = false; return models.PERSON.all(__cb(_, __frame, 300, 27, function ___(__0, __21) { rows = __21; __then(); }, true)); }); })(function ___(_error, __result) { __catch(function __$___closure() { if (_error) { e = _error; ok = true; __then(); } else { _(null, __result); } ; }); }); })(function ___() { __tryCatch(_, function __$___closure() { _(null, null, true); }); }); }); })(function ___(__e, __r, __cont) { (function ___(__then) { __tryCatch(_, function __$___closure() { assert(ok, true, "47"); __then(); }); })(function ___() { __tryCatch(_, function ___() { if (__cont) { __then(); } else { _(__e, __r); }; }); }); }); })(function ___() { __tryCatch(_, function __$___closure() { return models.ORDER.empty(__cb(_, __frame, 307, 17, function __$___closure() { return models.ORDER.count(__cb(_, __frame, 308, 24, function ___(__0, __22) { assert(__22, 0, "48"); return orm.execute("DROP TABLE \"ORDER\"", [], __cb(_, __frame, 309, 8, function __$___closure() { console.log("\n\nAll tests passed!\n\n"); return setTimeout(__cb(_, __frame, 311, 4, function __$___closure() { console.log("Now testing character encoding...\n"); return (function ___(__then) { (function ___(_) { __tryCatch(_, function __$___closure() { return orm.execute("DROP TABLE ORACLE_ORM_TEST_UTF", [], __cb(_, __frame, 314, 10, __then, true)); }); })(function ___(_error, __result) { __catch(function __$___closure() { if (_error) { e = _error; __then(); } else { _(null, __result); } ; }); }); })(function ___() { __tryCatch(_, function __$___closure() { return orm.execute("CREATE TABLE ORACLE_ORM_TEST_UTF(AAA NUMBER, BBB VARCHAR2(20))", [], __cb(_, __frame, 318, 12, function ___(__0, __23) { con(__23); return orm.execute("SELECT * from NLS_DATABASE_PARAMETERS", [], __cb(_, __frame, 319, 19, function ___(__0, __24) { dbParams = __24; return orm.execute("SELECT * FROM NLS_SESSION_PARAMETERS", [], __cb(_, __frame, 320, 24, function ___(__0, __25) { sessionParams = __25; console.log("\n\nDATABASE CHARACTER ENCODING:"); console.log(dbParams.filter(function(a) { var _ref1; return (((_ref1 = a.PARAMETER) === "NLS_NCHAR_CHARACTERSET") || (_ref1 === "NLS_CHARACTERSET")); }).map(function(a) { return ((a.PARAMETER + ": ") + a.VALUE); }).join("\n")); console.log(("NLS_LANG env variable: " + process.env.NLS_LANG)); console.log((("NLS_LANG session variable: " + ((((_ref1 = sessionParams.filter(function(a) { return (a.PARAMETER === "NLS_LANG"); })[0]) != null) ? _ref1.map(function(a) { return ((a.PARAMETER + ": ") + a.VALUE); }) : void 0))) + "\n\n")); utfString = "éêèÉÊÈ 日本"; return orm.execute("INSERT INTO ORACLE_ORM_TEST_UTF(AAA, BBB) VALUES (123, :1)", [utfString,], __cb(_, __frame, 335, 8, function __$___closure() { return orm.execute("SELECT * FROM ORACLE_ORM_TEST_UTF", [], __cb(_, __frame, 336, 18, function ___(__0, __26) { utfData = __26; assert(utfData[0].BBB, utfString, "50"); return orm.execute("DROP TABLE ORACLE_ORM_TEST_UTF", [], __cb(_, __frame, 338, 8, function __$___closure() { console.log("\n\nCharacter encoding is all good!\n\n"); __then(); }, true)); }, true)); }, true)); }, true)); }, true)); }, true)); }); }); }, true), 1500); }, true)); }, true)); }, true)); }); }); }, true)); }, true)); }, true)); }); }); }); }); }); }); }, true)); }, true)); }, true)); }); }); }, true)); }, true)); }, true)); }, true), 1, function __2(_, a) { var __frame = { name: "__2", line: 229 }; return __func(_, this, arguments, __2, 0, __frame, function __$__2() { return (function __$__2(__then) { if ((a.deleted == null)) { return a.sync(__cb(_, __frame, 2, 17, _, true)); } else { return _(null, a); } ; })(_); }); }); }, true)); }, true)); }); }); }, true)); }, true), -1, function __1(_, a) { var __frame = { name: "__1", line: 207 }; return __func(_, this, arguments, __1, 0, __frame, function __$__1() { return a.sync(__cb(_, __frame, 1, 15, _, true)); }); }); }, true)); }); }); }, true)); }, true)); }, true)); }, true)); }, true)); }, true)); }, true)); }, true)); }); }); }, true)); }, true)); }, true)); }); }); }, true)); }); }); }, true)); }, true)); }, true)); }, true)); }, true)); }, true)); }, true)); }, true)); }, true)); }, true)); }, true)); }, true)); }, true)); }, true)); }, true)); }, true)); }, true)); }, true)); }, true)); }, true)); }, true)); }); }); }); }); }, true)); }); })(function ___(_error, __result) { __catch(function __$___closure() { if (_error) { err = _error; con(err); stack(err); __then(); } else { _(null, __result); } ; }); }); })(function ___() { __tryCatch(_, function __$___closure() { _(); }); }); })(_); });}).call(this, __trap); 348 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "oracle-orm", 3 | "version": "0.1.9", 4 | "description": "An Oracle ORM that actually works", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "test": "node lib/unittests.js", 8 | "preinstall": "./driver/reinstall.sh" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/SGrondin/oracle-orm" 13 | }, 14 | "keywords": [ 15 | "oracle", 16 | "orm" 17 | ], 18 | "author": { 19 | "name": "Simon Grondin" 20 | }, 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/SGrondin/oracle-orm/issues" 24 | }, 25 | "devDependencies":{ 26 | "coffee-script": "1.7.x", 27 | "streamline": "0.10.x", 28 | "oracle":"0.3.x" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /recompile.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | DIR=$(dirname $0) 4 | pushd $DIR > /dev/null 5 | 6 | if [[ ! -d node_modules ]]; then 7 | echo 'Installing compiler tools...' 8 | sleep 1 9 | npm install 10 | fi 11 | 12 | echo 'Compiling oracle-orm...' 13 | 14 | node_modules/streamline/bin/_coffee -c src/ 15 | rm lib/*.js > /dev/null 16 | mv src/*.js lib/ 17 | 18 | popd > /dev/null 19 | echo 'Done!' 20 | -------------------------------------------------------------------------------- /src/Column.coffee: -------------------------------------------------------------------------------- 1 | class Column 2 | constructor: (@name, @type, @length) -> @ 3 | 4 | module.exports = Column 5 | -------------------------------------------------------------------------------- /src/ORM._coffee: -------------------------------------------------------------------------------- 1 | oracle = require "oracle" 2 | Table = require "./Table" 3 | Column = require "./Column" 4 | util = require "util" 5 | types = require "./types" 6 | 7 | class ORM 8 | constructor: (connectData, @debug=false, _) -> 9 | @link = oracle.connect connectData, _ 10 | instance = @ 11 | @connection = { 12 | "execute": (sql, args, cb) -> 13 | if instance.debug then console.log " "+sql, args 14 | instance.link.execute sql, args, (err, results...) -> 15 | if err? and not instance.debug then console.log ">>>>>", sql, args 16 | cb.apply {}, [err].concat results 17 | 18 | "driver": oracle 19 | } 20 | 21 | getModels: (_) -> 22 | ret = {} 23 | tables = @connection.execute "SELECT TABLE_NAME FROM USER_TABLES", [], _ 24 | tables.forEach_ _, -1, (_, table) => 25 | columns = @connection.execute "SELECT COLUMN_NAME, DATA_TYPE, DATA_LENGTH FROM USER_TAB_COLUMNS WHERE TABLE_NAME=:1 ORDER BY COLUMN_ID", [table.TABLE_NAME], _ 26 | objColumns = {} 27 | columns.forEach (c) -> 28 | objColumns[c.COLUMN_NAME] = new Column c.COLUMN_NAME, types.getType(c.DATA_TYPE), c.DATA_LENGTH 29 | primary = @connection.execute ""+ 30 | "SELECT T.CONSTRAINT_NAME AS CONSTRAINT_NAME, C.COLUMN_NAME AS COLUMN_NAME "+ 31 | "FROM ALL_CONSTRAINTS T, ALL_CONS_COLUMNS C WHERE T.OWNER = C.OWNER AND T.CONSTRAINT_NAME = C.CONSTRAINT_NAME AND "+ 32 | "T.TABLE_NAME = :1 AND T.CONSTRAINT_TYPE='P'", [table.TABLE_NAME], _ 33 | primary = primary.map (c) -> objColumns[c.COLUMN_NAME] 34 | ret[table.TABLE_NAME] = new Table @connection, table.TABLE_NAME, objColumns, primary 35 | ret 36 | 37 | execute: (sql, args, _) -> 38 | @connection.execute sql, args, _ 39 | 40 | disconnect: -> @link.close() 41 | 42 | module.exports = ORM 43 | -------------------------------------------------------------------------------- /src/Query._coffee: -------------------------------------------------------------------------------- 1 | Row = require "./Row" 2 | types = require "./types" 3 | helpers = require "./helpers" 4 | 5 | getOrderBy = (orderBy) -> 6 | if orderBy? and orderBy.length > 0 7 | orderBy = orderBy.map (a) -> 8 | if a[0] == "-" 9 | "\""+a[1..]+"\" DESC" 10 | else 11 | "\""+a+"\" ASC" 12 | "ORDER BY "+orderBy.join ", " 13 | else 14 | "" 15 | 16 | class Query 17 | constructor: (@connection, @table, safe=true) -> 18 | @getWhere = if safe 19 | (obj) -> 20 | helpers.getWhere obj, "=" 21 | else 22 | helpers.getWhereNoPlaceholders 23 | 24 | select: (where, orderBy, _) -> 25 | order = getOrderBy orderBy 26 | [where, values] = @getWhere where 27 | (@connection.execute "SELECT * FROM \""+@table.name+"\" WHERE "+where+" "+order, values, _).map (line) => 28 | new Row @connection, @table, line 29 | 30 | update: (pairs, where, _) -> 31 | [update, values1] = helpers.getWhere pairs, "=" 32 | [where, values2] = @getWhere where 33 | @connection.execute "UPDATE \""+@table.name+"\" SET "+update.join(", ")+ 34 | " WHERE "+where.join(" AND "), values1.concat(values2), _ 35 | 36 | insert: (pairs, _) -> 37 | columns = ("\""+col+"\"" for col in Object.keys pairs) 38 | values = helpers.getValues pairs 39 | placeholders = (helpers.getPlaceholders values.length).join ", " 40 | primaries = @table.primary.map((a) -> a.name).join ", " 41 | returnPlaceholders = (helpers.getPlaceholders @table.primary.length).join ", " 42 | params = [].concat values, (new @connection.driver.OutParam(types.typeToOCCI[i.type]) for i in @table.primary) 43 | 44 | @connection.execute "INSERT INTO \""+@table.name+"\"("+columns.join(", ")+") VALUES("+placeholders+") RETURNING "+primaries+" INTO "+ 45 | returnPlaceholders, params, _ 46 | 47 | del: (where, _) -> 48 | [where, values] = @getWhere where 49 | @connection.execute "DELETE FROM \""+@table.name+"\" WHERE "+where.join(" AND "), values, _ 50 | 51 | module.exports = Query 52 | -------------------------------------------------------------------------------- /src/Row._coffee: -------------------------------------------------------------------------------- 1 | helpers = require "./helpers" 2 | 3 | class Row 4 | constructor: (@connection, @table, @data) -> 5 | @deleted = false 6 | @makeBackData() 7 | 8 | makeBackData: -> 9 | @backdata = {} 10 | for own k, v of @data 11 | @backdata[k] = v 12 | @backdata 13 | 14 | getDirty: -> 15 | dirty = {} 16 | for k, v of @backdata 17 | if v != @data[k] 18 | dirty[k] = @data[k] 19 | dirty 20 | 21 | isDirty: -> 22 | Object.keys(@getDirty()).length > 0 23 | 24 | save: (_) -> 25 | if @deleted then throw new Error "Unit "+@table.name+" was deleted and doesn't exist anymore" 26 | dirty = @getDirty() 27 | if Object.keys(dirty).length == 0 then return [] 28 | [update, values1] = helpers.getWhere dirty, "=" 29 | [where, values2] = helpers.getWhere (helpers.getPKPairs @), "=" 30 | r = (@connection.execute "UPDATE \""+@table.name+"\" SET "+update.join(", ")+ 31 | " WHERE "+where.join(" AND "), values1.concat(values2), _) 32 | if r.updateCount == 0 33 | @deleted = true 34 | @data = {} 35 | throw new Error "Unit "+@table.name+" was deleted and doesn't exist anymore" 36 | else 37 | @makeBackData() 38 | @ 39 | 40 | sync: (_) -> 41 | if @deleted then throw new Error "Unit "+@table.name+" was deleted and doesn't exist anymore" 42 | [where, values] = helpers.getWhere (helpers.getPKPairs @), "=" 43 | r = @connection.execute "SELECT * FROM \""+@table.name+"\" WHERE "+where.join(" AND "), values, _ 44 | if r.length == 0 45 | @deleted = true 46 | @data = {} 47 | throw new Error "Unit "+@table.name+" was deleted and doesn't exist anymore" 48 | else 49 | @data = r[0] 50 | @makeBackData() 51 | @ 52 | 53 | reset: (_) -> 54 | @data = @backdata 55 | @makeBackData() 56 | @data 57 | 58 | del: (_) -> 59 | if @deleted then throw new Error "Unit "+@table.name+" was deleted and doesn't exist anymore" 60 | [where, values] = helpers.getWhere (helpers.getPKPairs @), "=" 61 | @deleted = true 62 | @connection.execute "DELETE FROM \""+@table.name+"\" WHERE "+where.join(" AND "), values, _ 63 | 64 | module.exports = Row 65 | -------------------------------------------------------------------------------- /src/Table._coffee: -------------------------------------------------------------------------------- 1 | Row = require "./Row" 2 | Query = require "./Query" 3 | 4 | class Table 5 | constructor: (@connection, @name, @columns, @primary=[]) -> 6 | 7 | validateColummns: (columnNames=[]) -> 8 | invalids = columnNames.map((c) => if not @columns[c]? then c else null).filter((a) -> a?) 9 | if not (invalids.length == 0) then throw new Error "Invalid column(s): "+invalids.join ", " 10 | 11 | add: (pairs, _) -> 12 | @validateColummns Object.keys pairs 13 | query = new Query @connection, @ 14 | query.insert pairs, _ 15 | 16 | get: (where={}, orderBy=null, _) -> 17 | @validateColummns Object.keys where 18 | @validateColummns orderBy?.map (a) -> if a[0] == "-" then a[1..] else a 19 | query = new Query @connection, @ 20 | query.select where, orderBy, _ 21 | 22 | getUnsafe: (where={}, orderBy=null, _) -> 23 | @validateColummns Object.keys where 24 | @validateColummns orderBy?.map (a) -> if a[0] == "-" then a[1..] else a 25 | query = new Query @connection, @, false 26 | query.select where, orderBy, _ 27 | 28 | all: (_) -> @get null, null, _ 29 | 30 | update: (columns={}, where={}, _) -> 31 | @validateColummns Object.keys columns 32 | @validateColummns Object.keys where 33 | query = new Query @connection, @ 34 | query.update columns, where, _ 35 | 36 | updateUnsafe: (columns={}, where={}, _) -> 37 | @validateColummns Object.keys columns 38 | @validateColummns Object.keys where 39 | query = new Query @connection, @, false 40 | query.update columns, where, _ 41 | 42 | del: (where={}, _) -> 43 | @validateColummns Object.keys where 44 | query = new Query @connection, @ 45 | query.del where, _ 46 | 47 | delUnsafe: (where={}, _) -> 48 | @validateColummns Object.keys where 49 | query = new Query @connection, @, false 50 | query.del where, _ 51 | 52 | count: (_) -> 53 | r = @connection.execute "SELECT COUNT(*) AS C FROM \""+@name+"\"", [], _ 54 | r[0].C 55 | 56 | empty: (_) -> 57 | @connection.execute "TRUNCATE TABLE \""+@name+"\"", [], _ 58 | 59 | module.exports = Table 60 | -------------------------------------------------------------------------------- /src/helpers.coffee: -------------------------------------------------------------------------------- 1 | placeCounter = 0 2 | getPlaceholders = (nb) -> 3 | (":"+(placeCounter++ % 100) for i in [1..nb]) 4 | 5 | getWhere = (pairs, separator="") -> 6 | where = (getListPlaceholders pairs, separator) 7 | values = getValues pairs 8 | [where, values] 9 | 10 | getWhereNoPlaceholders = (pairs, separator="") -> 11 | where = (getListNoPlaceholders pairs, separator) 12 | [where, []] 13 | 14 | getListPlaceholders = (obj, separator="=") -> 15 | if Object.keys(obj).length == 0 16 | ["1=1"] 17 | else 18 | Object.keys(obj).map (k) -> "\""+k+"\""+separator+(getPlaceholders 1) 19 | 20 | getListNoPlaceholders = (obj, separator="=") -> 21 | if Object.keys(obj).length == 0 22 | ["1=1"] 23 | else 24 | Object.keys(obj).map (k) -> "\""+k+"\""+separator+obj[k] 25 | 26 | 27 | getValues = (obj) -> 28 | Object.keys(obj).map (a) -> obj[a] 29 | 30 | getPKPairs = (row) -> 31 | pairs = {} 32 | row.table.primary.forEach (c) -> 33 | pairs[c.name] = row.backdata[c.name] 34 | pairs 35 | 36 | 37 | module.exports = {getPlaceholders, getWhere, getWhereNoPlaceholders, getValues, getPKPairs} 38 | -------------------------------------------------------------------------------- /src/index.coffee: -------------------------------------------------------------------------------- 1 | 2 | ORM = require "./ORM" 3 | 4 | 5 | 6 | 7 | module.exports = ORM 8 | -------------------------------------------------------------------------------- /src/types.coffee: -------------------------------------------------------------------------------- 1 | oracle = require "oracle" 2 | 3 | types = { 4 | STRING: 0 5 | NUMBER: 1 6 | DATE: 2 7 | } 8 | 9 | 10 | typeFromOracle = { 11 | "VARCHAR2": types.STRING 12 | "CHAR": types.STRING 13 | "CLOB": types.STRING 14 | "NUMBER": types.NUMBER 15 | "DATE": types.DATE 16 | } 17 | 18 | # Matches the 'types' object. ONLY USED FOR PRIMARY KEYS 19 | typeToOCCI = [oracle.OCCISTRING, oracle.OCCINUMBER] 20 | 21 | getType = (str) -> 22 | if not typeFromOracle[str]? then throw new Error "Invalid type: "+str 23 | typeFromOracle[str] 24 | 25 | module.exports = {types, getType, typeToOCCI} 26 | -------------------------------------------------------------------------------- /src/unittests._coffee: -------------------------------------------------------------------------------- 1 | util = require "util" 2 | fs = require "fs" 3 | global.con = (v) -> util.puts util.inspect v 4 | global.stack = (err) -> 5 | util.puts "__"+err.message 6 | util.puts err.stack 7 | 8 | assert = (a, b, number) -> 9 | if a == b 10 | console.log ">"+number+" OK "+a+" === "+b 11 | else 12 | console.log ">"+number+" FAILED!! "+a+" !== "+b 13 | throw new Error "Test "+number 14 | 15 | oracleConnectData = JSON.parse (fs.readFileSync __dirname+"/../testDB.json").toString("utf8") 16 | 17 | ORM = require "oracle-orm" 18 | # ORM = require "./index" 19 | 20 | try 21 | orm = new ORM oracleConnectData, true, _ 22 | 23 | # Arbitrary SQL 24 | try 25 | orm.execute "DROP TABLE PERSON", [], _ 26 | catch err 27 | try 28 | orm.execute "DROP TABLE \"ORDER\"", [], _ 29 | catch err 30 | 31 | orm.execute "CREATE TABLE PERSON (AAA NUMBER, BBB VARCHAR2(50), F_ORDER_ID NUMBER)", [], _ 32 | orm.execute """ALTER TABLE "#{oracleConnectData.user}"."PERSON" ADD CONSTRAINT PK_PERSON PRIMARY KEY("AAA","F_ORDER_ID")""", [], _ 33 | 34 | orm.execute """CREATE TABLE "ORDER" (ORDER_ID NUMBER, "NAME" VARCHAR2(50), NOTE VARCHAR2(50))""", [], _ 35 | orm.execute """ALTER TABLE "#{oracleConnectData.user}"."ORDER" ADD CONSTRAINT PK_ORDER PRIMARY KEY("ORDER_ID")""", [], _ 36 | 37 | orm.execute """ALTER TABLE "#{oracleConnectData.user}"."PERSON" ADD CONSTRAINT FK_ORDER FOREIGN KEY("F_ORDER_ID") REFERENCES "ORDER"("ORDER_ID")""", [], _ 38 | 39 | models = orm.getModels _ 40 | assert (models.PERSON.count _), 0, "1" 41 | 42 | 43 | # Add objects 44 | models.ORDER.add {ORDER_ID:1, NAME:"avocado"}, _ 45 | models.ORDER.add {ORDER_ID:2, NAME:"banana"}, _ 46 | models.ORDER.add {ORDER_ID:3, NAME:"coconut"}, _ 47 | models.ORDER.add {ORDER_ID:4, NAME:"date"}, _ 48 | models.ORDER.add {ORDER_ID:5, NAME:"eggplant"}, _ 49 | models.ORDER.add {ORDER_ID:6, NAME:"fig"}, _ 50 | models.ORDER.add {ORDER_ID:7, NAME:"grapefruit"}, _ 51 | 52 | models.PERSON.add {AAA:10, BBB:"abc", F_ORDER_ID:1}, _ 53 | models.PERSON.add {AAA:20, BBB:"xyz", F_ORDER_ID:2}, _ 54 | models.PERSON.add {AAA:30, BBB:"def", F_ORDER_ID:3}, _ 55 | models.PERSON.add {AAA:40, BBB:"def", F_ORDER_ID:3}, _ 56 | models.PERSON.add {AAA:50, F_ORDER_ID:5}, _ 57 | assert (models.PERSON.count _), 5, "2" 58 | 59 | 60 | rows = models.PERSON.all _ 61 | assert rows.length, 5, "3" 62 | 63 | try 64 | ok = false 65 | rows = models.PERSON {AAA:">35"}, ["AAA"], _ 66 | catch e 67 | ok = true 68 | finally assert ok, true, "3.1" 69 | rows = models.PERSON.getUnsafe {AAA:">35"}, ["AAA"], _ 70 | assert rows.length, 2, "4" 71 | 72 | assert rows[0].connection?.execute?, true, "5" 73 | assert rows[0].table.name, "PERSON", "6" 74 | assert rows[0].data.AAA, 40, "7" 75 | assert rows[0].data.BBB, "def", "8" 76 | assert rows[1].data.AAA, 50, "9" 77 | assert rows[1].data.BBB, null, "10" 78 | 79 | # UPDATE 80 | try 81 | ok = false 82 | models.PERSON.updateUnsafe {AAA:-100}, {BBB:"abc"}, _ 83 | catch e 84 | ok = true 85 | finally assert ok, true, "10.1" 86 | models.PERSON.update {AAA:-100}, {BBB:"abc"}, _ 87 | rows = models.PERSON.get {}, ["F_ORDER_ID", "AAA"], _ 88 | 89 | assert rows[0].data.AAA, -100, "11" 90 | 91 | # DELETE 92 | assert rows[0].deleted, false, "12" 93 | rows[0].del _ 94 | assert rows[0].deleted, true, "13" 95 | # Can't delete what's already been deleted 96 | try 97 | ok = false 98 | rows[0].del _ 99 | catch e 100 | ok = true 101 | finally assert ok, true, "14" 102 | 103 | rows = models.PERSON.all _ 104 | assert (models.PERSON.count _), 4, "15" 105 | rows = models.PERSON.get {}, ["AAA"], _ 106 | 107 | # SAVE 108 | assert rows[1].backdata.AAA, rows[1].data.AAA, "16" 109 | rows[1].data.AAA++ 110 | dirty = rows[1].getDirty() 111 | assert Object.keys(dirty).length, 1, "17" 112 | assert rows[1].isDirty(), true, "18" 113 | assert dirty.AAA, 31, "19" 114 | rows[1].save _ 115 | dirty = rows[1].getDirty() 116 | assert Object.keys(dirty).length, 0, "20" 117 | assert rows[1].isDirty(), false, "21" 118 | 119 | # SYNC 120 | rows[2].data.BBB = "xyz" 121 | dirty = rows[2].getDirty() 122 | assert Object.keys(dirty).length, 1, "22" 123 | assert rows[2].isDirty(), true, "23" 124 | rows[2].sync _ 125 | # rows[2].data.BBB == "def" or throw new Error "18" 126 | dirty = rows[2].getDirty() 127 | assert Object.keys(dirty).length, 0, "25" 128 | assert rows[2].isDirty(), false, "26" 129 | 130 | # SAVE clean 131 | rows[2].save _ 132 | 133 | # RESET 134 | rows[2].data.BBB = "xyz" 135 | assert rows[2].isDirty(), true, "27" 136 | rows[2].reset _ 137 | assert rows[2].data.BBB, "def", "28" 138 | assert rows[2].isDirty(), false, "29" 139 | 140 | 141 | ##### Run simple tests on ORDER ##### 142 | orders = models.ORDER.get {}, ["-NAME"], _ 143 | assert orders[0].data.NAME, "grapefruit", "30" 144 | try 145 | ok = false 146 | models.ORDER.update {NOTE:"nothing"}, {ORDER_ID:">=3"}, _ 147 | catch e 148 | ok = true 149 | finally assert ok, true, "30.1" 150 | models.ORDER.updateUnsafe {NOTE:"nothing"}, {ORDER_ID:">=3"}, _ 151 | 152 | 153 | 154 | assert (orders.filter (a) -> a.data.NOTE == null).length, 7, "31" 155 | orders.forEach_ _, -1, (_, a) -> a.sync _ 156 | assert (orders.filter (a) -> a.data.NOTE == null).length, 2, "32" 157 | 158 | assert (models.ORDER.count _), 7, "33" 159 | try 160 | ok = false 161 | models.ORDER.del {ORDER_ID:"=4"}, _ 162 | catch e 163 | ok = true 164 | finally assert ok, true, "33.1" 165 | models.ORDER.del {ORDER_ID:"4"}, _ 166 | assert (models.ORDER.count _), 6, "34" 167 | 168 | orders = orders.map_ _, 1, (_, a) -> if not a.deleted? then a.sync _ else a 169 | 170 | assert orders[4].isDirty(), false, "35" 171 | orders[4].data.NOTE = "APPLE" 172 | assert orders[4].isDirty(), true, "36" 173 | orders[4].save _ 174 | apple = orm.execute """SELECT * FROM "ORDER" WHERE "ORDER_ID" = 3""", [], _ 175 | assert apple[0].NOTE, "APPLE", "36.1" 176 | assert orders[4].isDirty(), false, "37" 177 | 178 | orm.execute """DELETE FROM "ORDER" WHERE "ORDER_ID"=7""", [], _ 179 | orders[0].data.NOTE = "OOPS" 180 | try 181 | ok = false 182 | orders[0].save _ 183 | catch e 184 | ok = true 185 | finally assert ok, true, "38" 186 | assert orders[0].data.NOTE?, false, "39" 187 | 188 | orders = (models.ORDER.get {}, ["NAME"], _) 189 | 190 | assert orders[4].data.NAME, "fig", "40" 191 | assert orders[4].isDirty(), false, "41" 192 | orders[4].data.NAME = "CHANGED" 193 | assert orders[4].isDirty(), true, "42" 194 | orders[4].reset _ 195 | assert orders[4].isDirty(), false, "43" 196 | 197 | assert orders[4].deleted, false, "44" 198 | orders[4].del _ 199 | assert orders[4].deleted, true, "45" 200 | 201 | # Can't delete due to foreign key 202 | try 203 | ok = false 204 | orders[2].del _ 205 | catch e 206 | ok = true 207 | finally assert ok, true, "45.1" 208 | 209 | # Test column validation 210 | try 211 | ok = false 212 | models.ORDER.add {ORDER_ID:100, NAME: "zebra", SOMETHING:123}, _ 213 | catch e 214 | ok = true 215 | finally assert ok, true, "45.2" 216 | try 217 | ok = false 218 | models.ORDER.get {}, ["-NAMEAAA"], _ 219 | catch e 220 | ok = true 221 | finally assert ok, true, "45.3" 222 | 223 | # EMPTY and DROP PERSON 224 | models.PERSON.empty _ 225 | assert (models.PERSON.count _), 0, "46" 226 | 227 | orm.execute "DROP TABLE PERSON", [], _ 228 | try 229 | ok = false 230 | rows = models.PERSON.all _ 231 | catch e 232 | ok = true 233 | finally assert ok, true, "47" 234 | 235 | # EMPTY and DROP ORDER 236 | models.ORDER.empty _ 237 | assert (models.ORDER.count _), 0, "48" 238 | orm.execute "DROP TABLE \"ORDER\"", [], _ 239 | 240 | console.log "\n\nAll tests passed!\n\n" 241 | 242 | 243 | setTimeout _, 1500 244 | 245 | console.log "Now testing character encoding...\n" 246 | 247 | try 248 | orm.execute "DROP TABLE ORACLE_ORM_TEST_UTF", [], _ 249 | catch e 250 | con orm.execute "CREATE TABLE ORACLE_ORM_TEST_UTF(AAA NUMBER, BBB VARCHAR2(20))", [], _ 251 | 252 | dbParams = orm.execute "SELECT * from NLS_DATABASE_PARAMETERS", [], _ 253 | sessionParams = orm.execute "SELECT * FROM NLS_SESSION_PARAMETERS", [], _ 254 | console.log "\n\nDATABASE CHARACTER ENCODING:" 255 | console.log dbParams.filter((a) -> a.PARAMETER in ["NLS_NCHAR_CHARACTERSET", "NLS_CHARACTERSET"]).map((a) -> a.PARAMETER+": "+a.VALUE).join "\n" 256 | console.log "NLS_LANG env variable: "+process.env.NLS_LANG 257 | console.log "NLS_LANG session variable: "+sessionParams.filter((a) -> a.PARAMETER == "NLS_LANG")[0]?.map((a) -> a.PARAMETER+": "+a.VALUE)+"\n\n" 258 | 259 | utfString = "éêèÉÊÈ 日本" 260 | orm.execute "INSERT INTO ORACLE_ORM_TEST_UTF(AAA, BBB) VALUES (123, :1)", [utfString], _ 261 | utfData = orm.execute "SELECT * FROM ORACLE_ORM_TEST_UTF", [], _ 262 | 263 | assert utfData[0].BBB, utfString, "50" 264 | 265 | orm.execute "DROP TABLE ORACLE_ORM_TEST_UTF", [], _ 266 | 267 | console.log "\n\nCharacter encoding is all good!\n\n" 268 | 269 | catch err 270 | con err 271 | stack err 272 | 273 | 274 | -------------------------------------------------------------------------------- /testDB.json: -------------------------------------------------------------------------------- 1 | { 2 | "driver": "oracle", 3 | "hostname": "hostname or IP", 4 | "port": 1521, 5 | "database": "DB name (SID)", 6 | "user": "username", 7 | "password": "password123" 8 | } 9 | --------------------------------------------------------------------------------