From 0079acb42aa9003d95acdfccf91bd76d86b5ac07 Mon Sep 17 00:00:00 2001 From: Holger Koser Date: Mon, 4 Nov 2013 19:48:13 +0100 Subject: [PATCH] Initial commit --- .gitignore | 4 + .jshintrc | 39 ++ .npmignore | 3 + LICENSE | 202 +++++++ Makefile | 36 ++ NOTICE | 4 + README.md | 249 +++++++- examples/app1.js | 32 ++ examples/app2.js | 41 ++ examples/app3.js | 46 ++ examples/app4.js | 41 ++ examples/app5.js | 52 ++ examples/call1.js | 76 +++ examples/client.js | 54 ++ examples/pool.js | 46 ++ examples/server.js | 113 ++++ index.js | 22 + lib/Client.js | 183 ++++++ lib/Lob.js | 1 + lib/index.js | 26 + lib/protocol/Connection.js | 446 +++++++++++++++ lib/protocol/Lob.js | 215 +++++++ lib/protocol/Parser.js | 441 +++++++++++++++ lib/protocol/Result.js | 149 +++++ lib/protocol/ResultSet.js | 343 +++++++++++ lib/protocol/Statement.js | 123 ++++ lib/protocol/Stringifier.js | 62 ++ lib/protocol/auth/index.js | 16 + lib/protocol/auth/scramsha256/Algorithm.js | 78 +++ .../auth/scramsha256/Authentication.js | 45 ++ lib/protocol/auth/scramsha256/Connect.js | 40 ++ lib/protocol/auth/scramsha256/index.js | 18 + lib/protocol/common/ClientDistributionMode.js | 21 + lib/protocol/common/CommandOption.js | 22 + lib/protocol/common/ConnectOption.js | 40 ++ lib/protocol/common/Constants.js | 66 +++ lib/protocol/common/DataFormatVersion.js | 21 + .../common/DistributionProtocolVersion.js | 19 + lib/protocol/common/ErrorLevel.js | 20 + lib/protocol/common/FunctionCode.js | 41 ++ lib/protocol/common/IoType.js | 20 + lib/protocol/common/LobOptions.js | 20 + lib/protocol/common/LobSourceType.js | 21 + lib/protocol/common/MessageType.js | 40 ++ lib/protocol/common/ParameterMode.js | 23 + lib/protocol/common/PartKind.js | 68 +++ lib/protocol/common/ResultSetAttributes.js | 22 + lib/protocol/common/SegmentKind.js | 21 + lib/protocol/common/SessionContext.js | 23 + lib/protocol/common/StatementContext.js | 19 + lib/protocol/common/TopologyInformation.js | 29 + lib/protocol/common/TypeCode.js | 87 +++ lib/protocol/common/index.js | 58 ++ lib/protocol/data/Binary.js | 42 ++ lib/protocol/data/Default.js | 42 ++ lib/protocol/data/Fields.js | 111 ++++ lib/protocol/data/Int32.js | 43 ++ lib/protocol/data/MultilineOptions.js | 74 +++ lib/protocol/data/Options.js | 166 ++++++ lib/protocol/data/ParameterMetadata.js | 76 +++ lib/protocol/data/Parameters.js | 159 ++++++ lib/protocol/data/ReadLobReply.js | 72 +++ lib/protocol/data/ReadLobRequest.js | 57 ++ lib/protocol/data/ResultSetMetadata.js | 84 +++ lib/protocol/data/SqlError.js | 66 +++ lib/protocol/data/Text.js | 42 ++ lib/protocol/data/Text20.js | 42 ++ lib/protocol/data/index.js | 66 +++ lib/protocol/index.js | 26 + lib/protocol/reply/Part.js | 70 +++ lib/protocol/reply/Segment.js | 161 ++++++ lib/protocol/reply/index.js | 17 + lib/protocol/request/Authenticate.js | 31 + lib/protocol/request/CloseResultSet.js | 35 ++ lib/protocol/request/Connect.js | 44 ++ lib/protocol/request/Disconnect.js | 22 + lib/protocol/request/DropStatementId.js | 35 ++ lib/protocol/request/Execute.js | 44 ++ lib/protocol/request/ExecuteDirect.js | 40 ++ lib/protocol/request/FetchNext.js | 38 ++ lib/protocol/request/Part.js | 51 ++ lib/protocol/request/Prepare.js | 40 ++ lib/protocol/request/ReadLob.js | 35 ++ lib/protocol/request/Segment.js | 93 +++ lib/protocol/request/index.js | 27 + lib/util/Queue.js | 115 ++++ lib/util/bignum.js | 532 ++++++++++++++++++ lib/util/index.js | 110 ++++ package.json | 38 ++ test/acceptance/hdb.Dummy.js | 44 ++ test/acceptance/hdb.Prepare.js | 129 +++++ test/acceptance/hdb.Query.js | 138 +++++ test/auth.scramsha256.js | 73 +++ test/data.Options.js | 94 ++++ test/data.ParameterMetadata.js | 73 +++ test/data.Parameters.js | 33 ++ test/data.ReadLob.js | 67 +++ test/data.ResultSet.js | 46 ++ test/data.ResultSetMetadata.js | 33 ++ test/data.TopogolyInformation.js | 40 ++ test/fixtures/numbers.js | 125 ++++ test/fixtures/parametersData.js | 65 +++ test/fixtures/resultSetData.js | 420 ++++++++++++++ test/fixtures/resultSetMetadata.js | 344 +++++++++++ test/fixtures/topogolyInformation.js | 144 +++++ test/lib.bignum.js | 197 +++++++ test/lib/config.tpl.json | 6 + test/lib/index.js | 128 +++++ test/mocha.opts | 3 + test/request.Authenticate.js | 82 +++ 110 files changed, 8976 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 .jshintrc create mode 100644 .npmignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 NOTICE create mode 100644 examples/app1.js create mode 100644 examples/app2.js create mode 100644 examples/app3.js create mode 100644 examples/app4.js create mode 100644 examples/app5.js create mode 100644 examples/call1.js create mode 100644 examples/client.js create mode 100644 examples/pool.js create mode 100644 examples/server.js create mode 100644 index.js create mode 100644 lib/Client.js create mode 100644 lib/Lob.js create mode 100644 lib/index.js create mode 100644 lib/protocol/Connection.js create mode 100644 lib/protocol/Lob.js create mode 100644 lib/protocol/Parser.js create mode 100644 lib/protocol/Result.js create mode 100644 lib/protocol/ResultSet.js create mode 100644 lib/protocol/Statement.js create mode 100644 lib/protocol/Stringifier.js create mode 100644 lib/protocol/auth/index.js create mode 100644 lib/protocol/auth/scramsha256/Algorithm.js create mode 100644 lib/protocol/auth/scramsha256/Authentication.js create mode 100644 lib/protocol/auth/scramsha256/Connect.js create mode 100644 lib/protocol/auth/scramsha256/index.js create mode 100644 lib/protocol/common/ClientDistributionMode.js create mode 100644 lib/protocol/common/CommandOption.js create mode 100644 lib/protocol/common/ConnectOption.js create mode 100644 lib/protocol/common/Constants.js create mode 100644 lib/protocol/common/DataFormatVersion.js create mode 100644 lib/protocol/common/DistributionProtocolVersion.js create mode 100644 lib/protocol/common/ErrorLevel.js create mode 100644 lib/protocol/common/FunctionCode.js create mode 100644 lib/protocol/common/IoType.js create mode 100644 lib/protocol/common/LobOptions.js create mode 100644 lib/protocol/common/LobSourceType.js create mode 100644 lib/protocol/common/MessageType.js create mode 100644 lib/protocol/common/ParameterMode.js create mode 100644 lib/protocol/common/PartKind.js create mode 100644 lib/protocol/common/ResultSetAttributes.js create mode 100644 lib/protocol/common/SegmentKind.js create mode 100644 lib/protocol/common/SessionContext.js create mode 100644 lib/protocol/common/StatementContext.js create mode 100644 lib/protocol/common/TopologyInformation.js create mode 100644 lib/protocol/common/TypeCode.js create mode 100644 lib/protocol/common/index.js create mode 100644 lib/protocol/data/Binary.js create mode 100644 lib/protocol/data/Default.js create mode 100644 lib/protocol/data/Fields.js create mode 100644 lib/protocol/data/Int32.js create mode 100644 lib/protocol/data/MultilineOptions.js create mode 100644 lib/protocol/data/Options.js create mode 100644 lib/protocol/data/ParameterMetadata.js create mode 100644 lib/protocol/data/Parameters.js create mode 100644 lib/protocol/data/ReadLobReply.js create mode 100644 lib/protocol/data/ReadLobRequest.js create mode 100644 lib/protocol/data/ResultSetMetadata.js create mode 100644 lib/protocol/data/SqlError.js create mode 100644 lib/protocol/data/Text.js create mode 100644 lib/protocol/data/Text20.js create mode 100644 lib/protocol/data/index.js create mode 100644 lib/protocol/index.js create mode 100644 lib/protocol/reply/Part.js create mode 100644 lib/protocol/reply/Segment.js create mode 100644 lib/protocol/reply/index.js create mode 100644 lib/protocol/request/Authenticate.js create mode 100644 lib/protocol/request/CloseResultSet.js create mode 100644 lib/protocol/request/Connect.js create mode 100644 lib/protocol/request/Disconnect.js create mode 100644 lib/protocol/request/DropStatementId.js create mode 100644 lib/protocol/request/Execute.js create mode 100644 lib/protocol/request/ExecuteDirect.js create mode 100644 lib/protocol/request/FetchNext.js create mode 100644 lib/protocol/request/Part.js create mode 100644 lib/protocol/request/Prepare.js create mode 100644 lib/protocol/request/ReadLob.js create mode 100644 lib/protocol/request/Segment.js create mode 100644 lib/protocol/request/index.js create mode 100644 lib/util/Queue.js create mode 100644 lib/util/bignum.js create mode 100644 lib/util/index.js create mode 100644 package.json create mode 100644 test/acceptance/hdb.Dummy.js create mode 100644 test/acceptance/hdb.Prepare.js create mode 100644 test/acceptance/hdb.Query.js create mode 100644 test/auth.scramsha256.js create mode 100644 test/data.Options.js create mode 100644 test/data.ParameterMetadata.js create mode 100644 test/data.Parameters.js create mode 100644 test/data.ReadLob.js create mode 100644 test/data.ResultSet.js create mode 100644 test/data.ResultSetMetadata.js create mode 100644 test/data.TopogolyInformation.js create mode 100644 test/fixtures/numbers.js create mode 100644 test/fixtures/parametersData.js create mode 100644 test/fixtures/resultSetData.js create mode 100644 test/fixtures/resultSetMetadata.js create mode 100644 test/fixtures/topogolyInformation.js create mode 100644 test/lib.bignum.js create mode 100644 test/lib/config.tpl.json create mode 100644 test/lib/index.js create mode 100644 test/mocha.opts create mode 100644 test/request.Authenticate.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fa8a12e --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules +lib-cov +coverage.html +config.json \ No newline at end of file diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..f625d16 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,39 @@ +{ + "node": true, + "asi": false, + "boss": false, + "eqnull": false, + "evil": false, + "expr": false, + "funcscope": false, + "smarttabs": true, + "shadow": false, + "sub": false, + "supernew": false, + "bitwise": true, + "camelcase": true, + "curly": true, + "eqeqeq": true, + "forin": true, + "immed": true, + "indent": 2, + "latedef": false, + "loopfunc": false, + "maxerr": 7, + "newcap": true, + "noarg": true, + "nonew": true, + "plusplus": false, + "quotmark": "single", + "regexdash": false, + "undef": true, + "unused": true, + "strict": true, + "trailing": true, + "white": true, + "predef": [ + "describe", + "it" + ] +} + diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..0f09758 --- /dev/null +++ b/.npmignore @@ -0,0 +1,3 @@ +.git* +examples/ +test/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0ae2ca1 --- /dev/null +++ b/Makefile @@ -0,0 +1,36 @@ +REPORTER = list +DOCUMENT_ROOT = /z/node-hdb + +check: test + +test: + @NODE_ENV=test ./node_modules/.bin/mocha \ + --reporter $(REPORTER) \ + --recursive \ + --bail + +test-unit: + @NODE_ENV=test ./node_modules/.bin/mocha \ + --reporter $(REPORTER) + +test-acceptance: + @NODE_ENV=test ./node_modules/.bin/mocha \ + --reporter $(REPORTER) \ + --bail \ + test/acceptance/*.js + +test-cov: lib-cov + @HDB_COV=1 $(MAKE) -s test REPORTER=html-cov > coverage.html + +test-cov-pub: test-cov + @cp -f coverage.html $(DOCUMENT_ROOT) + @$(MAKE) -s clean + +lib-cov: + @jscoverage lib lib-cov + +clean: + @rm -f coverage.html \ + rm -fr lib-cov + +.PHONY: test test-unit test-acceptance clean \ No newline at end of file diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..f9af85b --- /dev/null +++ b/NOTICE @@ -0,0 +1,4 @@ +SAP HANA Database client for node.js + Copyright 2013 SAP AG. + + This product includes software developed at SAP AG (http://www.sap.com) diff --git a/README.md b/README.md index 2eecb29..1eccafd 100644 --- a/README.md +++ b/README.md @@ -1 +1,248 @@ -THE SAP HANA driver for node.js +SAP HANA Database Client for Node +==================================== + +A JavaScript client for Node implementing the +[SAP HANA Database SQL Command Network Protocol](http://help.sap.com/hana/SAP_HANA_Database_SQL_command_network_protocol_en.pdf). + + +Install +------- + +Currently this module has to be installed from the [GitHub repository](https://github.com/sap/node-hdb): + +``` +$ npm install https://github.com/sap/node-hdb +``` + +Introduction +------------ + +A very simple example how to use this module: + +```js +var hdb = require('hdb'); +var client = hdb.createClient({ + host : 'hostname', + port : 30015, + user : 'user', + password : 'secret' +}).connect(); + +client.exec('select * from DUMMY', function(err, rows) { + if (err) { + console.error('Error:', err); + } else { + console.log('Results:', rows); + } + client.end(); +}); +``` + +Authentication methods +---------------------- + +The SAP HANA Database supports the following authentication methods: + +- **SCRAMSHA256** user/password based authentication method +- _GSS_ +- _SAML_ + +Currently only the SCRAMSHA256 authentication method is supported. + + +Establishing a connection to the database +----------------------------------------- + +In order to be able to handle connection errors it is recommended to explicitly +establish a connection using the `connect` method of the client object. + +```js +var hdb = require('hdb'); +var client = hdb.createClient({ + host : 'hostname', + port : 30015 +}); + +client.connect({ + user : 'user', + password : 'secret' +}, function(err) { + if (err) { + console.error('Client connection error:', err); + } else { + console.log('Client connected!'); + } +}); +``` + +Direct Statement Execution +-------------------------- + +Direct statement execution is the simplest way to execute SQL statements. +The only input parameter is the SQL command to be executed. +Generally we return the statement execution results using callbacks. +For callbacks we follow the convention described in the +[Node.js Style Guide](http://nodeguide.com/style.html#callbacks) +to reserve the first parameter of any callback for an optional error object. +The type of returned result depends on the kind of statement. + +### DDL Statement + +In the case of a DDL Statement nothing is returned. + +```js +client.exec('create table TEST.NUMBERS (a int, b varchar(16))', function(err) { + if (err) { + return console.error('Error:', err); + } + console.log('Table TEST.NUMBERS has been created'); +}); +``` + +### DML Statement + +In the case of a DML Statement the number of `affectedRows` is returned. + +```js +client.exec('insert into TEST.NUMBERS values (1, \'one\')', function(err, affectedRows) { + if (err) { + return console.error('Error:', err); + } + console.log('Number of affected rows:', affectedRows); +}); +``` + +### Query + +In the case of a Query all selected `rows` are fetched and returned in the callback. + +```js +client.exec('select A, B from TEST.NUMBERS oder by A', function(err, rows) { + if (err) { + return console.error('Error:', err); + } + console.log('Rows:', rows); +}); + +``` + +Prepared Statement Execution +---------------------------- + +### Prepare a Statement + +The client returns a `statement` object which can be executed multiple times. + +```js +client.prepare('call * from DUMMY where X = ?', function (err, statement){ + if (err) { + return console.error('Error:', err); + } + // do something with the statement + console.log('StatementId', statement.id); +}); +``` + +### Execute a Statement + +```js +statement.exec([1], function (err, rows) { + if (err) { + return console.error('Error:', err); + } + console.log('Rows:', rows); +}); +``` + +### Calling Stored Procedures + +If you have for example the following stored procedure: + +```sql +create procedure PROC_DUMMY (in a int, in b int, out c int, out d DUMMY, out e TABLES) + language sqlscript + reads sql data as + begin + c := :a + :b; + d = select * from DUMMY; + e = select * from TABLES; + end +``` +you can call it via a prepared statement. +The second argument is always an object with the scalar parameters. +If there are no scalar parameters, an empty object ``{}`` will be returned. +The following arguments are the `resultSets`. + +```js +client.prepare('call PROC_DUMMY (?, ?, ?, ?, ?)', function(err, statement){ + if (err) { + return console.error('Prepare error:', err); + } + statement.exec({ + A: 3, + B: 4 + }, function(err, parameters, dummyRows, tableRows) { + if (err) { + return console.error('Exec error:', err); + } + console.log('Parameters:', parameters); + console.log('Dummies:', dummyRows); + console.log('Tables:', tableRows); + }); +}); +``` + +### Drop Statement + +To drop the statement simply call + +```js +statement.drop(function(err){ + if (err) { + return console.error('Drop error:', err); + } +}); +``` + +Running tests +------------- + +To run the unit tests for _hdb_ simply run: + +```bash +$ make test-unit +``` + +To run the unit tests as well as acceptance tests for _hdb_ you have to run: + +```bash +$ make test +``` + +For the acceptance tests a database connection has to be established. Therefore you +need to copy the configuration template [config.tpl.json](./test/lib/config.tpl.json) +in the ```test/lib``` folder to ```config.json``` and change the connection data to yours. + + +Running examples +---------------- + +Also, for the [examples](./examples) you need a valid a ```config.json``` in the ```test/lib``` folder. + +The example for call procedure: + +```bash +$ node examples/call1 +``` + +Todo +---- +* Finish support for Lob data types + * Support for read Lob in development + * Support for write Lob not yet started +* Improve documentation of the client api +* Improve error handling +* SAML Authentication support +* Transaction handling +* Enhance tests +* ... diff --git a/examples/app1.js b/examples/app1.js new file mode 100644 index 0000000..30085e9 --- /dev/null +++ b/examples/app1.js @@ -0,0 +1,32 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = require('util'); +var client = require('./client'); + +var sql = + 'select SCHEMA_NAME || \'.\' || TABLE_NAME from TABLES'; + +client.exec(sql, done); + +function done(err, rows) { + client.end(); + if (err) { + return console.error(err); + } + console.log(util.inspect(rows, { + colors: true + })); +} \ No newline at end of file diff --git a/examples/app2.js b/examples/app2.js new file mode 100644 index 0000000..6378f52 --- /dev/null +++ b/examples/app2.js @@ -0,0 +1,41 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = require('util'); +var client = require('./client'); + +var sql = + 'select top 50 SCHEMA_NAME || \'.\' || TABLE_NAME as TABLE from TABLES'; +client.exec(sql, false, function onexec(err, rs) { + rs.fetch(function onfetch(err, rows) { + if (err) { + return done(err); + } + if (!rs.closed) { + rs.close(); + } + done(null, rows); + }); +}); + +function done(err, rows) { + client.end(); + if (err) { + return console.error(err); + } + console.log(util.inspect(rows, { + colors: true + })); +} \ No newline at end of file diff --git a/examples/app3.js b/examples/app3.js new file mode 100644 index 0000000..99e9799 --- /dev/null +++ b/examples/app3.js @@ -0,0 +1,46 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = require('util'); +var client = require('./client'); + +var sql = + 'select top 50 SCHEMA_NAME || \'.\' || TABLE_NAME as TABLE from TABLES'; +client.exec(sql, false, function onexec(err, rs) { + var rows = []; + rs.createReadStream() + .once('error', function onerror(err) { + done(err); + }) + .on('readable', function onreadable() { + rows = rows.concat(this.read()); + }) + .once('end', function onend() { + if (!rs.closed) { + rs.close(); + } + done(null, rows); + }); +}); + +function done(err, rows) { + client.end(); + if (err) { + return console.error(err); + } + console.log(util.inspect(rows, { + colors: true + })); +} \ No newline at end of file diff --git a/examples/app4.js b/examples/app4.js new file mode 100644 index 0000000..9fe92e0 --- /dev/null +++ b/examples/app4.js @@ -0,0 +1,41 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = require('util'); +var client = require('./client'); +var hdb = require('../index'); + +var sql = + 'select top 50 SCHEMA_NAME || \'.\' || TABLE_NAME as TABLE from TABLES'; +client.exec(sql, false, function onexec(err, rs) { + rs.createReadStream() + .once('error', function onerror(err) { + done(err); + }) + .once('end', function onend() { + if (!rs.closed) { + rs.close(); + } + done(null); + }) + .pipe(hdb.createJSONStringifier()).pipe(process.stdout); +}); + +function done(err, rows) { + client.end(); + if (err) { + return console.error(err); + } +} \ No newline at end of file diff --git a/examples/app5.js b/examples/app5.js new file mode 100644 index 0000000..343187b --- /dev/null +++ b/examples/app5.js @@ -0,0 +1,52 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var client = require('./client'); +var Lob = require('../lib/protocol/Lob'); + +var sql = 'select cdata, bdata from _SYS_REPO.ACTIVE_OBJECT ' + + 'where PACKAGE_ID = ? and OBJECT_NAME = ? and OBJECT_SUFFIX = ?'; +var values1 = [ + 'sap.ui5.1.resources', + 'sap-ui-core-dbg', 'js' +]; +var values2 = [ + 'sap.ui5.1.test-resources.sap.ui.core.samples.components', + 'SAPLogo', 'png' +]; +var values = values1; +var filename = path.join(__dirname, values.slice(1).join('.')); +client.prepare(sql, function onprepare(err, statement) { + if (err) { + return console.error('Prepare error:', err); + } + statement.exec(values, function onexec(err, rows) { + if (err) { + return console.error('Exec error:', err); + } + var lobDescriptor = rows[0].BDATA || rows[0].CDATA; + var lob = new Lob(client._connection, lobDescriptor); + lob.createReadStream(lobDescriptor) + .on('error', function onerror(err) { + console.error('Lob error:', err); + }) + .on('end', function onend() { + client.end(); + }) + .pipe(fs.createWriteStream(filename)); + }); +}); \ No newline at end of file diff --git a/examples/call1.js b/examples/call1.js new file mode 100644 index 0000000..1c4b146 --- /dev/null +++ b/examples/call1.js @@ -0,0 +1,76 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = require('util'); +var async = require('async'); +var client = require('./client'); + +async.series([setSchema, createProcDummy, callProcDummy], done); + +function setSchema(cb) { + var schema = client.get('user'); + var sql = util.format('set schema %s', schema); + client.exec(sql, cb); +} + +function createProcDummy(cb) { + var sql = 'drop procedure PROC_DUMMY'; + client.exec(sql, function onexec() { + // ignore error + var sql = [ + 'create procedure PROC_DUMMY (in a int, in b int, out c int, out d DUMMY)', + 'language sqlscript', + 'reads sql data as', + 'begin', + ' c := :a + :b;', + ' d = select * from DUMMY;', + 'end;' + ].join('\n'); + client.exec(sql, cb); + }); +} + +function callProcDummy(cb) { + var sql = 'call PROC_DUMMY (?, ?, ?, ?)'; + client.prepare(sql, function onprepare(err, statement) { + if (err) { + return cb(err); + } + statement.exec({ + A: 3, + B: 4 + }, function onexec(err, parameters, rows) { + statement.drop(); + if (err) { + return cb(err); + } + cb(null, { + C: parameters.C, + rows: rows + }); + }); + }); +} + +function done(err, results) { + client.end(); + if (err) { + return console.error('error', err); + } + console.log(util.inspect(results[2], { + depth: 4, + colors: true + })); +} \ No newline at end of file diff --git a/examples/client.js b/examples/client.js new file mode 100644 index 0000000..557eb16 --- /dev/null +++ b/examples/client.js @@ -0,0 +1,54 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var hdb = require('../index'); +var filename = path.join(__dirname, '..', 'test', 'lib', 'config.json'); +var options = JSON.parse(fs.readFileSync(filename)); + +var client = hdb.createClient({ + host: options.host, + port: options.port, + user: options.user +}); + +function onerror(err) { + throw err; + console.log('Network connection error', err); +} +client.on('error', onerror); + +function onclose() { + console.log('Client closed'); +} +client.on('close', onclose); + +function onconnect() { + console.log('Client connected'); +} +client.on('connect', onconnect); + +function ondisconnect() { + console.log('Client disconnected'); +} +client.on('disconnect', ondisconnect); + +client.connect({ + user: options.user, + password: options.password +}); + +module.exports = client; \ No newline at end of file diff --git a/examples/pool.js b/examples/pool.js new file mode 100644 index 0000000..cdde817 --- /dev/null +++ b/examples/pool.js @@ -0,0 +1,46 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var gp = require('generic-pool'); +var hdb = require('../index'); +var filename = path.join(__dirname, '..', 'test', 'lib', 'config.json'); +var options = JSON.parse(fs.readFileSync(filename)); + +var pool = gp.Pool({ + name: 'hdb', + create: function (callback) { + var client = hdb.createClient(options) + client.on('error', function onerror(err) { + console.error('Client error:', err); + }); + client.connect(function onconnect(err) { + if (err) { + return callback(err); + } + callback(null, client); + }); + }, + destroy: function ondestroy(client) { + client.end(); + }, + max: 3, + min: 1, + idleTimeoutMillis: 30000, + log: false +}); + +exports = module.exports = pool; \ No newline at end of file diff --git a/examples/server.js b/examples/server.js new file mode 100644 index 0000000..b70296b --- /dev/null +++ b/examples/server.js @@ -0,0 +1,113 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = require('util'); +var os = require('os'); +var url = require('url'); +var http = require('http'); +var pool = require('./pool'); +var hdb = require('../index'); +var hostname = os.hostname().toLowerCase(); +var port = process.env.PORT || 1337; + +if (!/\.wdf\.sap\.corp$/.test(hostname)) { + hostname += '.dhcp.wdf.sap.corp'; +} + +http.createServer(function (req, res) { + if (req.url === '/favicon.ico') { + res.writeHead(404); + res.end(); + return; + } + pool.acquire(function (err, client) { + if (err) { + return badRequest(res, err); + } + + var data = parseRequest(req); + res.writeHead(200, data.headers); + + console.log(data.sql); + + client.exec(data.sql, false, function onexec(err, rs) { + if (err) { + return badRequest(res, err); + } + var transform = createStringifier(); + rs.createReadStream() + .once('end', function onend() { + pool.release(client); + }) + .once('error', function (err) { + pool.release(client); + }) + .pipe(transform) + .pipe(res); + }); + + }); +}).listen(port); +console.log('Server running on at http://%s:%d/', hostname, port); + +function createStringifier() { + return new hdb.Stringifier({ + header: '[', + footer: ']', + seperator: ',', + stringify: JSON.stringify + }); +} + +function badRequest(res, err) { + res.writeHead(400, { + 'Content-Type': 'text/plain' + }); + res.end(err.message); +} + +function escapeQualifiedName(name) { + return '"' + name.toUpperCase().replace(/"/g, '""').replace(/\./g, '"."') + + '"'; +} + +function parseRequest(req) { + var reqUrl = url.parse(req.url, true); + var query = reqUrl.query || {}; + var top = parseInt(query.top, 10) || 10000; + + var path = reqUrl.pathname.split('/'); + path.shift(); + if (!path[path.length - 1]) { + path.pop(); + } + + var tablename = path.map(function normalize(segment) { + return segment.toLowerCase(); + }).join('.') || 'tables'; + + var headers = { + 'Content-Type': 'application/json' + }; + if (top > 10000) { + headers['Content-Disposition'] = 'attachment; filename=' + tablename + + '.json;'; + } + return { + sql: util.format('select * from %s limit %d', escapeQualifiedName(tablename), + top), + headers: headers + }; +} \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..62d7315 --- /dev/null +++ b/index.js @@ -0,0 +1,22 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var lib = require('./lib'); +exports.Client = lib.Client; +exports.Stringifier = lib.Stringifier; +exports.createJSONStringifier = lib.createJSONStringifier; +exports.createClient = function createClient(options) { + return new lib.Client(options); +}; \ No newline at end of file diff --git a/lib/Client.js b/lib/Client.js new file mode 100644 index 0000000..6fdc71e --- /dev/null +++ b/lib/Client.js @@ -0,0 +1,183 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = require('./util'); +var EventEmitter = require('events').EventEmitter; +var Connection = require('./protocol/Connection'); +var Result = require('./protocol/Result'); +var Statement = require('./protocol/Statement'); +var ResultSet = require('./protocol/ResultSet'); + +module.exports = Client; + +util.inherits(Client, EventEmitter); + +function Client(options) { + EventEmitter.call(this); + + this._settings = util.extend({}, options); + this._connection = new Connection(); + + var self = this; + + function cleanup() { + self._connection.removeListener('error', onerror); + } + + function onerror(err) { + self.emit('error', err); + } + this._connection.on('error', onerror); + + function onclose(hadError) { + cleanup(); + self.emit('close', hadError); + } + this._connection.once('close', onclose); +} + +Object.defineProperties(Client.prototype, { + connectOptions: { + get: function getConnectOptions() { + return this._connection.connectOptions; + } + }, + clientId: { + get: function getConnectOptions() { + return this._connection.clientId; + } + }, +}); + +Client.prototype.get = function (key) { + if (key === undefined) { + return this._settings; + } + return this._settings[key]; +}; + +Client.prototype.set = function (key, value) { + if (!value && typeof util.isObject(key)) { + this._settings = util.extend(this._settings, key); + } else { + this._settings[key] = value; + } + return this; +}; + +Client.prototype.connect = function connect(options, cb) { + if (util.isFunction(options)) { + cb = options; + options = {}; + } + + var openOptions = { + host: this._settings.host, + port: this._settings.port + }; + + var authOptions = util.extend({ + user: this._settings.user, + password: this._settings.password + }, options); + + var self = this; + var connection = this._connection; + + function done(err) { + if (!err) { + self.emit('connect'); + } + if (util.isFunction(cb)) { + cb(err); + } + } + + function authenticate() { + connection.connect(authOptions, done); + } + + if (connection.readyState === 'closed') { + connection.open(openOptions, authenticate); + } else { + authenticate(); + } + return this; +}; + +Client.prototype.disconnect = function disconnect(cb) { + + function done(err) { + /* jshint validthis:true */ + if (!err) { + this.emit('disconnect'); + } + if (util.isFunction(cb)) { + cb(err); + } + } + this._connection.disconnect(done.bind(this)); + return this; +}; + +Client.prototype.end = function end() { + this._connection.close(); +}; + +Client.prototype.prepare = function prepare(command, cb) { + var options; + if (util.isString(command)) { + options = { + command: command + }; + } else if (util.isObject(command)) { + options = command; + } + + var statement = new Statement(this._connection); + this._connection.prepare({ + command: command + }, function onreply(err, reply) { + statement.handle(err, reply, cb); + }); + return this; +}; + +Client.prototype.exec = function exec(command, options, cb) { + var defaults = { + autoFetch: true + }; + if (util.isFunction(options)) { + cb = options; + options = defaults; + } else if (util.isObject(options)) { + options = util.extend(defaults, options); + } else { + var autoFetch = !! options; + options = defaults; + options.autoFetch = autoFetch; + } + var result = new Result(this._connection, options); + this._connection.executeDirect({ + command: command + }, function onreply(err, reply) { + result.handle(err, reply, cb); + }); + return this; +}; + +Client.prototype.createResultSet = function createResultSet(options) { + return new ResultSet(this._connection, options); +}; \ No newline at end of file diff --git a/lib/Lob.js b/lib/Lob.js new file mode 100644 index 0000000..a726efc --- /dev/null +++ b/lib/Lob.js @@ -0,0 +1 @@ +'use strict'; \ No newline at end of file diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..56eb15c --- /dev/null +++ b/lib/index.js @@ -0,0 +1,26 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = exports.util = require('./util'); +util.extend(exports, require('./protocol')); +exports.Client = require('./Client'); +exports.createJSONStringifier = function createJSONStringifier() { + return new exports.Stringifier({ + header: '[', + footer: ']', + seperator: ',', + stringify: JSON.stringify + }); +}; \ No newline at end of file diff --git a/lib/protocol/Connection.js b/lib/protocol/Connection.js new file mode 100644 index 0000000..0d105b7 --- /dev/null +++ b/lib/protocol/Connection.js @@ -0,0 +1,446 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var net = require('net'); +var os = require('os'); +var EventEmitter = require('events').EventEmitter; +var auth = require('./auth'); +var util = require('../util'); +var common = require('./common'); +var request = require('./request'); +var reply = require('./reply'); +var ReplySegment = reply.Segment; +var MessageTypeName = common.MessageTypeName; +var SegmentKind = common.SegmentKind; +var bignum = util.bignum; + +var MAX_PACKET_SIZE = Math.pow(2, 17); +var PACKET_HEADER_LENGTH = 32; + +module.exports = Connection; + +util.inherits(Connection, EventEmitter); + +function Connection() { + EventEmitter.call(this); + + this._socket = undefined; + this._queue = new util.Queue().pause(); + this._state = new ConnectionState(); +} + +Connection.open = function openConnection(options, cb) { + var connection = new Connection(); + connection.open(options, cb); + return connection; +}; + +Object.defineProperties(Connection.prototype, { + connectOptions: { + get: function getConnectOptions() { + return this._state.connectOptions; + } + }, + clientId: { + get: function getConnectOptions() { + return this._state.clientId; + } + }, + readyState: { + get: function getReadyState() { + if (!this._socket) { + return 'closed'; + } + var sessionId = this._state.sessionId; + if (util.isUndefined(sessionId)) { + return 'opening'; + } + if (this._queue.running === true) { + if (parseInt(sessionId, 10) > 0) { + return 'connected'; + } + return 'connecting'; + } + return this._socket.readyState; + } + } +}); + +Connection.prototype.open = function open(options, cb) { + var self = this; + + if (this._socket) { + return; + } + + this._socket = net.connect(util.extend(options, { + allowHalfOpen: false + }), function connectListener() { + self._socket.write(initializationRequestBuffer); + }); + this._socket.setNoDelay(true); + + function cleanup() { + self._socket.removeListener('error', onerror); + self._socket = undefined; + self._queue.abort(); + self._state = new ConnectionState(); + } + + function onerror(err) { + self.emit('error', err); + } + this._socket.on('error', onerror); + + function onclose(hadError) { + cleanup(); + self.emit('close', hadError); + } + this._socket.once('close', onclose); + + this._socket.once('readable', readInitializationReply.bind(this, cb)); + return this; +}; + +Connection.prototype.enqueue = function enqueue(msg, cb) { + var task = this._queue.createTask(send.bind(this, msg), cb); + task.name = MessageTypeName[msg.type]; + this._queue.push(task); +}; + +Connection.prototype.connect = function connect(options, cb) { + var state = this._state; + + if (state.connecting === true) { + return cb(new Error('Already connecting')); + } + state.connecting = true; + + var name = options.algorithm || 'SCRAMSHA256'; + var authMethod = auth[name]; + var algorithm = new authMethod.Algorithm(options.clientChallenge); + + var authOptions = { + authentication: { + user: options.user, + algorithm: name, + clientChallenge: algorithm.clientChallenge + } + }; + var authMessage = request.authenticate(authMethod, authOptions); + + var connOptions = { + authentication: { + user: options.user, + algorithm: name, + clientProof: undefined + }, + clientId: this.clientId, + connectOptions: this.connectOptions + }; + + function connReceive(err, reply) { + /* jshint validthis:true */ + state.connecting = false; + if (err) { + return cb(err); + } + this._queue.resume(); + cb(null, reply); + } + + function authReceive(err, reply) { + /* jshint validthis:true */ + if (err) { + state.connecting = false; + return cb(err); + } + + var authReply = authMethod.Authentication.convert(reply.authentication); + algorithm.salts = [authReply.salt]; + algorithm.serverChallenge = authReply.serverChallenge; + connOptions.authentication.clientProof = algorithm.getClientProof(options.password); + var connMessage = request.connect(authMethod, connOptions); + send.call(this, connMessage, connReceive.bind(this)); + } + send.call(this, authMessage, authReceive.bind(this)); +}; + +Connection.prototype.disconnect = function disconnect(cb) { + var self = this; + var state = this._state; + + if (state.disconnecting === true) { + return cb(new Error('Already disconnecting')); + } + state.disconnecting = true; + + function done(err, reply) { + state.disconnecting = false; + if (err) { + return cb(err); + } + state.sessionId = -1; + state.packetCount = -1; + cb(null, reply); + } + + function enqueueDisconnect() { + self.enqueue(request.disconnect(), done); + } + + if (this._queue.empty && !this._queue.busy) { + return enqueueDisconnect(); + } + this._queue.once('drain', enqueueDisconnect); +}; + +Connection.prototype.executeDirect = function executeDirect(sql, cb) { + var options; + if (util.isString(sql)) { + options = { + command: sql + }; + } else if (util.isObject(sql)) { + options = sql; + } + this.enqueue(request.executeDirect(options), cb); +}; + +Connection.prototype.prepare = function prepare(sql, cb) { + var options; + if (util.isString(sql)) { + options = { + command: sql + }; + } else if (util.isObject(sql)) { + options = sql; + } + this.enqueue(request.prepare(options), cb); +}; + +Connection.prototype.readLob = function readLob(options, cb) { + if (options.locatorId) { + options = { + readLobRequest: options + }; + } + this.enqueue(request.readLob(options), cb); +}; + +Connection.prototype.execute = function execute(options, cb) { + this.enqueue(request.execute(options), cb); +}; + +Connection.prototype.fetchNext = function fetchNext(options, cb) { + this.enqueue(request.fetchNext(options), cb); +}; + +Connection.prototype.closeResultSet = function closeResultSet(resultSetId, cb) { + var options; + if (Buffer.isBuffer(resultSetId)) { + options = { + resultSetId: resultSetId + }; + } else if (util.isObject(resultSetId)) { + options = resultSetId; + } + this.enqueue(request.closeResultSet(options), cb); +}; + +Connection.prototype.dropStatement = function dropStatement(statementId, cb) { + var options; + if (Buffer.isBuffer(statementId)) { + options = { + statementId: statementId + }; + } else if (util.isObject(statementId)) { + options = statementId; + } + this.enqueue(request.dropStatementId(options), cb); +}; + +Connection.prototype.close = function close() { + if (this._queue.empty && !this._queue.busy) { + return endSocket(this._socket); + } + this._queue.once('drain', endSocket.bind(null, this._socket)); +}; + +function endSocket(socket) { + socket.readable = false; + socket.end(); +} + +function send(message, receive) { + /* jshint validthis:true */ + + var state = this._state; + var buffer = messageToBuffer(message); + var length = buffer.length - PACKET_HEADER_LENGTH; + + state.receive = receive; + // Increaee packet count + state.packetCount++; + // Session identifier + bignum.writeUInt64LE(buffer, state.sessionId, 0); + // Packet sequence number in this session + // Packets with the same sequence number belong to one request / reply pair + buffer.writeUInt32LE(state.packetCount, 8); + // Used space in this packet + buffer.writeUInt32LE(length, 12); + // Total space in this buffer + buffer.writeUInt32LE(MAX_PACKET_SIZE, 16); + // Number of segments in this packet + buffer.writeUInt16LE(1, 20); + // Filler + buffer.fill(0x00, 22, PACKET_HEADER_LENGTH); + // Write request packet to socket + if (this._socket) { + this._socket.write(buffer); + } +} + +function messageToBuffer(message) { + var buffer; + if (util.isBuffer(message)) { + buffer = message; + } else { + buffer = message.toBuffer(MAX_PACKET_SIZE - PACKET_HEADER_LENGTH); + } + var target = new Buffer(PACKET_HEADER_LENGTH + buffer.length); + buffer.copy(target, PACKET_HEADER_LENGTH); + return target; +} + +function readInitializationReply(cb) { + /* jshint validthis:true */ + var state = this._state; + var chunk; + + chunk = this._socket.read(InitializationReply.LENGTH); + if (chunk === null) { + return; + } + this._socket.read(); + this._socket.on('readable', readReply.bind(this)); + + state.sessionId = -1; + state.packetCount = -1; + + var info = InitializationReply.read(chunk, 0); + this.emit('open', info); + if (util.isFunction(cb)) { + cb(info); + } +} + +function readReply() { + /* jshint validthis:true */ + var state = this._state; + var chunk; + + if (util.isUndefined(state.packetHeader)) { + chunk = this._socket.read(PACKET_HEADER_LENGTH); + if (chunk === null) { + return; + } + state.packetHeader = PacketHeader.read(chunk, 0); + if (state.sessionId !== state.packetHeader.sessionId) { + state.sessionId = state.packetHeader.sessionId; + state.packetCount = -1; + } + } + chunk = this._socket.read(state.packetHeader.length); + if (chunk === null) { + return; + } + this._socket.read(); + + state.packetHeader = undefined; + var cb = state.receive; + state.receive = undefined; + if (util.isFunction(cb)) { + var error, segment, reply; + try { + segment = ReplySegment.create(chunk, 0); + reply = segment.getReply(); + if (reply.kind === SegmentKind.ERROR) { + error = reply.error; + } + } catch (err) { + error = err; + error.code = 'PROTOCOL_PARSE_ERROR'; + } + cb(error, reply); + } +} + +function ConnectionState() { + var pid = process.pid || 'nodejs'; + this.clientId = [pid, os.hostname()].join('@'); + this.connectOptions = common.DEFAULT_CONNECT_OPTIONS.slice(0); + this.connecting = false; + this.disconnecting = false; + this.sessionId = undefined; + this.packetCount = undefined; + this.packetHeader = undefined; + this.receive = undefined; +} + +function PacketHeader(sessionId, packetCount, length) { + this.sessionId = sessionId; + this.packetCount = packetCount; + this.length = length; +} + +PacketHeader.read = function readPacketHeader(buffer, offset) { + offset = offset || 0; + + return new PacketHeader( + bignum.readUInt64LE(buffer, offset), + buffer.readUInt32LE(offset + 8), + buffer.readUInt32LE(offset + 12) + ); +}; + +function Version(major, minor) { + this.major = major; + this.minor = minor; +} + +Version.read = function readVersion(buffer, offset) { + return new Version( + buffer.readInt8(offset), + buffer.readInt16LE(offset + 1) + ); +}; + +function InitializationReply(productVersion, protocolVersion) { + this.productVersion = productVersion; + this.protocolVersion = protocolVersion; +} + +InitializationReply.LENGTH = 8; + +InitializationReply.read = function readInitializationReply(buffer) { + var productVersion = Version.read(buffer, 0); + var protocolVersion = Version.read(buffer, 3); + return new InitializationReply(productVersion, protocolVersion); +}; + +var initializationRequestBuffer = new Buffer([ + 0xff, 0xff, 0xff, 0xff, 4, 20, 0x00, 4, 1, 0x00, 0x00, 1, 1, 1 +]); \ No newline at end of file diff --git a/lib/protocol/Lob.js b/lib/protocol/Lob.js new file mode 100644 index 0000000..3b2fc97 --- /dev/null +++ b/lib/protocol/Lob.js @@ -0,0 +1,215 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = require('../util'); +var EventEmitter = require('events').EventEmitter; +var Readable = require('stream').Readable; + +module.exports = Lob; + +util.inherits(Lob, EventEmitter); + +Lob.DEFAULT_READ_SIZE = Math.pow(2, 14); +Lob.MAX_READ_SIZE = Math.pow(2, 18); +Lob.TYPE_BLOB = 0; +Lob.TYPE_CLOB = 1; + +function Lob(connection, lob) { + EventEmitter.call(this); + + this._connection = connection; + this._running = undefined; + this._readSize = Lob.DEFAULT_READ_SIZE; + this.type = lob.type; + this.id = lob.locatorId; + this.byteLength = lob.byteLength; + this.charLength = lob.charLength; + this.ended = false; + this.offset = 0; + this._data = null; + if (Buffer.isBuffer(lob.chunk) && lob.chunk.length) { + var size = lob.chunk.length; + if (this.type === Lob.TYPE_CLOB) { + size = lob.chunk.toString('utf-8').length; + } + this._data = { + isLast: lob.isLast, + chunk: lob.chunk, + size: size + }; + } +} + +Object.defineProperties(Lob.prototype, { + readSize: { + get: function getFetchSize() { + return this._readSize; + } + } +}); + +Lob.prototype.setReadSize = function setReadSize(readSize) { + if (readSize > Lob.MAX_READ_SIZE) { + this._readSize = Lob.MAX_READ_SIZE; + } + this._readSize = readSize; + return this; +}; + +Lob.prototype.read = function read(cb) { + if (typeof this._running !== 'undefined') { + var err = new Error('Lob invalid state error'); + return cb(err); + } + readLob(this, cb); +}; + +Lob.prototype.createReadStream = function createReadStream(options) { + if (typeof this._running !== 'undefined') { + return null; + } + return createReadLobStream(this, options); +}; + +Lob.prototype.stopRead = function stopRead() { + this._running = false; + return this; +}; + +Lob.prototype.startRead = function startRead() { + if (!this._running && !this.ended) { + this._running = true; + if (util.isObject(this._data)) { + handleData.call(this, this._data); + this._data = undefined; + } else { + sendReadLob.call(this); + } + } + return this; +}; + +function createReadLobStream(lob, options) { + options = options || {}; + var readable = new Readable(options); + readable._read = function _read() { + lob.startRead(); + }; + + function cleanup() { + lob.removeListener('error', onerror); + lob.removeListener('data', ondata); + lob.removeListener('end', onend); + } + + function onerror(err) { + cleanup(); + readable.emit('error', err); + } + lob.once('error', onerror); + + function ondata(chunk) { + if (!readable.push(chunk)) { + lob.stopFetch(); + } + } + lob.on('data', ondata); + + function onend() { + cleanup(); + readable.push(null); + } + lob.once('end', onend); + + return readable; +} + +function readLob(lob, cb) { + var offset = 0; + var buffer = new Buffer(lob.byteLength); + + function done(err) { + lob.removeListener('error', onerror); + lob.removeListener('data', ondata); + lob.removeListener('end', onend); + if (util.isFunction(cb)) { + cb(err, buffer); + } + } + + function onerror(err) { + done(err); + } + lob.on('error', onerror); + + function ondata(chunk) { + chunk.copy(buffer, offset); + offset += chunk.length; + } + lob.on('data', ondata); + + function onend() { + done(null); + } + lob.on('end', onend); + lob.startRead(); +} + +function sendReadLob() { + /* jshint validthis:true */ + + this._connection.readLob({ + locatorId: this.id, + offset: this.offset + 1, + length: this._readSize + }, receiveData.bind(this)); +} + +function receiveData(err, reply) { + /* jshint validthis:true */ + + if (err) { + this._running = false; + this.emit('error', err); + return; + } + + var data = reply.readLobReply; + if (this._running) { + handleData.call(this, data); + } else { + this._data = data; + } +} + +function handleData(data) { + /* jshint validthis:true */ + + if (Buffer.isBuffer(data.chunk)) { + this.offset += data.size || this._readSize; + this.emit('data', data.chunk); + } + + if (data.isLast) { + this.ended = true; + this._running = false; + process.nextTick(this.emit.bind(this, 'end')); + return; + } + + if (this._running) { + process.nextTick(sendReadLob.bind(this)); + } +} \ No newline at end of file diff --git a/lib/protocol/Parser.js b/lib/protocol/Parser.js new file mode 100644 index 0000000..5ae83c4 --- /dev/null +++ b/lib/protocol/Parser.js @@ -0,0 +1,441 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = require('../util'); +var common = require('./common'); +var bignum = util.bignum; +var TypeCode = common.TypeCode; + +var READ_TINYINT = 'readTinyInt()'; +var READ_SMALLINT = 'readSmallInt()'; +var READ_INT = 'readInt()'; +var READ_BIGINT = 'readBigInt()'; +var READ_STRING = 'readBytes(\'utf-8\')'; +var READ_BINARY = 'readBytes()'; +var READ_DATE = 'readDate()'; +var READ_DAYDATE = 'readDayDate()'; +var READ_TIME = 'readTime()'; +var READ_SECONDTIME = 'readSecondTime()'; +var READ_TIMESTAMP = 'readTimestamp()'; +var READ_LONGDATE = 'readLongDate()'; +var READ_SECONDDATE = 'readSecondDate()'; +var READ_LOB = 'readLob(0)'; +var READ_NCLOB = 'readLob(1)'; +var READ_DOUBLE = 'readDouble()'; +var READ_FLOAT = 'readFloat()'; +var READ_DECIMAL = 'readDecimal(%d)'; + +var readFunctionMap = {}; +readFunctionMap[TypeCode.TINYINT] = READ_TINYINT; +readFunctionMap[TypeCode.SMALLINT] = READ_SMALLINT; +readFunctionMap[TypeCode.INT] = READ_INT; +readFunctionMap[TypeCode.BIGINT] = READ_BIGINT; +readFunctionMap[TypeCode.STRING] = READ_STRING; +readFunctionMap[TypeCode.VARCHAR1] = READ_STRING; +readFunctionMap[TypeCode.VARCHAR2] = READ_STRING; +readFunctionMap[TypeCode.CHAR] = READ_STRING; +readFunctionMap[TypeCode.NCHAR] = READ_STRING; +readFunctionMap[TypeCode.NVARCHAR] = READ_STRING; +readFunctionMap[TypeCode.NSTRING] = READ_STRING; +readFunctionMap[TypeCode.SHORTTEXT] = READ_STRING; +readFunctionMap[TypeCode.ALPHANUM] = READ_STRING; +readFunctionMap[TypeCode.BINARY] = READ_BINARY; +readFunctionMap[TypeCode.VARBINARY] = READ_BINARY; +readFunctionMap[TypeCode.BSTRING] = READ_BINARY; +readFunctionMap[TypeCode.DATE] = READ_DATE; +readFunctionMap[TypeCode.TIME] = READ_TIME; +readFunctionMap[TypeCode.TIMESTAMP] = READ_TIMESTAMP; +readFunctionMap[TypeCode.DAYDATE] = READ_DAYDATE; +readFunctionMap[TypeCode.SECONDTIME] = READ_SECONDTIME; +readFunctionMap[TypeCode.LONGDATE] = READ_LONGDATE; +readFunctionMap[TypeCode.SECONDDATE] = READ_SECONDDATE; +readFunctionMap[TypeCode.BLOB] = READ_LOB; +readFunctionMap[TypeCode.LOCATOR] = READ_LOB; +readFunctionMap[TypeCode.CLOB] = READ_NCLOB; +readFunctionMap[TypeCode.NCLOB] = READ_NCLOB; +readFunctionMap[TypeCode.NLOCATOR] = READ_NCLOB; +readFunctionMap[TypeCode.TEXT] = READ_NCLOB; +readFunctionMap[TypeCode.DOUBLE] = READ_DOUBLE; +readFunctionMap[TypeCode.REAL] = READ_FLOAT; +readFunctionMap[TypeCode.DECIMAL] = READ_DECIMAL; + +function createFunctionBody(metadata, nameProperty) { + var functionBody = ['var obj = {};']; + metadata.forEach(function (column, index) { + var fn = readFunctionMap[column.dataType]; + if (column.dataType === TypeCode.DECIMAL) { + fn = util.format(fn, column.fraction); + } + var key = (typeof nameProperty === 'string') ? column[nameProperty] : + index; + functionBody.push('obj["' + key + '"] = this.' + fn + ';'); + }); + functionBody.push('return obj;'); + return functionBody.join('\n'); +} + +module.exports = Parser; + +Parser.DEFAULT_THRESHOLD = 128; + +function Parser(metadata, options) { + /*jshint evil:true */ + options = options || {}; + if (typeof options.nameProperty === 'undefined') { + options.nameProperty = 'columnDisplayName'; + } + this._parseRow = new Function(createFunctionBody(metadata, options.nameProperty)); + this._threshold = options.threshold || Parser.DEFAULT_THRESHOLD; + this._queue = []; + this.taskId = 0; + if (typeof setImmediate !== 'undefined') { + this._setImmediate = setImmediate; + } else { + this._setImmediate = process.nextTick; + } +} + +Parser.prototype.parse = function (buffer, target, done) { + var task = new ParserTask(buffer, target, done); + task.id = ++this.taskId; + if (this._queue.push(task) === 1) { + executeTask(this); + } +}; + +Parser.prototype.parseParameters = function (buffer) { + return this._parseRow.call(new ParserState(buffer)); +}; + +function executeTask(parser) { + var task = parser._queue[0]; + if (task.active) return; + task.active = true; + var state = task.state; + var target = task.target; + var parseRow = parser._parseRow.bind(state); + var bufferLength = state.buffer.length; + + var read = function () { + for (var i = 0; i < parser._threshold && state.offset < bufferLength; i++) { + target.push(parseRow()); + } + return state.offset < bufferLength; + }; + + var done = function (err) { + task.done(err); + parser._queue.shift(); + if (err) return; + if (parser._queue.length) { + executeTask(parser); + } + }; + + var next = function () { + parser._setImmediate(function () { + var bytesRemaining; + try { + bytesRemaining = read(); + } catch (err) { + done(err); + return; + } + if (bytesRemaining > 0) { + next(); + } else { + done(null); + } + }); + }; + + next(); +} + +function ParserTask(buffer, target, callback) { + this.state = new ParserState(buffer); + this.active = false; + var self = this; + if (typeof target === 'function') { + this.target = []; + callback = target; + this.done = function (err) { + callback(err, self.target); + }; + } else { + this.target = target; + this.done = function (err) { + callback(err); + }; + } +} + +function ParserState(buffer) { + this.buffer = buffer; + this.offset = 0; +} + +ParserState.prototype.readTinyInt = function () { + if (this.buffer[this.offset++] === 0x00) { + return null; + } + /* + var uInt = this.buffer[this.offset++]; + if (!(uInt & 0x80)) + return uInt; + return ((0xff - uInt + 1) * -1); + */ + var value = this.buffer.readInt8(this.offset); + this.offset += 1; + return value; +}; + +ParserState.prototype.readSmallInt = function () { + if (this.buffer[this.offset++] === 0x00) { + return null; + } + var value = this.buffer.readInt16LE(this.offset); + this.offset += 2; + return value; +}; + +ParserState.prototype.readInt = function () { + if (this.buffer[this.offset++] === 0x00) { + return null; + } + var value = this.buffer.readInt32LE(this.offset); + this.offset += 4; + return value; +}; + +ParserState.prototype.readBigInt = function () { + if (this.buffer[this.offset++] === 0x00) { + return null; + } + var value = bignum.readInt64LE(this.buffer, this.offset); + this.offset += 8; + return value; +}; + +ParserState.prototype.readString = function () { + this.readBytes('utf-8'); +}; + +ParserState.prototype.readBinary = function () { + this.readBytes(); +}; + +ParserState.prototype.readBytes = function (encoding) { + var length = this.buffer[this.offset++]; + switch (length) { + case 0xff: + return null; + case 0xf6: + length = this.buffer.readInt16LE(this.offset); + this.offset += 2; + break; + case 0xf7: + length = this.buffer.readInt32LE(this.offset); + this.offset += 4; + break; + } + var value; + if (encoding) { + value = this.buffer.toString(encoding, this.offset, this.offset + length); + } else { + value = new Buffer(length); + this.buffer.copy(value, 0, this.offset, this.offset + length); + } + this.offset += length; + return value; +}; + +ParserState.prototype.readDate = function () { + if (!(this.buffer[this.offset + 1] & 0x80)) { + this.offset += 4; + return null; + } + var year = this.buffer.readInt16LE(this.offset); + if (year & 0x8000) year = year & 0x7fff; + if (year & 0x4000) year = year | 0x8000; + var month = this.buffer.readInt8(this.offset + 2) + 1; + var day = this.buffer.readInt8(this.offset + 3); + this.offset += 4; + return bignum.lpad4(year) + '-' + bignum.lpad2(month) + '-' + bignum.lpad2( + day); +}; + +ParserState.prototype.readTime = function () { + if (!(this.buffer[this.offset] & 0x80)) { + this.offset += 4; + return null; + } + var hour = this.buffer.readInt8(this.offset); + if (hour & 0x80) hour = hour & 0x7f; + var min = this.buffer.readInt8(this.offset + 1); + var msec = this.buffer.readUInt16LE(this.offset + 2); + this.offset += 4; + return bignum.lpad2(hour) + ':' + bignum.lpad2(min) + ':' + bignum.lpad2(msec / + 1000); +}; + +ParserState.prototype.readTimestamp = function () { + var date = this.readDate(); + var time = this.readTime(); + if (!date && !time) { + return null; + } else if (date && time) { + return date + 'T' + time; + } else if (date) { + return date; + } else { + return time; + } +}; + +ParserState.prototype.readDayDate = function () { + var value = this.buffer.readInt32LE(this.offset); + this.offset += 4; + if (value === 3652062 || value === 0) { + return null; + } + return value - 1; +}; + +ParserState.prototype.readSecondTime = function () { + var value = this.buffer.readInt32LE(this.offset); + this.offset += 4; + if (value === 86402 || value === 0) { + return null; + } + return value - 1; +}; + +ParserState.prototype.readSecondDate = function () { + var value = bignum.readInt64LE(this.buffer, this.offset); + this.offset += 8; + if (value === 315538070401) { + return null; + } + return value - 1; +}; + +ParserState.prototype.readLongDate = function () { + var value = bignum.readInt64LE(this.buffer, this.offset); + this.offset += 8; + if (value === '3155380704000000001') { + return null; + } + if (typeof value === 'string') { + var index = value.length - 7; + return value.substring(0, index) + lpad7(value.substring(index) - 1); + } else { + return value - 1; + } +}; + +ParserState.prototype.readLob = function readLob(type) { + // skip source type + this.offset += 1; + // offset 1 + var options = this.buffer[this.offset]; + this.offset += 1; + // offset 2 + if ( !! (options & 0x01)) { + return null; + } + // skip 2 byte filler + this.offset += 2; + // offset 4 + var charLength = bignum.readInt64LE(this.buffer, this.offset); + this.offset += 8; + // offset 12 + var byteLength = bignum.readInt64LE(this.buffer, this.offset); + this.offset += 8; + // offset 20 + var locatorId = this.buffer.slice(this.offset, this.offset + 8); + this.offset += 8; + // offset 28 + var chunkLength = this.buffer.readInt32LE(this.offset); + this.offset += 4; + // offset 32 + var value = this.buffer.slice(this.offset, this.offset + chunkLength); + this.offset += chunkLength; + // is last + var isLast = !! (options & 0x04); + if (isLast) { + return value; + } + return { + type: type, + locatorId: locatorId, + charLength: charLength, + byteLength: byteLength, + isLast: isLast, + chunk: value + }; +}; + +ParserState.prototype.readNCLob = function () { + if (this.buffer[this.offset + 1] === 0x01) { + this.offset += 2; + return null; + } + var length = this.buffer.readInt32LE(this.offset + 28); + this.offset += 32; + var value = this.buffer.toString('utf-8', this.offset, this.offset + length); + this.offset += length; + return value; +}; + +ParserState.prototype.readDouble = function () { + if (this.buffer[this.offset] === 0xff && + this.buffer[this.offset + 1] === 0xff && + this.buffer[this.offset + 2] === 0xff && + this.buffer[this.offset + 3] === 0xff && + this.buffer[this.offset + 4] === 0xff && + this.buffer[this.offset + 5] === 0xff && + this.buffer[this.offset + 6] === 0xff && + this.buffer[this.offset + 7] === 0xff) { + this.offset += 8; + return null; + } + var value = this.buffer.readDoubleLE(this.offset); + this.offset += 8; + return value; +}; + +ParserState.prototype.readFloat = function () { + if (this.buffer[this.offset] === 0xff && this.buffer[this.offset + 1] === + 0xff && this.buffer[this.offset + 2] === 0xff && this.buffer[this.offset + + 3] === 0xff) { + this.offset += 4; + return null; + } + var value = this.buffer.readFloatLE(this.offset); + this.offset += 4; + return value; +}; + +ParserState.prototype.readDecimal = function (fraction) { + var value; + if (fraction > 34) { + value = bignum.readDecFloat(this.buffer, this.offset); + } else { + value = bignum.readDecFixed(this.buffer, this.offset, fraction); + } + this.offset += 16; + return value; +}; \ No newline at end of file diff --git a/lib/protocol/Result.js b/lib/protocol/Result.js new file mode 100644 index 0000000..34f2f71 --- /dev/null +++ b/lib/protocol/Result.js @@ -0,0 +1,149 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = require('../util'); +var ResultSet = require('./ResultSet'); +var Parser = require('./Parser'); +var common = require('./common'); +var FunctionCode = common.FunctionCode; + +module.exports = Result; + +function Result(connection, options) { + options = util.extend({ + autoFetch: true + }, options); + this._connection = connection; + this._autoFetch = options.autoFetch; + this._resultSetMetadata = undefined; + this._parameterMetadata = undefined; +} + +Result.prototype.setResultSetMetadata = function setResultSetMetadata(metadata) { + this._resultSetMetadata = metadata; +}; + +Result.prototype.setParameterMetadata = function setParameterMetadata(metadata) { + this._parameterMetadata = metadata; +}; + +Result.prototype.handle = function handle(err, reply, cb) { + + if (err) { + return cb(err); + } + + var args = [null]; + var resultSets = reply.resultSets || []; + var outputParameters = {}; + + // handle missing resultSet metadata + if (this._resultSetMetadata && resultSets.length) { + if (!resultSets[0].metadata) { + resultSets[0].metadata = this._resultSetMetadata; + } + } + // output parameter + if (this._parameterMetadata && util.isObject(reply.outputParameters)) { + outputParameters = getOutputParameters(this._parameterMetadata, + reply.outputParameters.buffer); + } + + function onresults(err, results) { + if (err) { + return cb(err); + } + Array.prototype.push.apply(args, results); + cb.apply(null, args); + } + + switch (reply.functionCode) { + case FunctionCode.SELECT: + case FunctionCode.SELECT_FOR_UPDATE: + this.createResultSets(resultSets, onresults); + return; + case FunctionCode.INSERT: + case FunctionCode.UPDATE: + case FunctionCode.DELETE: + cb(null, reply.rowsAffected); + return; + case FunctionCode.DDL: + cb(null); + return; + case FunctionCode.DB_PROCEDURE_CALL: + case FunctionCode.DB_PROCEDURE_CALL_WITH_RESULT: + args.push(outputParameters); + this.createResultSets(resultSets, onresults); + return; + default: + err = new Error('Invalid or unsupported FunctionCode'); + cb(err); + return; + } +}; + +Result.prototype.createResultSets = function createResultSets(resultSets, cb) { + if (!resultSets.length) { + return cb(null, []); + } + + var resultSetObjects = []; + for (var i = 0; i < resultSets.length; i++) { + resultSetObjects.push(new ResultSet(this._connection, resultSets[i])); + } + if (!this._autoFetch) { + return cb(null, resultSetObjects); + } + fetchAll(resultSetObjects, cb); +}; + +function getOutputParameters(metadata, buffer) { + var parser = new Parser(metadata, { + nameProperty: 'name', + }); + return parser.parseParameters(buffer); +} + +function fetchAll(resultSets, cb) { + var results = []; + + function done(err) { + resultSets.filter(function isOpen(rs) { + return !rs.closed; + }).forEach(function close(rs) { + rs.close(function onclose(err) { + console.log('close', err) + }); + }); + if (err) { + return cb(err); + } + cb(null, results); + } + + function next(i) { + if (i === resultSets.length) { + return done(null); + } + resultSets[i].fetch(function onfetch(err, rows) { + if (err) { + return done(err); + } + results.push(rows); + process.nextTick(next.bind(null, i + 1)); + }); + } + next(0); +} \ No newline at end of file diff --git a/lib/protocol/ResultSet.js b/lib/protocol/ResultSet.js new file mode 100644 index 0000000..28b8da3 --- /dev/null +++ b/lib/protocol/ResultSet.js @@ -0,0 +1,343 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = require('../util'); +var EventEmitter = require('events').EventEmitter; +var Readable = require('stream').Readable; +var common = require('./common'); +var Parser = require('./Parser'); +var Lob = require('./Lob'); +var TypeCode = common.TypeCode; + +module.exports = ResultSet; + +util.inherits(ResultSet, EventEmitter); + +ResultSet.MAX_FETCH_SIZE = Math.pow(2, 15) - 1; +ResultSet.DEFAULT_FETCH_SIZE = Math.pow(2, 10); + +function ResultSet(connection, resultSet) { + EventEmitter.call(this); + + this._connection = connection; + this._running = undefined; + this._data = resultSet.data; + this._close = false; + this._fetchSize = ResultSet.DEFAULT_FETCH_SIZE; + this.id = resultSet.id; + this.metadata = resultSet.metadata; + this.lobs = this.metadata.filter(isLob); + this.ended = false; + this.closed = false; +} + +Object.defineProperties(ResultSet.prototype, { + fetchSize: { + get: function getFetchSize() { + return this._fetchSize; + } + } +}); + +ResultSet.prototype.setFetchSize = function setFetchSize(fetchSize) { + if (fetchSize > ResultSet.MAX_FETCH_SIZE) { + this._fetchSize = ResultSet.MAX_FETCH_SIZE; + } + this._fetchSize = fetchSize; + return this; +}; + +ResultSet.prototype.readLob = function readLob(lob, cb) { + new Lob(this._connection, lob).read(cb); +}; + + +ResultSet.prototype.createReadLobStream = function createReadLobStream(lob, + options, cb) { + if (util.isFunction(options)) { + cb = options; + options = {}; + } + return new Lob(this._connection, lob).createReadStream(options); +}; + +ResultSet.prototype.fetch = function fetch(cb) { + var self = this; + + function done(err, rows) { + /* jshint validthis:true */ + + if (util.isFunction(cb)) { + cb(err, rows, self.closed); + } + } + + if (typeof this._running !== 'undefined') { + var err = new Error('ResultSet invalid state error'); + return done(err); + } + fetchRows(this, done); + return this; +}; + +ResultSet.prototype.createReadStream = function createReadStream(options) { + if (typeof this._running !== 'undefined') { + return null; + } + return createResultSetReadStream(this, options); +}; + +ResultSet.prototype.stopFetch = function stopFetch() { + this._running = false; + return this; +}; + +ResultSet.prototype.startFetch = function startFetch() { + if (!this._running && !this.ended) { + this._running = true; + if (util.isObject(this._data)) { + handleData.call(this, this._data); + this._data = undefined; + } else { + sendFetch.call(this); + } + } + return this; +}; + +ResultSet.prototype.close = function close(cb) { + if (util.isFunction(cb)) { + this._close = cb; + } else { + this._close = true; + } + return this; +}; + +ResultSet.prototype.createParser = function createParser(treshhold) { + return new Parser(this.metadata, { + treshhold: treshhold || 0 + }); +}; + +function createResultSetReadStream(resultSet, options) { + + var parser = resultSet.createParser(); + + options = options || {}; + options.objectMode = true; + var readable = new Readable(options); + readable._read = function _read() { + resultSet.startFetch(); + }; + + var finished = false; + var pending = 0; + + function cleanup() { + if (finished) { + return; + } + finished = true; + resultSet.removeListener('error', onerror); + resultSet.removeListener('data', ondata); + resultSet.removeListener('end', onend); + } + + function onerror(err) { + cleanup(); + readable.emit('error', err); + } + resultSet.once('error', onerror); + + function ondata(count, chunk) { + var rows = []; + pending += 1; + parser.parse(chunk, rows, function parsed(err) { + pending -= 1; + if (err) { + cleanup(); + readable.emit('error', err); + return; + } + if (!readable.push(rows)) { + resultSet.stopFetch(); + } + if (pending === 0 && finished) { + readable.push(null); + } + }); + } + resultSet.on('data', ondata); + + function onend(closed) { + cleanup(); + if (!closed && resultSet._close) { + sendClose.call(resultSet); + } + if (pending === 0) { + readable.push(null); + } + } + resultSet.once('end', onend); + + return readable; +} + +function fetchRows(resultSet, cb) { + + var parser = resultSet.createParser(); + var rows = []; + var finished = false; + var pending = 0; + + function done(err) { + if (util.isFunction(cb)) { + cb(err, rows); + } + } + + function cleanup() { + if (finished) { + return; + } + finished = true; + resultSet.removeListener('error', onerror); + resultSet.removeListener('data', ondata); + resultSet.removeListener('end', onend); + } + + function onerror(err) { + cleanup(); + done(err); + } + resultSet.on('error', onerror); + + function ondata(count, chunk) { + pending += 1; + parser.parse(chunk, rows, function parsed(err) { + pending -= 1; + if (err) { + cleanup(); + done(err); + return; + } + if (pending === 0 && finished) { + done(null); + } + }); + } + resultSet.on('data', ondata); + + function onend() { + cleanup(); + if (pending === 0) { + done(null); + } + } + resultSet.on('end', onend); + resultSet.startFetch(); +} + +function sendClose() { + /* jshint validthis:true */ + + function done(err) { + this._connection = undefined; + if (!err) { + this.closed = true; + this.emit('close'); + } + if (util.isFunction(this._close)) { + this._close(err); + } + } + this._connection.closeResultSet({ + resultSetId: this.id + }, done.bind(this)); +} + +function sendFetch() { + /* jshint validthis:true */ + + this._connection.fetchNext({ + resultSetId: this.id, + fetchSize: this.fetchSize + }, receiveData.bind(this)); +} + +function receiveData(err, reply) { + /* jshint validthis:true */ + + if (err) { + this._running = false; + this.emit('error', err); + return; + } + + var data = reply.resultSets[0].data; + if (this._running) { + handleData.call(this, data); + } else { + this._data = data; + } +} + +function handleData(data) { + /* jshint validthis:true */ + + if (data.argumentCount && Buffer.isBuffer(data.buffer)) { + this.emit('data', data.argumentCount, data.buffer); + } + + if (isLast(data)) { + this.ended = true; + this._running = false; + this.closed = isClosed(data); + this.emit('end', this.closed); + if (!this.closed && this._close) { + sendClose.call(this); + } + return; + } + + if (this._running) { + process.nextTick(sendFetch.bind(this)); + } +} + +function isLast(data) { + /* jshint bitwise:false */ + return !!(data.attributes & common.ResultSetAttributes.LAST); +} + +function isClosed(data) { + /* jshint bitwise:false */ + return !!(data.attributes & common.ResultSetAttributes.CLOSED); +} + +function isLob(column) { + switch (column.dataType) { + case TypeCode.BLOB: + case TypeCode.LOCATOR: + case TypeCode.CLOB: + case TypeCode.NCLOB: + case TypeCode.NLOCATOR: + case TypeCode.TEXT: + return true; + default: + return false; + } +} \ No newline at end of file diff --git a/lib/protocol/Statement.js b/lib/protocol/Statement.js new file mode 100644 index 0000000..ef4b416 --- /dev/null +++ b/lib/protocol/Statement.js @@ -0,0 +1,123 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = require('../util'); +var Result = require('./Result'); +var common = require('./common'); +var IoType = common.IoType; + +module.exports = Statement; + +function Statement(connection) { + this._connection = connection; + this.id = undefined; + this.parameterMetadata = undefined; + this.resultSetMetadata = undefined; + this.droped = false; +} + +Statement.prototype.exec = function exec(values, options, cb) { + var defaults = { + autoFetch: true + }; + if (util.isFunction(options)) { + cb = options; + options = defaults; + } else if (util.isObject(options)) { + options = util.extend(defaults, options); + } else { + var autoFetch = !! options; + options = defaults; + options.autoFetch = autoFetch; + } + + var result = new Result(this._connection, options); + result.setResultSetMetadata(this.resultSetMetadata); + result.setParameterMetadata(this.parameterMetadata.filter(isOutputParameter)); + this._connection.execute({ + statementId: this.id, + parameters: getInputParameters(this.parameterMetadata, values) + }, function onreply(err, reply) { + result.handle(err, reply, cb); + }); + return this; +}; + +Statement.prototype.drop = function drop(cb) { + + function done(err) { + /* jshint validthis:true */ + this._connection = undefined; + if (!err) { + this.droped = true; + } + if (util.isFunction(cb)) { + cb(err); + } + } + this._connection.dropStatement({ + statementId: this.id + }, done.bind(this)); +}; + +Statement.prototype.getParameterName = function getParameterName(i) { + if (util.isArray(this.parameterMetadata) && i < this.parameterMetadata.length) { + return this.parameterMetadata[i].name; + } +}; + +Statement.prototype.handle = function handle(err, reply, cb) { + if (err) { + this._connection = undefined; + return cb(err); + } + + this.id = reply.statementId; + if (util.isArray(reply.resultSets) && reply.resultSets.length) { + this.resultSetMetadata = reply.resultSets[0].metadata; + } + this.parameterMetadata = reply.parameterMetadata; + cb(null, this); +}; + +function getInputParameters(parameterMetadata, values) { + var inputParameterMetadata = parameterMetadata.filter(isInputParameter); + if (!inputParameterMetadata.length) { + return null; + } + if (util.isArray(values)) { + return inputParameterMetadata.map(function (metadata, index) { + return { + type: metadata.dataType, + value: typeof values[index] === 'undefined' ? null : values[index] + }; + }); + } + return inputParameterMetadata.map(function (metadata) { + return { + type: metadata.dataType, + value: typeof values[metadata.name] === 'undefined' ? null : values[ + metadata.name] + }; + }); +} + +function isInputParameter(metadata) { + return metadata.ioType === IoType.INPUT || metadata.ioType === IoType.IN_OUT; +} + +function isOutputParameter(metadata) { + return metadata.ioType === IoType.OUTPUT || metadata.ioType === IoType.IN_OUT; +} \ No newline at end of file diff --git a/lib/protocol/Stringifier.js b/lib/protocol/Stringifier.js new file mode 100644 index 0000000..468c992 --- /dev/null +++ b/lib/protocol/Stringifier.js @@ -0,0 +1,62 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = require('util'); +var Transform = require('stream').Transform; + +module.exports = Stringifier; + +util.inherits(Stringifier, Transform); + +function Stringifier(options) { + options = options || {}; + + Transform.call(this, options); + + this._writableState.objectMode = true; + + this._map = options.map; + this._header = options.header !== undefined ? options.header : '['; + this._footer = options.footer !== undefined ? options.footer : ']'; + this._seperator = options.seperator !== undefined ? options.seperator : ','; + this._stringify = options.stringify || JSON.stringify; + this._first = true; +} + +Stringifier.prototype._transform = function (chunk, encoding, done) { + var str = ''; + if (chunk && chunk.length) { + if (typeof this._map === 'function') { + chunk = chunk.map(this.map); + } + str = this._first ? this._header : this._seperator; + this._first = false; + for (var i = 0; i < chunk.length; i++) { + if (i > 0) + str += this._seperator; + str += this._stringify(chunk[i]); + } + } + done(null, str); +}; + +Stringifier.prototype._flush = function (done) { + if (this._first) { + this.push(this._header + this._footer); + } else { + this.push(this._footer); + } + done(null); +}; \ No newline at end of file diff --git a/lib/protocol/auth/index.js b/lib/protocol/auth/index.js new file mode 100644 index 0000000..ac22a38 --- /dev/null +++ b/lib/protocol/auth/index.js @@ -0,0 +1,16 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +exports.SCRAMSHA256 = require('./scramsha256'); \ No newline at end of file diff --git a/lib/protocol/auth/scramsha256/Algorithm.js b/lib/protocol/auth/scramsha256/Algorithm.js new file mode 100644 index 0000000..7e84f81 --- /dev/null +++ b/lib/protocol/auth/scramsha256/Algorithm.js @@ -0,0 +1,78 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var crypto = require('crypto'); + +exports = module.exports = Algorithm; + +var CLIENT_PROOF_SIZE = 32; + +function Algorithm(clientChallenge) { + this.clientChallenge = clientChallenge || crypto.randomBytes(64); + this.serverChallenge = undefined; + this.salts = undefined; +} + +Algorithm.prototype.update = function update(args) { + this.salts = args; + this.serverChallenge = this.salts.pop(); +}; + +Algorithm.prototype.getClientProof = function getClientProof(password) { + if (typeof password === 'string') { + password = new Buffer(password, 'utf-8'); + } + var buf = new Buffer(2 + (CLIENT_PROOF_SIZE + 1) * this.salts.length); + buf[0] = 0x00; + buf.writeInt8(this.salts.length, 1); + var offset = 2; + this.salts.forEach(function scrambleSalt(salt) { + buf.writeInt8(CLIENT_PROOF_SIZE, offset); + offset += 1; + scramble(password, salt, this.clientChallenge, this.serverChallenge) + .copy(buf, offset); + offset += CLIENT_PROOF_SIZE; + }, this); + return buf; +}; + +function scramble(password, salt, clientkey, serverkey) { + var length = salt.length + serverkey.length + clientkey.length; + var msg = Buffer.concat([salt, serverkey, clientkey], length); + var key = sha256(hmac(password, salt)); + var sig = hmac(sha256(key), msg); + return xor(sig, key); +} + +function xor(a, b) { + /* jshint bitwise:false */ + var result = new Buffer(a.length); + for (var i = 0; i < a.length; i++) { + result[i] = a[i] ^ b[i]; + } + return result; +} + +function hmac(key, msg) { + var hash = crypto.createHmac('sha256', key); + hash.update(msg); + return new Buffer(hash.digest(), 'binary'); +} + +function sha256(msg) { + var hash = crypto.createHash('sha256'); + hash.update(msg); + return new Buffer(hash.digest(), 'binary'); +} \ No newline at end of file diff --git a/lib/protocol/auth/scramsha256/Authentication.js b/lib/protocol/auth/scramsha256/Authentication.js new file mode 100644 index 0000000..15bada6 --- /dev/null +++ b/lib/protocol/auth/scramsha256/Authentication.js @@ -0,0 +1,45 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var Fields = require('../../data/Fields'); + +exports.convert = convert; +exports.read = read; +exports.write = write; +exports.getByteLength = Fields.getByteLength; +exports.getArgumentCount = Fields.getArgumentCount; + +function read(part) { + return convert(Fields.read(part)); +} + +function convert(fields) { + var serverChallengeData = Fields.read({ + argumentCount: 1, + buffer: fields[1] + }); + return { + algorithm: fields[0].toString('ascii'), + salt: serverChallengeData[0], + serverChallenge: serverChallengeData[1] + }; +} + +function write(part, options) { + /* jshint validthis:true */ + + options = options || this; + return Fields.write(part, [options.user, options.algorithm, options.clientChallenge]); +} \ No newline at end of file diff --git a/lib/protocol/auth/scramsha256/Connect.js b/lib/protocol/auth/scramsha256/Connect.js new file mode 100644 index 0000000..01fcb64 --- /dev/null +++ b/lib/protocol/auth/scramsha256/Connect.js @@ -0,0 +1,40 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var Fields = require('../../data/Fields'); + +exports.convert = convert; +exports.read = read; +exports.write = write; +exports.getByteLength = Fields.getByteLength; +exports.getArgumentCount = Fields.getArgumentCount; + +function read(part) { + return convert(Fields.read(part)); +} + +function convert(fields) { + return { + algorithm: fields[0].toString('ascii'), + serverProof: fields[1] + }; +} + +function write(part, options) { + /* jshint validthis:true */ + + options = options || this; + return Fields.write(part, [options.user, options.algorithm, options.clientProof]); +} \ No newline at end of file diff --git a/lib/protocol/auth/scramsha256/index.js b/lib/protocol/auth/scramsha256/index.js new file mode 100644 index 0000000..324046a --- /dev/null +++ b/lib/protocol/auth/scramsha256/index.js @@ -0,0 +1,18 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +exports.Algorithm = require('./Algorithm'); +exports.Authentication = require('./Authentication'); +exports.Connect = require('./Connect'); \ No newline at end of file diff --git a/lib/protocol/common/ClientDistributionMode.js b/lib/protocol/common/ClientDistributionMode.js new file mode 100644 index 0000000..46e132b --- /dev/null +++ b/lib/protocol/common/ClientDistributionMode.js @@ -0,0 +1,21 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +module.exports = { + OFF: 0, + CONNECTION: 1, + STATEMENT_ONLY: 2, + STATEMENT_CONNECTION: 3 +}; \ No newline at end of file diff --git a/lib/protocol/common/CommandOption.js b/lib/protocol/common/CommandOption.js new file mode 100644 index 0000000..aa4a8ef --- /dev/null +++ b/lib/protocol/common/CommandOption.js @@ -0,0 +1,22 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +module.exports = { + SELFETCH_OFF: 1, + SCROLLABLE_CURSOR_ON: 2, + NO_RESULT_SET_CLOSE_NEEDED: 4, + HOLD_CURSORS_OVER_COMMIT: 8, + EXECUTE_LOCALLY: 16 +}; \ No newline at end of file diff --git a/lib/protocol/common/ConnectOption.js b/lib/protocol/common/ConnectOption.js new file mode 100644 index 0000000..388e0f8 --- /dev/null +++ b/lib/protocol/common/ConnectOption.js @@ -0,0 +1,40 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +module.exports = { + CONNECTION_ID: 1, + COMPLETE_ARRAY_EXECUTION: 2, + CLIENT_LOCALE: 3, + SUPPORTS_LARGE_BULK_OPERATIONS: 4, + DISTRIBUTION_ENABLED: 5, + PRIMARY_CONNECTION_ID: 6, + PRIMARY_CONNECTION_HOST: 7, + PRIMARY_CONNECTION_PORT: 8, + COMPLETE_DATATYPE_SUPPORT: 9, + LARGE_NUMBER_OF_PARAMETERS_SUPPORT: 10, + SYSTEM_ID: 11, + DATA_FORMAT_VERSION: 12, + ABAP_VARCHAR_MODE: 13, + SELECT_FOR_UPDATE_SUPPORTED: 14, + DISTRIBUTION_MODE: 15, + ENGINE_DATA_FORMAT_VERSION: 16, + DISTRIBUTION_PROTOCOL_VERSION: 17, + SPLIT_BATCH_COMMANDS: 18, + USE_TRANSACTION_FLAGS_ONLY: 19, + ROW_AND_COLUMN_OPTIMIZED_FORMAT: 20, + IGNORE_UNKNOWN_PARTS: 21, + TABLE_OUTPUT_PARAMETER_METADATA_SUPPORT: 22, + DATA_FORMAT_VERSION2: 23 +}; \ No newline at end of file diff --git a/lib/protocol/common/Constants.js b/lib/protocol/common/Constants.js new file mode 100644 index 0000000..31935a2 --- /dev/null +++ b/lib/protocol/common/Constants.js @@ -0,0 +1,66 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var ConnectOption = require('./ConnectOption'); +var TypeCode = require('./TypeCode'); +var ClientDistributionMode = require('./ClientDistributionMode'); +var DataFormatVersion = require('./DataFormatVersion'); +var DistributionProtocolVersion = require('./DistributionProtocolVersion'); + +module.exports = { + IEEE_754_BINARY_64_PRECISION: Math.pow(2, 53), + PACKET_HEADER_LENGTH: 32, + SEGMENT_HEADER_LENGTH: 24, + PART_HEADER_LENGTH: 16, + MAX_PACKET_SIZE: Math.pow(2, 16), + MAX_RESULT_SET_SIZE: Math.pow(2, 20), + DEFAULT_CONNECT_OPTIONS: [{ + name: ConnectOption.CLIENT_LOCALE, + value: 'en_US', + type: TypeCode.STRING + }, { + name: ConnectOption.COMPLETE_ARRAY_EXECUTION, + value: true, + type: TypeCode.BOOLEAN + }, { + name: ConnectOption.DATA_FORMAT_VERSION2, + value: DataFormatVersion.COMPLETE_DATATYPE_SUPPORT, + type: TypeCode.INT + }, { + name: ConnectOption.DATA_FORMAT_VERSION, + value: DataFormatVersion.COMPLETE_DATATYPE_SUPPORT, + type: TypeCode.INT + }, { + name: ConnectOption.DISTRIBUTION_ENABLED, + value: true, + type: TypeCode.BOOLEAN + }, { + name: ConnectOption.DISTRIBUTION_MODE, + value: ClientDistributionMode.STATEMENT_CONNECTION, + type: TypeCode.INT + }, { + name: ConnectOption.SELECT_FOR_UPDATE_SUPPORTED, + value: true, + type: TypeCode.BOOLEAN + }, { + name: ConnectOption.DISTRIBUTION_PROTOCOL_VERSION, + value: DistributionProtocolVersion.STATEMENT_SEQUENCE_NUMBER_SUPPORTED, + type: TypeCode.INT + }, { + name: ConnectOption.ROW_AND_COLUMN_OPTIMIZED_FORMAT, + value: true, + type: TypeCode.BOOLEAN + }] +}; \ No newline at end of file diff --git a/lib/protocol/common/DataFormatVersion.js b/lib/protocol/common/DataFormatVersion.js new file mode 100644 index 0000000..fe5fee9 --- /dev/null +++ b/lib/protocol/common/DataFormatVersion.js @@ -0,0 +1,21 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +module.exports = { + BASE_FORMAT: 0, + COMPLETE_DATATYPE_SUPPORT: 1, + EXTENDED_DATE_TIME_SUPPORT: 3, // deprecated + LEVEL4: 4 +}; \ No newline at end of file diff --git a/lib/protocol/common/DistributionProtocolVersion.js b/lib/protocol/common/DistributionProtocolVersion.js new file mode 100644 index 0000000..fe27925 --- /dev/null +++ b/lib/protocol/common/DistributionProtocolVersion.js @@ -0,0 +1,19 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +module.exports = { + BASE: 0, + STATEMENT_SEQUENCE_NUMBER_SUPPORTED: 1 +}; \ No newline at end of file diff --git a/lib/protocol/common/ErrorLevel.js b/lib/protocol/common/ErrorLevel.js new file mode 100644 index 0000000..504fea8 --- /dev/null +++ b/lib/protocol/common/ErrorLevel.js @@ -0,0 +1,20 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +module.exports = { + WARNING: 0, + ERROR: 1, + FATAL: 2 +}; \ No newline at end of file diff --git a/lib/protocol/common/FunctionCode.js b/lib/protocol/common/FunctionCode.js new file mode 100644 index 0000000..abf9700 --- /dev/null +++ b/lib/protocol/common/FunctionCode.js @@ -0,0 +1,41 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +module.exports = { + NIL: 0, + DDL: 1, + INSERT: 2, + UPDATE: 3, + DELETE: 4, + SELECT: 5, + SELECT_FOR_UPDATE: 6, + EXPLAIN: 7, + DB_PROCEDURE_CALL: 8, + DB_PROCEDURE_CALL_WITH_RESULT: 9, + FETCH: 10, + COMMIT: 11, + ROLLBACK: 12, + SAVEPOINT: 13, + CONNECT: 14, + WRITE_LOB: 15, + READ_LOB: 16, + PING: 17, + DISCONNECT: 18, + CLOSE_CURSOR: 19, + FIND_LOB: 20, + ABAP_STREAM: 21, + XA_START: 22, + XA_JOIN: 23 +}; \ No newline at end of file diff --git a/lib/protocol/common/IoType.js b/lib/protocol/common/IoType.js new file mode 100644 index 0000000..3acdf6d --- /dev/null +++ b/lib/protocol/common/IoType.js @@ -0,0 +1,20 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +module.exports = { + INPUT: 1, + IN_OUT: 2, + OUTPUT: 4 +}; \ No newline at end of file diff --git a/lib/protocol/common/LobOptions.js b/lib/protocol/common/LobOptions.js new file mode 100644 index 0000000..79b29e1 --- /dev/null +++ b/lib/protocol/common/LobOptions.js @@ -0,0 +1,20 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +module.exports = { + NULL_INDICATOR: 1, + DATA_INCLUDED: 2, + LAST_DATA: 4 +}; \ No newline at end of file diff --git a/lib/protocol/common/LobSourceType.js b/lib/protocol/common/LobSourceType.js new file mode 100644 index 0000000..b7c7b2d --- /dev/null +++ b/lib/protocol/common/LobSourceType.js @@ -0,0 +1,21 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +module.exports = { + UNKNOWN: 0, + BLOB: 1, + CLOB: 2, + NCLOB: 3 +}; \ No newline at end of file diff --git a/lib/protocol/common/MessageType.js b/lib/protocol/common/MessageType.js new file mode 100644 index 0000000..c3b1ea0 --- /dev/null +++ b/lib/protocol/common/MessageType.js @@ -0,0 +1,40 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +module.exports = { + NIL: 0, + EXECUTE_DIRECT: 2, + PREPARE: 3, + ABAP_STREAM: 4, + XA_START: 5, + XA_JOIN: 6, + EXECUTE: 13, + READ_LOB: 16, + WRITE_LOB: 17, + FIND_LOB: 18, + PING: 25, + AUTHENTICATE: 65, + CONNECT: 66, + COMMIT: 67, + ROLLBACK: 68, + CLOSE_RESULT_SET: 69, + DROP_STATEMENT_ID: 70, + FETCH_NEXT: 71, + DISCONNECT: 77, + EXECUTE_ITAB: 78, + FETCH_NEXT_ITAB: 79, + INSERT_NEXT_ITAB: 80, + BATCH_PREPARE: 81 +}; \ No newline at end of file diff --git a/lib/protocol/common/ParameterMode.js b/lib/protocol/common/ParameterMode.js new file mode 100644 index 0000000..2b29f0f --- /dev/null +++ b/lib/protocol/common/ParameterMode.js @@ -0,0 +1,23 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +module.exports = { + MANDATORY: 1, + OPTIONAL: 2, + DEFAULT: 4, + ESCAPE_CHAR: 8, + READONLY: 16, + AUTO_INCREMENT: 32 +}; \ No newline at end of file diff --git a/lib/protocol/common/PartKind.js b/lib/protocol/common/PartKind.js new file mode 100644 index 0000000..cd24b02 --- /dev/null +++ b/lib/protocol/common/PartKind.js @@ -0,0 +1,68 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +module.exports = { + NIL: 0, + COMMAND: 3, + RESULT_SET: 5, + ERROR: 6, + STATEMENT_ID: 10, + TRANSACTION_ID: 11, + ROWS_AFFECTED: 12, + RESULT_SET_ID: 13, + TOPOLOGY_INFORMATION: 15, + TABLE_LOCATION: 16, + READ_LOB_REQUEST: 17, + READ_LOB_REPLY: 18, + TABLE_NAME: 19, + /* + ABAP_ISTREAM: 25, + ABAP_OSTREAM: 26, + */ + COMMAND_INFO: 27, + WRITE_LOB_REQUEST: 28, + WRITE_LOB_REPLY: 30, + PARAMETERS: 32, + AUTHENTICATION: 33, + SESSION_CONTEXT: 34, + CLIENT_ID: 35, + STATEMENT_CONTEXT: 39, + PARTITION_INFORMATION: 40, + OUTPUT_PARAMETERS: 41, + CONNECT_OPTIONS: 42, + COMMIT_OPTIONS: 43, + FETCH_OPTIONS: 44, + FETCH_SIZE: 45, + PARAMETER_METADATA: 47, + RESULT_SET_METADATA: 48, + FIND_LOB_REQUEST: 49, + FIND_LOB_REPLY: 50, + /* + ITAB_SHM: 51, + ITAB_CHUNK_METADATA: 53, + ITAB_METADATA: 55, + ITAB_RESULT_CHUNK: 56, + */ + CLIENT_INFO: 57, + /* + STREAM_DATA: 58, + OSTREAM_RESULT: 59, + FDA_REQUEST_METADATA: 60, + FDA_REPLY_METADATA: 61, + BATCH_PREPARE: 62, + BATCH_EXECUTE: 63, + */ + TRANSACTION_FLAGS: 64 +}; \ No newline at end of file diff --git a/lib/protocol/common/ResultSetAttributes.js b/lib/protocol/common/ResultSetAttributes.js new file mode 100644 index 0000000..d5acae2 --- /dev/null +++ b/lib/protocol/common/ResultSetAttributes.js @@ -0,0 +1,22 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +module.exports = { + LAST: 1, + NEXT: 2, + FIRST: 4, + ROW_NOT_FOUND: 8, + CLOSED: 16 +}; \ No newline at end of file diff --git a/lib/protocol/common/SegmentKind.js b/lib/protocol/common/SegmentKind.js new file mode 100644 index 0000000..d743743 --- /dev/null +++ b/lib/protocol/common/SegmentKind.js @@ -0,0 +1,21 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +module.exports = { + INVALID: 0, + REQUEST: 1, + REPLY: 2, + ERROR: 5 +}; \ No newline at end of file diff --git a/lib/protocol/common/SessionContext.js b/lib/protocol/common/SessionContext.js new file mode 100644 index 0000000..56edb3c --- /dev/null +++ b/lib/protocol/common/SessionContext.js @@ -0,0 +1,23 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +module.exports = { + PRIMARY_CONNECTION_ID: 1, + PRIMARY_HOST: 2, + PRIMARY_PORT: 3, + MASTER_CONNECTION_ID: 4, + MASTER_HOST: 5, + MASTER_PORT: 6 +}; \ No newline at end of file diff --git a/lib/protocol/common/StatementContext.js b/lib/protocol/common/StatementContext.js new file mode 100644 index 0000000..1903968 --- /dev/null +++ b/lib/protocol/common/StatementContext.js @@ -0,0 +1,19 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +module.exports = { + STATEMENT_SEQUENCE_INFO: 1, + SERVER_EXECUTION_TIME: 2 +}; \ No newline at end of file diff --git a/lib/protocol/common/TopologyInformation.js b/lib/protocol/common/TopologyInformation.js new file mode 100644 index 0000000..54ef96c --- /dev/null +++ b/lib/protocol/common/TopologyInformation.js @@ -0,0 +1,29 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +module.exports = { + HOST_NAME: 1, + HOST_PORT_NUMBER: 2, + TENAT_NAME: 3, + LOAD_FACTOR: 4, + VOLUME_ID: 5, + IS_MASTER: 6, + IS_CURRENT_SESSION: 7, + SERVICE_TYPE: 8, + NETWORK_DOMAIN: 9, + IS_STANDBY: 10, + ALL_IP_ADRESSES: 11, + ALL_HOST_NAMES: 12 +}; \ No newline at end of file diff --git a/lib/protocol/common/TypeCode.js b/lib/protocol/common/TypeCode.js new file mode 100644 index 0000000..82aea78 --- /dev/null +++ b/lib/protocol/common/TypeCode.js @@ -0,0 +1,87 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +module.exports = { + NULL: 0, + TINYINT: 1, + SMALLINT: 2, + INT: 3, + BIGINT: 4, + DECIMAL: 5, + REAL: 6, + DOUBLE: 7, + CHAR: 8, + VARCHAR1: 9, + NCHAR: 10, + NVARCHAR: 11, + BINARY: 12, + VARBINARY: 13, + DATE: 14, + TIME: 15, + TIMESTAMP: 16, + TIME_TZ: 17, + TIME_LTZ: 18, + TIMESTAMP_TZ: 19, + TIMESTAMP_LTZ: 20, + INTERVAL_YM: 21, + INTERVAL_DS: 22, + ROWID: 23, + UROWID: 24, + CLOB: 25, + NCLOB: 26, + BLOB: 27, + BOOLEAN: 28, + STRING: 29, + NSTRING: 30, + LOCATOR: 31, + NLOCATOR: 32, + BSTRING: 33, + DECIMAL_DIGIT_ARRAY: 34, + VARCHAR2: 35, + UNUSED1: 36, + UNUSED2: 37, + UNUSED3: 38, + UNUSED4: 39, + UNUSED5: 40, + UNUSED6: 41, + UNUSED7: 42, + UNUSED8: 43, + UNUSED9: 44, + TABLE: 45, + UNUSED11: 46, + UNUSED1F: 47, + ABAPSTREAM: 48, + ABAPSTRUCT: 49, + UNUSED12: 50, + TEXT: 51, + SHORTTEXT: 52, + UNUSED15: 53, + UNUSED16: 54, + ALPHANUM: 55, + UNUSED18: 56, + UNUSED19: 57, + UNUSED20: 58, + UNUSED21: 59, + UNUSED22: 60, + LONGDATE: 61, + SECONDDATE: 62, + DAYDATE: 63, + SECONDTIME: 64, + CSDATE: 65, + CSTIME: 66, + BLOB_DISK: 71, + CLOB_DISK: 72, + NCLOB_DISK: 73 +}; \ No newline at end of file diff --git a/lib/protocol/common/index.js b/lib/protocol/common/index.js new file mode 100644 index 0000000..2cf9dc7 --- /dev/null +++ b/lib/protocol/common/index.js @@ -0,0 +1,58 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = require('../../util'); + +exports.ClientDistributionMode = require('./ClientDistributionMode'); +exports.CommandOption = require('./CommandOption'); +exports.ConnectOption = require('./ConnectOption'); +exports.DataFormatVersion = require('./DataFormatVersion'); +exports.DistributionProtocolVersion = require('./DistributionProtocolVersion'); +exports.ErrorLevel = require('./ErrorLevel'); +exports.FunctionCode = require('./FunctionCode'); +exports.IoType = require('./IoType'); +exports.LobOptions = require('./LobOptions'); +exports.MessageType = require('./MessageType'); +exports.ParameterMode = require('./ParameterMode'); +exports.PartKind = require('./PartKind'); +exports.ResultSetAttributes = require('./ResultSetAttributes'); +exports.SegmentKind = require('./SegmentKind'); +exports.SessionContext = require('./SessionContext'); +exports.StatementContext = require('./StatementContext'); +exports.TopologyInformation = require('./TopologyInformation'); +exports.TypeCode = require('./TypeCode'); + +invert('ConnectOption'); +invert('TopologyInformation'); +invert('MessageType'); +invert('StatementContext'); +invert('SessionContext'); +invert('TypeCode'); +invert('FunctionCode'); +invert('PartKind'); +invert('SegmentKind'); + +function invert(name) { + /*jshint forin: false */ + var source = exports[name]; + var target = {}; + for (var key in source) { + target[source[key]] = key; + } + exports[name + 'Name'] = target; +} + + +util.extend(exports, require('./Constants')); \ No newline at end of file diff --git a/lib/protocol/data/Binary.js b/lib/protocol/data/Binary.js new file mode 100644 index 0000000..a68fda1 --- /dev/null +++ b/lib/protocol/data/Binary.js @@ -0,0 +1,42 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +exports.read = read; +exports.write = write; +exports.getByteLength = getByteLength; +exports.getArgumentCount = getArgumentCount; + +function read(part) { + return part.buffer; +} + +function write(part, value) { + /* jshint validthis:true */ + + value = value || this; + part = part || {}; + part.argumentCount = getArgumentCount(value); + part.buffer = value; + return part; +} + +function getByteLength(value) { + return value.length; +} + +function getArgumentCount(value) { + /* jshint unused:false */ + return 1; +} \ No newline at end of file diff --git a/lib/protocol/data/Default.js b/lib/protocol/data/Default.js new file mode 100644 index 0000000..fcb354a --- /dev/null +++ b/lib/protocol/data/Default.js @@ -0,0 +1,42 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = require('../../util'); + +exports.read = read; +exports.write = write; +exports.getByteLength = getByteLength; +exports.getArgumentCount = getArgumentCount; + +function read(part) { + return part; +} + +function write(part, sourcePart) { + /* jshint validthis:true */ + + sourcePart = sourcePart || this; + part = part || {}; + return util.extend(part, sourcePart); +} + +function getByteLength(part) { + return part.length; +} + +function getArgumentCount(part) { + /* jshint unused:false */ + return part.argumentCount; +} \ No newline at end of file diff --git a/lib/protocol/data/Fields.js b/lib/protocol/data/Fields.js new file mode 100644 index 0000000..6bf282d --- /dev/null +++ b/lib/protocol/data/Fields.js @@ -0,0 +1,111 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = require('../../util'); + +exports.read = read; +exports.write = write; +exports.getByteLength = getByteLength; +exports.getArgumentCount = getArgumentCount; + +function read(part) { + var offset = 0; + var buffer = part.buffer; + var fields = []; + + var numberOfFields = buffer.readUInt16LE(offset); + offset += 2; + var fieldLength; + for (var i = 0; i < numberOfFields; i++) { + fieldLength = buffer[offset]; + offset += 1; + if (fieldLength === 0xff) { + fieldLength = buffer.readUInt16LE(offset); + offset += 2; + } + fields.push(buffer.slice(offset, offset + fieldLength)); + offset += fieldLength; + } + return fields; +} + +function write(part, fields) { + /* jshint validthis:true */ + + var offset = 0; + part = part || {}; + fields = fields || this; + + var byteLength = getByteLength(fields); + var buffer = new Buffer(byteLength); + + buffer.writeUInt16LE(fields.length, 0); + offset += 2; + + var field, fieldLength, data; + for (var i = 0; i < fields.length; i++) { + field = fields[i]; + if (Buffer.isBuffer(field)) { + data = field; + } else if (util.isArray(field)) { + data = write({}, field).buffer; + } else { + data = new Buffer(field, 'ascii'); + } + fieldLength = data.length; + if (fieldLength <= 250) { + buffer[offset] = fieldLength; + offset += 1; + } else { + buffer[offset] = 0xff; + offset += 1; + buffer.writeUInt16LE(fieldLength, offset); + offset += 2; + } + data.copy(buffer, offset); + offset += fieldLength; + } + part.argumentCount = getArgumentCount(fields); + part.buffer = buffer; + return part; +} + +function getByteLength(fields) { + var byteLength = 2; + var fieldLength; + for (var i = 0; i < fields.length; i++) { + fieldLength = getByteLengthOfField(fields[i]); + if (fieldLength <= 250) { + byteLength += fieldLength + 1; + } else { + byteLength += fieldLength + 3; + } + } + return byteLength; +} + +function getArgumentCount(fields) { + /* jshint unused:false */ + return 1; +} + +function getByteLengthOfField(field) { + if (Buffer.isBuffer(field)) { + return field.length; + } else if (util.isArray(field)) { + return getByteLength(field); + } + return Buffer.byteLength(field, 'ascii'); +} \ No newline at end of file diff --git a/lib/protocol/data/Int32.js b/lib/protocol/data/Int32.js new file mode 100644 index 0000000..5c1d5f7 --- /dev/null +++ b/lib/protocol/data/Int32.js @@ -0,0 +1,43 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +exports.read = read; +exports.write = write; +exports.getByteLength = getByteLength; +exports.getArgumentCount = getArgumentCount; + +function read(part) { + return part.buffer.readInt32LE(0); +} + +function write(part, value) { + /* jshint validthis:true */ + + value = value || this; + part = part || {}; + part.argumentCount = getArgumentCount(value); + part.buffer = new Buffer(4); + part.buffer.writeInt32LE(value, 0); + return part; +} + +function getByteLength(value) { + return 4; +} + +function getArgumentCount(value) { + /* jshint unused:false */ + return 1; +} \ No newline at end of file diff --git a/lib/protocol/data/MultilineOptions.js b/lib/protocol/data/MultilineOptions.js new file mode 100644 index 0000000..a36b326 --- /dev/null +++ b/lib/protocol/data/MultilineOptions.js @@ -0,0 +1,74 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var opts = require('./Options'); +var getByteLengthOfOptions = opts.getByteLength; +var writeOptions = opts.write; +var readOptions = opts._read; + +exports.read = read; +exports.write = write; +exports.getByteLength = getByteLength; +exports.getArgumentCount = getArgumentCount; + +function read(part) { + var offset = 0; + var buffer = part.buffer; + var lines = []; + var numberOfOptions, options; + for (var i = 0; i < part.argumentCount; i++) { + numberOfOptions = buffer.readInt16LE(offset); + offset += 2; + options = new Array(numberOfOptions); + offset = readOptions.call(options, buffer, offset); + lines.push(options); + } + return lines; +} + +function write(part, lines) { + /* jshint validthis:true */ + + var offset = 0; + lines = lines || this; + part = part || {}; + var byteLength = getByteLength(lines); + var buffer = new Buffer(byteLength); + var options; + for (var i = 0; i < lines.length; i++) { + options = writeOptions({}, lines[i]); + buffer.writeInt16LE(options.argumentCount, offset); + offset += 2; + options.buffer.copy(buffer, offset); + offset += options.buffer.length; + } + part.argumentCount = getArgumentCount(lines); + part.buffer = buffer; + return part; +} + +function getByteLength(lines) { + var byteLength = 0; + + for (var i = 0; i < lines.length; i++) { + byteLength += 2 + getByteLengthOfOptions(lines[i]); + } + return byteLength; +} + +function getArgumentCount(lines) { + /* jshint unused:false */ + return lines.length; +} \ No newline at end of file diff --git a/lib/protocol/data/Options.js b/lib/protocol/data/Options.js new file mode 100644 index 0000000..95a9601 --- /dev/null +++ b/lib/protocol/data/Options.js @@ -0,0 +1,166 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = require('../../util'); +var bignum = util.bignum; +var common = require('../common'); +var TypeCode = common.TypeCode; + +exports.read = read; +exports._read = _read; +exports.write = write; +exports.getByteLength = getByteLength; +exports.getArgumentCount = getArgumentCount; + +function read(part) { + var options = new Array(part.argumentCount); + _read.call(options, part.buffer, 0); + return options; +} + +function _read(buffer, offset) { + /* jshint validthis:true */ + + offset = offset || 0; + var options = this; + var option, length; + for (var i = 0; i < options.length; i++) { + option = { + name: buffer[offset], + type: buffer[offset + 1], + value: undefined + }; + offset += 2; + switch (option.type) { + case TypeCode.BOOLEAN: + option.value = buffer[offset] ? true : false; + offset += 1; + break; + case TypeCode.INT: + option.value = buffer.readInt32LE(offset); + offset += 4; + break; + case TypeCode.BIGINT: + option.value = bignum.readInt64LE(buffer, offset); + offset += 8; + break; + case TypeCode.DOUBLE: + option.value = buffer.readDoubleLE(offset); + offset += 8; + break; + case TypeCode.STRING: + length = buffer.readInt16LE(offset); + offset += 2; + option.value = buffer.toString('utf-8', offset, offset + length); + offset += length; + break; + case TypeCode.BSTRING: + length = buffer.readInt16LE(offset); + offset += 2; + option.value = new Buffer(length); + buffer.copy(option.value, 0, offset, offset + length); + offset += length; + break; + } + options[i] = option; + } + return offset; +} + +function write(part, options) { + /* jshint validthis:true */ + + var offset = 0; + part = part || {}; + options = options || this; + + var byteLength = getByteLength(options); + var buffer = new Buffer(byteLength); + var option; + for (var i = 0; i < options.length; i++) { + option = options[i]; + buffer[offset] = option.name; + buffer[offset + 1] = option.type; + offset += 2; + switch (option.type) { + case TypeCode.BOOLEAN: + buffer[offset] = !! option.value ? 1 : 0; + offset += 1; + break; + case TypeCode.INT: + buffer.writeInt32LE(option.value, offset); + offset += 4; + break; + case TypeCode.BIGINT: + buffer.writeInt64LE(option.value, offset); + offset += 8; + break; + case TypeCode.DOUBLE: + buffer.writeDoubleLE(option.value, offset); + offset += 8; + break; + case TypeCode.STRING: + byteLength = Buffer.byteLength(option.value, 'utf-8'); + buffer.writeInt16LE(byteLength, offset); + offset += 2; + buffer.write(option.value, offset, byteLength, 'utf-8'); + offset += byteLength; + break; + case TypeCode.BSTRING: + byteLength = option.value.length; + buffer.writeInt16LE(byteLength, offset); + offset += 2; + option.value.copy(buffer, offset); + offset += byteLength; + break; + } + } + part.argumentCount = options.length; + part.buffer = buffer; + return part; +} + +function getByteLength(options) { + var byteLength = 0; + var option; + for (var i = 0; i < options.length; i++) { + option = options[i]; + switch (option.type) { + case TypeCode.BOOLEAN: + byteLength += 3; + break; + case TypeCode.INT: + byteLength += 6; + break; + case TypeCode.BIGINT: + byteLength += 10; + break; + case TypeCode.DOUBLE: + byteLength += 10; + break; + case TypeCode.STRING: + byteLength += 4 + Buffer.byteLength(option.value, 'utf-8'); + break; + case TypeCode.BSTRING: + byteLength += 4 + option.value.length; + break; + } + } + return byteLength; +} + +function getArgumentCount(options) { + return options.length; +} \ No newline at end of file diff --git a/lib/protocol/data/ParameterMetadata.js b/lib/protocol/data/ParameterMetadata.js new file mode 100644 index 0000000..164f7e5 --- /dev/null +++ b/lib/protocol/data/ParameterMetadata.js @@ -0,0 +1,76 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var common = require('../common'); +var ParameterMode = common.ParameterMode; + +exports.read = read; +exports.getArgumentCount = getArgumentCount; + +function read(part) { + var params = new Array(part.argumentCount); + _read.call(params, part.buffer, 0); + return params; +} + +function _read(buffer, offset) { + /* jshint validthis:true */ + + offset = offset || 0; + var params = this; + var textOffset = offset + params.length * 16; + for (var i = 0; i < params.length; i++) { + params[i] = new Parameter(buffer, offset, textOffset); + offset += 16; + } + return offset; +} + +function getArgumentCount(params) { + /* jshint unused:false */ + return params.length; +} + +function Parameter(buffer, offset, textOffset) { + this.mode = buffer[offset]; + this.dataType = buffer[offset + 1]; + this.ioType = buffer[offset + 2]; + var nameOffset = buffer.readInt32LE(offset + 4); + if (nameOffset < 0) { + this.name = undefined; + } else { + var start = textOffset + nameOffset; + var length = buffer[start]; + start += 1; + this.name = buffer.toString('utf-8', start, start + length); + } + this.length = buffer.readInt16LE(offset + 8); + this.fraction = buffer.readInt16LE(offset + 10); +} + +Parameter.prototype.isReadOnly = function isReadOnly() { + /* jshint bitwise:false */ + return this.mode & ParameterMode.READONLY ? true : false; +}; + +Parameter.prototype.isMandatory = function isMandatory() { + /* jshint bitwise:false */ + return this.mode & ParameterMode.MANDATORY ? true : false; +}; + +Parameter.prototype.isAutoIncrement = function isAutoIncrement() { + /* jshint bitwise:false */ + return this.mode & ParameterMode.AUTO_INCREMENT ? true : false; +}; \ No newline at end of file diff --git a/lib/protocol/data/Parameters.js b/lib/protocol/data/Parameters.js new file mode 100644 index 0000000..6317c4c --- /dev/null +++ b/lib/protocol/data/Parameters.js @@ -0,0 +1,159 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = require('../../util'); +var common = require('../common'); +var TypeCode = common.TypeCode; +var bignum = util.bignum; + +exports.write = write; +exports.getArgumentCount = getArgumentCount; + +function write(part, params) { + /* jshint validthis:true, bitwise:false */ + + part = part || {}; + params = params || this; + + var buffers = []; + var byteLength; + var buffer, type, value; + for (var i = 0; i < params.length; i++) { + type = params[i].type; + value = params[i].value; + if (typeof value === 'undefined' || value === null) { + buffer = new Buffer(1); + buffer[0] = type | 0x80; + buffers.push(buffer); + } else { + switch (type) { + case TypeCode.TINYINT: + buffer = new Buffer(2); + buffer[0] = type; + buffer.writeInt8(value, 1); + buffers.push(buffer); + break; + case TypeCode.SMALLINT: + buffer = new Buffer(3); + buffer[0] = type; + buffer.writeInt16LE(value, 1); + buffers.push(buffer); + break; + case TypeCode.INT: + buffer = new Buffer(5); + buffer[0] = type; + buffer.writeInt32LE(value, 1); + buffers.push(buffer); + break; + case TypeCode.BIGINT: + buffer = new Buffer(9); + buffer[0] = type; + bignum.writeInt64LE(buffer, value, 1); + buffers.push(buffer); + break; + case TypeCode.REAL: + buffer = new Buffer(5); + buffer[0] = type; + buffer.writeFloatLE(value, 1); + buffers.push(buffer); + break; + case TypeCode.DOUBLE: + buffer = new Buffer(9); + buffer[0] = type; + buffer.writeDoubleLE(value, 1); + buffers.push(buffer); + break; + case TypeCode.STRING: + case TypeCode.NSTRING: + case TypeCode.VARCHAR1: + case TypeCode.VARCHAR2: + case TypeCode.CHAR: + case TypeCode.NCHAR: + case TypeCode.NVARCHAR: + case TypeCode.SHORTTEXT: + case TypeCode.ALPHANUM: + byteLength = Buffer.byteLength(value, 'utf-8'); + if (byteLength <= 245) { + buffer = new Buffer(2 + byteLength); + buffer[0] = type; + buffer[1] = byteLength; + buffer.write(value, 2, byteLength, 'utf-8'); + } else if (byteLength <= 32767) { + buffer = new Buffer(4 + byteLength); + buffer[0] = type; + buffer[1] = 246; + buffer.writeInt16LE(byteLength, 2); + buffer.write(value, 4, byteLength, 'utf-8'); + } else { + buffer = new Buffer(6 + byteLength); + buffer[0] = type; + buffer[1] = 247; + buffer.writeInt32LE(byteLength, 2); + buffer.write(value, 6, byteLength, 'utf-8'); + } + buffers.push(buffer); + break; + case TypeCode.BSTRING: + case TypeCode.BINARY: + case TypeCode.VARBINARY: + byteLength = value.length; + if (byteLength <= 245) { + buffer = new Buffer(2 + byteLength); + buffer[0] = type; + buffer[1] = byteLength; + value.copy(buffer, 2); + } else if (byteLength <= 32767) { + buffer = new Buffer(4 + byteLength); + buffer[0] = type; + buffer[1] = 246; + buffer.writeInt16LE(byteLength, 2); + value.copy(buffer, 4); + } else { + buffer = new Buffer(6 + byteLength); + buffer[0] = type; + buffer[1] = 247; + buffer.writeInt32LE(byteLength, 2); + value.copy(buffer, 6); + } + buffers.push(buffer); + break; + case TypeCode.DATE: + buffer = new Buffer(6); + buffer[0] = type; + buffer.writeInt16LE(~~value.substring(0, 4), 1); + buffer.writeInt8(~~value.substring(5, 7), 3); + buffer.writeInt16LE(~~value.substring(8, 10), 4); + buffers.push(buffer); + break; + case TypeCode.TIME: + buffer = new Buffer(5); + buffer[0] = type; + buffer.writeInt8(~~value.substring(0, 2), 1); + buffer.writeInt8(~~value.substring(3, 5), 2); + buffer.writeUInt16LE(Math.round(parseFloat(value.substring(6))), 3); + buffers.push(buffer); + break; + } + } + } + part.argumentCount = getArgumentCount(params); + part.buffer = Buffer.concat(buffers); + return part; +} + +function getArgumentCount(params) { + /* jshint unused:false */ + return 1; +} \ No newline at end of file diff --git a/lib/protocol/data/ReadLobReply.js b/lib/protocol/data/ReadLobReply.js new file mode 100644 index 0000000..c64371b --- /dev/null +++ b/lib/protocol/data/ReadLobReply.js @@ -0,0 +1,72 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var common = require('../common'); +var LobOptions = common.LobOptions; + +exports.read = read; +exports.getByteLength = getByteLength; +exports.getArgumentCount = getArgumentCount; + +function read(part) { + var offset = 0; + var buffer = part.buffer; + + var locatorId = buffer.slice(offset, offset + 8); + offset += 8; + var options = buffer[offset]; + offset += 1; + var length = buffer.readInt32LE(offset); + offset = 16; + var chunk = buffer.slice(offset, offset + length); + offset += length; + return new ReadLobReply(locatorId, options, chunk); +} + +function getByteLength(chunk) { + return 16 + chunk.length; +} + +function getArgumentCount(value) { + /* jshint unused:false */ + return 1; +} + +function ReadLobReply(locatorId, options, chunk) { + this.locatorId = locatorId; + this.options = options; + this.chunk = chunk; +} + +Object.defineProperties(ReadLobReply.prototype, { + isNull: { + get: function isNull() { + /* jshint bitwise:false */ + return !!(this.options & LobOptions.NULL_INDICATOR); + } + }, + isDataIncluded: { + get: function isDataIncluded() { + /* jshint bitwise:false */ + return !!(this.options & LobOptions.DATA_INCLUDED); + } + }, + isLast: { + get: function isLast() { + /* jshint bitwise:false */ + return !!(this.options & LobOptions.LAST_DATA); + } + } +}); \ No newline at end of file diff --git a/lib/protocol/data/ReadLobRequest.js b/lib/protocol/data/ReadLobRequest.js new file mode 100644 index 0000000..22ea0c3 --- /dev/null +++ b/lib/protocol/data/ReadLobRequest.js @@ -0,0 +1,57 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = require('../../util'); +var bignum = util.bignum; + +var READ_LOB_REQUEST_LENGTH = 24; + +exports.write = write; +exports.getByteLength = getByteLength; +exports.getArgumentCount = getArgumentCount; + +function write(part, req) { + /* jshint validthis:true */ + + var offset = 0; + part = part || {}; + req = req || this; + + var buffer = new Buffer(READ_LOB_REQUEST_LENGTH); + if (Buffer.isBuffer(req.locatorId)) { + req.locatorId.copy(buffer, offset, 0, 8); + } else { + bignum.writeInt64LE(buffer, req.locatorId, offset); + } + offset += 8; + bignum.writeInt64LE(buffer, req.offset, offset); + offset += 8; + buffer.writeInt32LE(req.length, offset); + offset += 4; + buffer.fill(0x00, offset); + part.argumentCount = getArgumentCount(req); + part.buffer = buffer; + return part; +} + +function getByteLength(req) { + /* jshint unused:false */ + return 24; +} + +function getArgumentCount(req) { + /* jshint unused:false */ + return 1; +} \ No newline at end of file diff --git a/lib/protocol/data/ResultSetMetadata.js b/lib/protocol/data/ResultSetMetadata.js new file mode 100644 index 0000000..136f059 --- /dev/null +++ b/lib/protocol/data/ResultSetMetadata.js @@ -0,0 +1,84 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var common = require('../common'); +var ParameterMode = common.ParameterMode; +var READONLY = ParameterMode.READONLY; +var AUTO_INCREMENT = ParameterMode.AUTO_INCREMENT; +var MANDATORY = ParameterMode.MANDATORY; + +exports.read = read; +exports.getArgumentCount = getArgumentCount; + +function read(part) { + var columns = new Array(part.argumentCount); + _read.call(columns, part.buffer, 0); + return columns; +} + +function _read(buffer, offset) { + /* jshint validthis:true */ + + offset = offset || 0; + var columns = this; + var textOffset = offset + columns.length * 24; + for (var i = 0; i < columns.length; i++) { + columns[i] = new Column(buffer, offset, textOffset); + offset += 24; + } + return offset; +} + +function getArgumentCount(columns) { + /* jshint unused:false */ + return columns.length; +} + +function Column(buffer, offset, textOffset) { + this.mode = buffer.readInt8(offset); + this.dataType = buffer.readInt8(offset + 1); + this.fraction = buffer.readInt16LE(offset + 2); + this.length = buffer.readInt16LE(offset + 4); + offset += 8; + + ['tableName', 'schemaName', 'columnName', 'columnDisplayName'].forEach( + function readName(name) { + var start = buffer.readInt32LE(offset); + offset += 4; + if (start < 0) { + this[name] = undefined; + } else { + start += textOffset; + var length = buffer.readUInt8(start); + start += 1; + this[name] = buffer.toString('utf-8', start, start + length); + } + }, this); +} + +Column.prototype.isReadOnly = function isReadOnly() { + /* jshint bitwise:false */ + return this.mode & READONLY ? true : false; +}; + +Column.prototype.isMandatory = function isMandatory() { + /* jshint bitwise:false */ + return this.mode & MANDATORY ? true : false; +}; + +Column.prototype.isAutoIncrement = function isAutoIncrement() { + /* jshint bitwise:false */ + return this.mode & AUTO_INCREMENT ? true : false; +}; \ No newline at end of file diff --git a/lib/protocol/data/SqlError.js b/lib/protocol/data/SqlError.js new file mode 100644 index 0000000..4a9fe44 --- /dev/null +++ b/lib/protocol/data/SqlError.js @@ -0,0 +1,66 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = require('../../util'); +var ErrorLevel = require('../common/ErrorLevel'); + +exports.read = read; +exports.getByteLength = getByteLength; +exports.getArgumentCount = getArgumentCount; + +function read(part) { + var err = new SqlError(); + _read.call(err, part.buffer, 0); + return err; +} + +function _read(buffer, offset) { + /* jshint validthis:true */ + + offset = offset || 0; + this.code = buffer.readInt32LE(offset); + offset += 4; + this.position = buffer.readInt32LE(offset); + offset += 4; + var length = buffer.readInt32LE(offset); + offset += 4; + this.level = buffer.readInt8(offset); + if (this.level === ErrorLevel.FATAL) { + this.fatal = true; + } + offset += 1; + this.sqlState = buffer.toString('ascii', offset, offset + 5); + offset += 5; + this.message = buffer.toString('utf-8', offset, offset + length); + offset += util.alignLength(length, 8); + return offset; +} + +function getByteLength(err) { + return 18 + Buffer.byteLength(err.message); +} + +function getArgumentCount(err) { + /* jshint unused:false */ + return 1; +} + +function SqlError() { + this.message = undefined; + this.code = undefined; + this.sqlState = undefined; + this.level = undefined; + this.position = undefined; +} \ No newline at end of file diff --git a/lib/protocol/data/Text.js b/lib/protocol/data/Text.js new file mode 100644 index 0000000..cf38c3e --- /dev/null +++ b/lib/protocol/data/Text.js @@ -0,0 +1,42 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +exports.read = read; +exports.write = write; +exports.getByteLength = getByteLength; +exports.getArgumentCount = getArgumentCount; + +function read(part) { + return part.buffer.toString('utf-8'); +} + +function write(part, value) { + /* jshint validthis:true */ + + value = value || this; + part = part || {}; + part.argumentCount = getArgumentCount(value); + part.buffer = new Buffer(value, 'utf-8'); + return part; +} + +function getByteLength(value) { + return Buffer.byteLength(value); +} + +function getArgumentCount(value) { + /* jshint unused:false */ + return 1; +} \ No newline at end of file diff --git a/lib/protocol/data/Text20.js b/lib/protocol/data/Text20.js new file mode 100644 index 0000000..5986b09 --- /dev/null +++ b/lib/protocol/data/Text20.js @@ -0,0 +1,42 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +exports.read = read; +exports.write = write; +exports.getByteLength = getByteLength; +exports.getArgumentCount = getArgumentCount; + +function read(part) { + return part.buffer.slice(1).toString('utf-8'); +} + +function write(part, value) { + /* jshint validthis:true */ + + value = value || this; + part = part || {}; + part.argumentCount = getArgumentCount(value); + part.buffer = new Buffer(' ' + value, 'utf-8'); + return part; +} + +function getByteLength(value) { + return Buffer.byteLength(value) + 1; +} + +function getArgumentCount(value) { + /* jshint unused:false */ + return 1; +} \ No newline at end of file diff --git a/lib/protocol/data/index.js b/lib/protocol/data/index.js new file mode 100644 index 0000000..163da83 --- /dev/null +++ b/lib/protocol/data/index.js @@ -0,0 +1,66 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var common = require('../common'); +var PartKind = common.PartKind; + +var Binary = require('./Binary'); +var Default = require('./Default'); +var Fields = require('./Fields'); +var Int32 = require('./Int32'); +var MultilineOptions = require('./MultilineOptions'); +var Options = require('./Options'); +var ParameterMetadata = require('./ParameterMetadata'); +var Parameters = require('./Parameters'); +var ReadLobReply = require('./ReadLobReply'); +var ReadLobRequest = require('./ReadLobRequest'); +var ResultSetMetadata = require('./ResultSetMetadata'); +var SqlError = require('./SqlError'); +var Text = require('./Text'); +var Text20 = require('./Text20'); + +var rw = module.exports = {}; +rw[PartKind.AUTHENTICATION] = Fields; +rw[PartKind.COMMAND] = Text; +rw[PartKind.CLIENT_ID] = Text20; +rw[PartKind.CONNECT_OPTIONS] = Options; +rw[PartKind.COMMIT_OPTIONS] = Options; +rw[PartKind.ERROR] = SqlError; +rw[PartKind.FETCH_OPTIONS] = Options; +rw[PartKind.FETCH_SIZE] = Int32; +rw[PartKind.TRANSACTION_ID] = Binary; +rw[PartKind.TOPOLOGY_INFORMATION] = MultilineOptions; +rw[PartKind.PARAMETERS] = Parameters; +rw[PartKind.PARAMETER_METADATA] = ParameterMetadata; +rw[PartKind.RESULT_SET_ID] = Binary; +rw[PartKind.RESULT_SET] = Default; +rw[PartKind.OUTPUT_PARAMETERS] = Default; +rw[PartKind.RESULT_SET_METADATA] = ResultSetMetadata; +rw[PartKind.ROWS_AFFECTED] = Int32; +rw[PartKind.READ_LOB_REQUEST] = ReadLobRequest; +rw[PartKind.READ_LOB_REPLY] = ReadLobReply; +rw[PartKind.SESSION_CONTEXT] = Options; +rw[PartKind.STATEMENT_CONTEXT] = Options; +rw[PartKind.STATEMENT_ID] = Binary; +rw[PartKind.PARTITION_INFORMATION] = Default; +rw[PartKind.TRANSACTION_FLAGS] = Options; +rw[PartKind.TABLE_NAME] = Text; + +for (var name in PartKind) { + var kind = PartKind[name]; + if (!rw[kind]) { + rw[kind] = Default; + } +} \ No newline at end of file diff --git a/lib/protocol/index.js b/lib/protocol/index.js new file mode 100644 index 0000000..417acf3 --- /dev/null +++ b/lib/protocol/index.js @@ -0,0 +1,26 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +exports.Connection = require('./Connection'); +exports.Parser = require('./Parser'); +exports.Result = require('./Result'); +exports.ResultSet = require('./ResultSet'); +exports.Statement = require('./Statement'); +exports.Stringifier = require('./Stringifier'); +exports.auth = require('./auth'); +exports.common = require('./common'); +exports.data = require('./data'); +exports.reply = require('./reply'); +exports.request = require('./request'); \ No newline at end of file diff --git a/lib/protocol/reply/Part.js b/lib/protocol/reply/Part.js new file mode 100644 index 0000000..e3543f9 --- /dev/null +++ b/lib/protocol/reply/Part.js @@ -0,0 +1,70 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = require('../../util'); +var common = require('../common'); +var PartKind = common.PartKind; + +var PART_HEADER_LENGTH = common.PART_HEADER_LENGTH; +var BIG_ARGUMENT_COUNT_INDICATOR = -1; + +module.exports = Part; + +function Part(kind, attributes, argumentCount) { + this.kind = kind || PartKind.NIL; + this.attributes = attributes || 0; + this.argumentCount = argumentCount || 0; + this.buffer = undefined; +} + +Object.defineProperty(Part.prototype, 'byteLength', { + get: function getByteLength() { + var byteLength = PART_HEADER_LENGTH; + if (Buffer.isBuffer(this.buffer)) { + byteLength += util.alignLength(this.buffer.length, 8); + } + return byteLength; + } +}); + +Part.createPart = createPart; + +function createPart(buffer, offset) { + var part = new Part(); + readPart.call(part, buffer, offset); + return part; +} + +Part.read = readPart; + +function readPart(buffer, offset) { + /* jshint validthis:true */ + offset = offset || 0; + + this.kind = buffer[offset]; + this.attributes = buffer[offset + 1]; + this.argumentCount = buffer.readInt16LE(offset + 2); + if (this.argumentCount === BIG_ARGUMENT_COUNT_INDICATOR) { + this.argumentCount = buffer.readInt32LE(offset + 4); + } + var length = buffer.readInt32LE(offset + 8); + offset += PART_HEADER_LENGTH; + if (length > 0) { + this.buffer = new Buffer(length); + buffer.copy(this.buffer, 0, offset, offset + length); + offset += util.alignLength(length, 8); + } + return offset; +} \ No newline at end of file diff --git a/lib/protocol/reply/Segment.js b/lib/protocol/reply/Segment.js new file mode 100644 index 0000000..7750394 --- /dev/null +++ b/lib/protocol/reply/Segment.js @@ -0,0 +1,161 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = require('../../util'); +var Part = require('./Part'); +var data = require('../data'); +var common = require('../common'); +var FunctionCode = common.FunctionCode; +var SegmentKind = common.SegmentKind; +var PartKindName = common.PartKindName; +var MAX_PACKET_SIZE = common.MAX_PACKET_SIZE; +var PACKET_HEADER_LENGTH = common.PACKET_HEADER_LENGTH; +var SEGMENT_HEADER_LENGTH = common.SEGMENT_HEADER_LENGTH; + +module.exports = Segment; + +function Segment(kind, functionCode) { + this.kind = kind || SegmentKind.INVALID; + this.functionCode = functionCode || FunctionCode.NIL; + this.parts = []; +} + +Segment.prototype.push = function push(part) { + this.parts.push(part); +}; + +Segment.create = createSegment; + +function createSegment(buffer, offset) { + var segment = new Segment(); + readSegment.call(segment, buffer, offset); + return segment; +} + +Segment.read = readSegment; + +function readSegment(buffer, offset) { + /* jshint validthis:true */ + offset = offset || 0; + + var numberOfParts = buffer.readInt16LE(offset + 8); + this.kind = buffer.readInt8(offset + 12); + this.functionCode = buffer.readInt16LE(offset + 14); + offset += SEGMENT_HEADER_LENGTH; + for (var i = 0; i < numberOfParts; i++) { + var part = new Part(); + offset = Part.read.call(part, buffer, offset); + this.push(part); + } + return offset; +} + +Segment.prototype.toBuffer = function toBuffer(size) { + size = size || (MAX_PACKET_SIZE - PACKET_HEADER_LENGTH); + var remainingSize = size - SEGMENT_HEADER_LENGTH; + var length = SEGMENT_HEADER_LENGTH; + var buffers = this.parts.map(function getPartBuffer(part) { + var buffer = part.toBuffer(remainingSize); + remainingSize -= buffer.length; + length += buffer.length; + return buffer; + }); + + var header = new Buffer(SEGMENT_HEADER_LENGTH); + // Length of the segment, including the header + header.writeInt32LE(length, 0); + // Offset of the segment within the message buffer + header.writeInt32LE(0, 4); + // Number of contained parts + header.writeInt16LE(this.parts.length, 8); + // Number of segment within packet + header.writeInt16LE(1, 10); + // Segment kind + header.writeInt8(SegmentKind.REQUEST, 12); + // Message type + header.writeInt8(this.type, 13); + // Whether the command shall be committed + header.writeInt8(this.commitImmediateley, 14); + // Command options + header.writeInt8(this.commandOptions, 15); + // Filler + header.fill(0x00, 16); + + buffers.unshift(header); + return Buffer.concat(buffers, length); +}; + +Segment.prototype.getPart = function (kind) { + var parts = this.parts.filter(function isKindOf(part) { + return part.kind === kind; + }); + if (parts.length === 1) { + return parts[0]; + } else if (parts.length > 1) { + return parts; + } + return null; +}; + +Segment.prototype.getReply = function getReply() { + var reply = new Reply(this.kind, this.functionCode); + for (var i = 0; i < this.parts.length; i++) { + reply.add(this.parts[i]); + } + return reply; +}; + +function Reply(kind, functionCode) { + this.kind = kind; + this.functionCode = functionCode; + this.resultSets = []; +} + +Reply.prototype.addResultSetFragment = function addResultSetFragment(name, + value) { + var resultSet; + if (this.resultSets.length) { + resultSet = this.resultSets[this.resultSets.length - 1]; + } + if (name === 'resultSet') { + name = 'data'; + } else if (name === 'resultSetId') { + name = 'id'; + } else if (name === 'resultSetMetadata') { + name = 'metadata'; + } + if (!resultSet || resultSet[name]) { + resultSet = {}; + resultSet[name] = value; + this.resultSets.push(resultSet); + } else { + resultSet[name] = value; + } +}; + +Reply.prototype.add = function add(part) { + var name = util._2cc(PartKindName[part.kind]); + var value = data[part.kind].read(part); + if (/^resultSet/.test(name) || name === 'tableName') { + this.addResultSetFragment(name, value); + } else if (util.isUndefined(this[name])) { + this[name] = value; + } else if (util.isArray(this[name])) { + this[name].push(value); + } else { + var existingValue = this[name]; + this[name] = [existingValue, value]; + } +}; \ No newline at end of file diff --git a/lib/protocol/reply/index.js b/lib/protocol/reply/index.js new file mode 100644 index 0000000..fbbe3cf --- /dev/null +++ b/lib/protocol/reply/index.js @@ -0,0 +1,17 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +exports.Part = require('./Part'); +exports.Segment = require('./Segment'); \ No newline at end of file diff --git a/lib/protocol/request/Authenticate.js b/lib/protocol/request/Authenticate.js new file mode 100644 index 0000000..d2a8a3a --- /dev/null +++ b/lib/protocol/request/Authenticate.js @@ -0,0 +1,31 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var Segment = require('./Segment'); +var common = require('../common'); +var MessageType = common.MessageType; +var PartKind = common.PartKind; + +module.exports = function authenticate(method, options) { + var segment = new Segment(MessageType.AUTHENTICATE); + + // authentication + segment.add({ + kind: PartKind.AUTHENTICATION, + module: method.Authentication + }, options.authentication || options); + + return segment; +}; \ No newline at end of file diff --git a/lib/protocol/request/CloseResultSet.js b/lib/protocol/request/CloseResultSet.js new file mode 100644 index 0000000..f13e17b --- /dev/null +++ b/lib/protocol/request/CloseResultSet.js @@ -0,0 +1,35 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var Segment = require('./Segment'); +var common = require('../common'); +var MessageType = common.MessageType; +var PartKind = common.PartKind; + +module.exports = function fetchNext(options) { + var segment = new Segment(MessageType.CLOSE_RESULT_SET, { + commitImmediateley: true + }); + + // statementSequenceInfo + if (options.statementSequenceInfo) { + segment.add(PartKind.STATEMENT_CONTEXT, [options.statementSequenceInfo]); + } + + // resultSetId + segment.add(PartKind.RESULT_SET_ID, options.resultSetId); + + return segment; +}; \ No newline at end of file diff --git a/lib/protocol/request/Connect.js b/lib/protocol/request/Connect.js new file mode 100644 index 0000000..162b6b8 --- /dev/null +++ b/lib/protocol/request/Connect.js @@ -0,0 +1,44 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var Segment = require('./Segment'); +var data = require('../data'); +var common = require('../common'); +var MessageType = common.MessageType; +var PartKind = common.PartKind; + +module.exports = function connect(method, options) { + var segment = new Segment(MessageType.CONNECT); + + // authentication + segment.add({ + kind: PartKind.AUTHENTICATION, + module: method.Connect + }, options.authentication); + + // clientId + segment.add(PartKind.CLIENT_ID, options.clientId); + + // connectOptions + var connectOptions; + if (options.connectOptions) { + connectOptions = options.connectOptions; + } else { + connectOptions = data.ConnectOptions.getDefaultConnectOptions(); + } + segment.add(PartKind.CONNECT_OPTIONS, connectOptions); + + return segment; +}; \ No newline at end of file diff --git a/lib/protocol/request/Disconnect.js b/lib/protocol/request/Disconnect.js new file mode 100644 index 0000000..21839ab --- /dev/null +++ b/lib/protocol/request/Disconnect.js @@ -0,0 +1,22 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var Segment = require('./Segment'); +var common = require('../common'); +var MessageType = common.MessageType; + +module.exports = function disconnect() { + return new Segment(MessageType.DISCONNECT); +}; \ No newline at end of file diff --git a/lib/protocol/request/DropStatementId.js b/lib/protocol/request/DropStatementId.js new file mode 100644 index 0000000..25e30e1 --- /dev/null +++ b/lib/protocol/request/DropStatementId.js @@ -0,0 +1,35 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var Segment = require('./Segment'); +var common = require('../common'); +var MessageType = common.MessageType; +var PartKind = common.PartKind; + +module.exports = function fetchNext(options) { + var segment = new Segment(MessageType.DROP_STATEMENT_ID, { + commitImmediateley: true + }); + + // statementSequenceInfo + if (options.statementSequenceInfo) { + segment.add(PartKind.STATEMENT_CONTEXT, [options.statementSequenceInfo]); + } + + // statementId + segment.add(PartKind.STATEMENT_ID, options.statementId); + + return segment; +}; \ No newline at end of file diff --git a/lib/protocol/request/Execute.js b/lib/protocol/request/Execute.js new file mode 100644 index 0000000..14f4dc3 --- /dev/null +++ b/lib/protocol/request/Execute.js @@ -0,0 +1,44 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var Segment = require('./Segment'); +var common = require('../common'); +var MessageType = common.MessageType; +var PartKind = common.PartKind; +var CommandOption = common.CommandOption; + +module.exports = function execute(options) { + /* jshint bitwise:false */ + var commitImmediateley = true; + if (typeof options.commitImmediateley === 'boolean') { + commitImmediateley = options.commitImmediateley; + } + var commandOptions = CommandOption.SCROLLABLE_CURSOR_ON | CommandOption.HOLD_CURSORS_OVER_COMMIT; + if (typeof options.commandOptions === 'number') { + commandOptions = options.commandOptions; + } + var segment = new Segment(MessageType.EXECUTE, { + commitImmediateley: commitImmediateley, + commandOptions: commandOptions + }); + + // statementId + segment.add(PartKind.STATEMENT_ID, options.statementId); + + // parameters + segment.add(PartKind.PARAMETERS, options.parameters); + + return segment; +}; \ No newline at end of file diff --git a/lib/protocol/request/ExecuteDirect.js b/lib/protocol/request/ExecuteDirect.js new file mode 100644 index 0000000..f82ee4e --- /dev/null +++ b/lib/protocol/request/ExecuteDirect.js @@ -0,0 +1,40 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var Segment = require('./Segment'); +var common = require('../common'); +var MessageType = common.MessageType; +var PartKind = common.PartKind; +var CommandOption = common.CommandOption; + +module.exports = function executeDirect(options) { + /* jshint bitwise:false */ + var commitImmediateley = true; + if (typeof options.commitImmediateley === 'boolean') { + commitImmediateley = options.commitImmediateley; + } + var commandOptions = CommandOption.SCROLLABLE_CURSOR_ON | CommandOption.HOLD_CURSORS_OVER_COMMIT; + if (typeof options.commandOptions === 'number') { + commandOptions = options.commandOptions; + } + var segment = new Segment(MessageType.EXECUTE_DIRECT, { + commitImmediateley: commitImmediateley, + commandOptions: commandOptions + }); + + // command + segment.add(PartKind.COMMAND, options.command); + return segment; +}; \ No newline at end of file diff --git a/lib/protocol/request/FetchNext.js b/lib/protocol/request/FetchNext.js new file mode 100644 index 0000000..951a38e --- /dev/null +++ b/lib/protocol/request/FetchNext.js @@ -0,0 +1,38 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var Segment = require('./Segment'); +var common = require('../common'); +var MessageType = common.MessageType; +var PartKind = common.PartKind; + +module.exports = function fetchNext(options) { + var segment = new Segment(MessageType.FETCH_NEXT, { + commitImmediateley: true + }); + + // statementSequenceInfo + if (options.statementSequenceInfo) { + segment.add(PartKind.STATEMENT_CONTEXT, [options.statementSequenceInfo]); + } + + // resultSetId + segment.add(PartKind.RESULT_SET_ID, options.resultSetId); + + // fetchSize + segment.add(PartKind.FETCH_SIZE, options.fetchSize); + + return segment; +}; \ No newline at end of file diff --git a/lib/protocol/request/Part.js b/lib/protocol/request/Part.js new file mode 100644 index 0000000..e5372e4 --- /dev/null +++ b/lib/protocol/request/Part.js @@ -0,0 +1,51 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = require('../../util'); +var PartKind = require('../common/PartKind'); + +var PART_HEADER_LENGTH = 16; + +module.exports = Part; + +function Part(options) { + this.kind = options.kind || PartKind.NIL; + this.attributes = options.attributes || 0; + this.argumentCount = options.argumentCount || 0; + this.buffer = undefined; +} + + +Part.prototype.toBuffer = function toBuffer(size) { + var byteLength = util.alignLength(this.buffer.length, 8); + var buffer = new Buffer(PART_HEADER_LENGTH + byteLength); + // Part kind, specifies nature of part data + buffer.writeInt8(this.kind, 0); + // Further attributes of part + buffer.writeInt8(this.attributes, 1); + // Argument count, number of elements in part data. + buffer.writeInt16LE(this.argumentCount, 2); + // Argument count, number of elements in part data (only for some part kinds). + buffer.writeInt32LE(0, 4); + // Length of part buffer in bytes + buffer.writeInt32LE(this.buffer.length, 8); + // Length in packet remaining without this part. + buffer.writeInt32LE(size, 12); + this.buffer.copy(buffer, PART_HEADER_LENGTH); + if (this.buffer.length < byteLength) { + buffer.fill(0x00, PART_HEADER_LENGTH + this.buffer.length); + } + return buffer; +}; \ No newline at end of file diff --git a/lib/protocol/request/Prepare.js b/lib/protocol/request/Prepare.js new file mode 100644 index 0000000..4564b6e --- /dev/null +++ b/lib/protocol/request/Prepare.js @@ -0,0 +1,40 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var Segment = require('./Segment'); +var common = require('../common'); +var MessageType = common.MessageType; +var PartKind = common.PartKind; +var CommandOption = common.CommandOption; + +module.exports = function executeDirect(options) { + /* jshint bitwise:false */ + var commitImmediateley = true; + if (typeof options.commitImmediateley === 'boolean') { + commitImmediateley = options.commitImmediateley; + } + var commandOptions = CommandOption.SCROLLABLE_CURSOR_ON | CommandOption.HOLD_CURSORS_OVER_COMMIT; + if (typeof options.commandOptions === 'number') { + commandOptions = options.commandOptions; + } + var segment = new Segment(MessageType.PREPARE, { + commitImmediateley: commitImmediateley, + commandOptions: commandOptions + }); + + // command + segment.add(PartKind.COMMAND, options.command); + return segment; +}; \ No newline at end of file diff --git a/lib/protocol/request/ReadLob.js b/lib/protocol/request/ReadLob.js new file mode 100644 index 0000000..978ac29 --- /dev/null +++ b/lib/protocol/request/ReadLob.js @@ -0,0 +1,35 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var Segment = require('./Segment'); +var common = require('../common'); +var MessageType = common.MessageType; +var PartKind = common.PartKind; + +module.exports = function fetchNext(options) { + var segment = new Segment(MessageType.READ_LOB, { + commitImmediateley: true + }); + + // statementSequenceInfo + if (options.statementSequenceInfo) { + segment.add(PartKind.STATEMENT_CONTEXT, [options.statementSequenceInfo]); + } + + // readLobRequest + segment.add(PartKind.READ_LOB_REQUEST, options.readLobRequest); + + return segment; +}; \ No newline at end of file diff --git a/lib/protocol/request/Segment.js b/lib/protocol/request/Segment.js new file mode 100644 index 0000000..59d14ed --- /dev/null +++ b/lib/protocol/request/Segment.js @@ -0,0 +1,93 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = require('../../util'); +var data = require('../data'); +var common = require('../common'); +var Part = require('./Part'); +var MessageType = common.MessageType; +var SegmentKind = common.SegmentKind; + +var MAX_PACKET_SIZE = common.MAX_PACKET_SIZE; +var PACKET_HEADER_LENGTH = common.PACKET_HEADER_LENGTH; +var SEGMENT_HEADER_LENGTH = common.SEGMENT_HEADER_LENGTH; + +module.exports = Segment; + +function Segment(type, options) { + options = options || {}; + this.type = type || MessageType.NIL; + this.commitImmediateley = !! options.commitImmediateley ? 1 : 0; + this.commandOptions = options.commandOptions || 0; + this.parts = []; +} + +Segment.prototype.addPart = function addPart(part) { + this.parts.push(part); + return part; +}; + +Segment.prototype.add = function add(kind, options) { + if (options) { + var dataModule; + if (util.isNumber(kind)) { + dataModule = data[kind]; + } else { + dataModule = kind.module; + kind = kind.kind; + } + var part = new Part({ + kind: kind + }); + part.argumentCount = dataModule.getArgumentCount(options); + dataModule.write(part, options); + this.parts.push(part); + } +}; + +Segment.prototype.toBuffer = function toBuffer(size) { + size = size || (MAX_PACKET_SIZE - PACKET_HEADER_LENGTH); + var remainingSize = size - SEGMENT_HEADER_LENGTH; + var length = SEGMENT_HEADER_LENGTH; + var buffers = this.parts.map(function (part) { + var buffer = part.toBuffer(remainingSize); + remainingSize -= buffer.length; + length += buffer.length; + return buffer; + }); + + var header = new Buffer(SEGMENT_HEADER_LENGTH); + // Length of the segment, including the header + header.writeInt32LE(length, 0); + // Offset of the segment within the message buffer + header.writeInt32LE(0, 4); + // Number of contained parts + header.writeInt16LE(this.parts.length, 8); + // Number of segment within packet + header.writeInt16LE(1, 10); + // Segment kind + header.writeInt8(SegmentKind.REQUEST, 12); + // Message type + header.writeInt8(this.type, 13); + // Whether the command shall be committed + header.writeInt8(this.commitImmediateley, 14); + // Command options + header.writeInt8(this.commandOptions, 15); + // Filler + header.fill(0x00, 16, SEGMENT_HEADER_LENGTH); + + buffers.unshift(header); + return Buffer.concat(buffers, length); +}; \ No newline at end of file diff --git a/lib/protocol/request/index.js b/lib/protocol/request/index.js new file mode 100644 index 0000000..02c3a4a --- /dev/null +++ b/lib/protocol/request/index.js @@ -0,0 +1,27 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +exports.Segment = require('./Segment'); +exports.Part = require('./Part'); +exports.authenticate = require('./Authenticate'); +exports.connect = require('./Connect'); +exports.disconnect = require('./Disconnect'); +exports.executeDirect = require('./ExecuteDirect'); +exports.prepare = require('./Prepare'); +exports.execute = require('./Execute'); +exports.fetchNext = require('./FetchNext'); +exports.closeResultSet = require('./CloseResultSet'); +exports.dropStatementId = require('./DropStatementId'); +exports.readLob = require('./ReadLob'); \ No newline at end of file diff --git a/lib/util/Queue.js b/lib/util/Queue.js new file mode 100644 index 0000000..0f884ac --- /dev/null +++ b/lib/util/Queue.js @@ -0,0 +1,115 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = require('util'); +var EventEmitter = require('events').EventEmitter; + +module.exports = Queue; + +util.inherits(Queue, EventEmitter); + +function Queue(immediate) { + EventEmitter.call(this); + + this.queue = []; + this.busy = false; + this.running = !! immediate; +} + +Object.defineProperty(Queue.prototype, 'empty', { + get: function isEmpty() { + return this.queue.length === 0; + } +}); + +Queue.prototype.unshift = function unshift(task) { + this.queue.unshift(task); + if (this.running) { + run.call(this); + } + return this; +}; + +Queue.prototype.push = function push(task) { + this.queue.push(task); + if (this.running) { + run.call(this); + } + return this; +}; + +Queue.prototype.resume = function resume() { + this.running = true; + if (this.queue.length) { + run.call(this); + } + return this; +}; + +Queue.prototype.pause = function pause() { + this.running = false; + return this; +}; + +Queue.prototype.abort = function abort() { + this.queue = []; + this.busy = false; + this.running = false; + this.removeAllListeners(); + return this; +}; + +Queue.prototype.createTask = function createTask(send, receive, name) { + return new Task(send, receive, name); +} + +function run() { + /* jshint validthis:true */ + if (this.running && !this.busy) { + // Queue is running and not busy + this.busy = true; + var task = this.queue.shift(); + process.nextTick(task.run.bind(task, next.bind(this))); + } +} + +function next(err, name) { + /* jshint validthis:true */ + this.busy = false; + if (err instanceof Error) { + this.pause(); + this.emit('error', err); + } + if (this.queue.length) { + run.call(this); + } else { + this.emit('drain'); + } +} + +function Task(send, receive, name) { + this.send = send; + this.receive = receive; + this.name = name; +} + +Task.prototype.run = function run(next) { + function receive() { + /* jshint validthis:true */ + this.receive.apply(null, arguments); + next(null, this.name); + } + this.send(receive.bind(this)); +}; \ No newline at end of file diff --git a/lib/util/bignum.js b/lib/util/bignum.js new file mode 100644 index 0000000..a0bfb6c --- /dev/null +++ b/lib/util/bignum.js @@ -0,0 +1,532 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; +/* jshint bitwise:false */ + +var INT_10_1 = Math.pow(10, 1); +var INT_10_2 = Math.pow(10, 2); + +var INT_10_3 = Math.pow(10, 3); +var INT_10_4 = Math.pow(10, 4); +var INT_10_5 = Math.pow(10, 5); +var INT_10_6 = Math.pow(10, 6); +var INT_10_7 = Math.pow(10, 7); +var INT_10_8 = Math.pow(10, 8); +var INT_10_9 = Math.pow(10, 9); +var INT_10_10 = Math.pow(10, 10); +var INT_10_11 = Math.pow(10, 11); +var INT_10_12 = Math.pow(10, 12); +var INT_10_13 = Math.pow(10, 13); +var INT_10_14 = Math.pow(10, 14); + +var ZERO_1 = '0'; +var ZERO_2 = '00'; +var ZERO_3 = '000'; +var ZERO_4 = '0000'; +var ZERO_5 = '00000'; +var ZERO_6 = '000000'; +var ZERO_7 = '0000000'; +var ZERO_8 = '00000000'; +var ZERO_9 = '000000000'; +var ZERO_10 = '0000000000'; +var ZERO_11 = '00000000000'; +var ZERO_12 = '000000000000'; +var ZERO_13 = '0000000000000'; + +var BASE = INT_10_7; + +var EXP_BIAS = 6176; + +var INT_2_16 = Math.pow(2, 16); +var INT_2_21 = Math.pow(2, 21); +var INT_2_32 = Math.pow(2, 32); + +/* 2^32 = 429 4967296 */ +var INT_2_32_0 = 4967296; +var INT_2_32_1 = 429; + +/* 2^53 = 90 0719925 4740992 */ +var INT_2_53_0 = 4740992; +var INT_2_53_1 = 719925; +var INT_2_53_2 = 90; + +/* 2^64 = 184467 4407370 9551616 */ +var INT_2_64_0 = 9551616; +var INT_2_64_1 = 4407370; +var INT_2_64_2 = 184467; + +/* 10^7 for base 2^16 */ +var BIN_10_7_0 = INT_10_7 % INT_2_16; +var BIN_10_7_1 = Math.floor(INT_10_7 / INT_2_16); + +/* 10^14 for base 2^16 */ +var BIN_10_14_0 = INT_10_14 % INT_2_16; +var BIN_10_14_1 = Math.floor(INT_10_14 / INT_2_16) % INT_2_16; +var BIN_10_14_2 = Math.floor(Math.floor(INT_10_14 / INT_2_16) / INT_2_16); + +/* Decimal zero padding */ +var MAX_DECIMAL_LENGTH = 34; +var ZEROS = ['']; +for (var i = 1; i < MAX_DECIMAL_LENGTH; i++) { + ZEROS.push(ZEROS[i - 1] + '0'); +} + +function lpad14(number) { + /* jshint curly: false */ + if (number >= INT_10_13) return number; + if (number >= INT_10_12) return ZERO_1 + number; + if (number >= INT_10_11) return ZERO_2 + number; + if (number >= INT_10_10) return ZERO_3 + number; + if (number >= INT_10_9) return ZERO_4 + number; + if (number >= INT_10_8) return ZERO_5 + number; + if (number >= INT_10_7) return ZERO_6 + number; + if (number >= INT_10_6) return ZERO_7 + number; + if (number >= INT_10_5) return ZERO_8 + number; + if (number >= INT_10_4) return ZERO_9 + number; + if (number >= INT_10_3) return ZERO_10 + number; + if (number >= INT_10_2) return ZERO_11 + number; + if (number >= INT_10_1) return ZERO_12 + number; + return ZERO_13 + number; +} + +function lpad7(number) { + /* jshint curly: false */ + if (number >= INT_10_6) return number; + if (number >= INT_10_5) return ZERO_1 + number; + if (number >= INT_10_4) return ZERO_2 + number; + if (number >= INT_10_3) return ZERO_3 + number; + if (number >= INT_10_2) return ZERO_4 + number; + if (number >= INT_10_1) return ZERO_5 + number; + return ZERO_6 + number; +} + +function lpad4(number) { + /* jshint curly: false */ + if (number >= INT_10_3) return number; + if (number >= INT_10_2) return ZERO_1 + number; + if (number >= INT_10_1) return ZERO_2 + number; + return ZERO_3 + number; +} + +function lpad2(number) { + /* jshint curly: false */ + if (number >= INT_10_1) return number; + return ZERO_1 + number; +} + +function _readInt64(buffer, offset, unsigned) { + + var x, y, s, y0, y1, x0, x1, x2; + + x = buffer[offset + 2] << 16; + x |= buffer[offset + 1] << 8; + x |= buffer[offset]; + x += buffer[offset + 3] << 24 >>> 0; + offset += 4; + + y = buffer[offset + 2] << 16; + y |= buffer[offset + 1] << 8; + y |= buffer[offset]; + y += buffer[offset + 3] << 24 >>> 0; + + if (!unsigned && (y & 0x80000000)) { + y = 0xffffffff - y; + if (x === 0) { + y += 1; + } else { + x = 0xffffffff - x + 1; + } + s = -1; + } else { + s = 1; + } + + if (y === 0) { + return s * x; + } + if (y < INT_2_21 || (y === INT_2_21 && x === 0)) { + return s * (y * INT_2_32 + x); + } + + if (x < BASE) { + x0 = x % BASE; + x1 = 0; + } else { + x0 = x % BASE; + x1 = Math.floor(x / BASE); + } + + if (y < BASE) { + y0 = y % BASE; + y1 = 0; + } else { + y0 = y % BASE; + y1 = Math.floor(y / BASE); + } + + x0 += y0 * INT_2_32_0; + x1 += y0 * INT_2_32_1 + y1 * INT_2_32_0; + x2 = y1 * INT_2_32_1; + + if (x0 >= BASE) { + x1 += Math.floor(x0 / BASE); + x0 %= BASE; + } + if (x1 >= BASE) { + x2 += Math.floor(x1 / BASE); + x1 %= BASE; + } + + if (s === 1) { + return '' + x2 + lpad14(x1 * BASE + x0); + } + return '-' + x2 + lpad14(x1 * BASE + x0); + +} + +function readDec128(buffer, offset) { + + var i, j, k, l, z0, z1, y0, y1, y2, x0, x1, x2, x3, x4; + + offset = offset || 0; + + if ((buffer[offset + 15] & 0x70) === 0x70) { + return null; + } + + i = buffer[offset + 2] << 16; + i |= buffer[offset + 1] << 8; + i |= buffer[offset]; + i += buffer[offset + 3] << 24 >>> 0; + offset += 4; + + j = buffer[offset + 2] << 16; + j |= buffer[offset + 1] << 8; + j |= buffer[offset]; + j += buffer[offset + 3] << 24 >>> 0; + offset += 4; + + k = buffer[offset + 2] << 16; + k |= buffer[offset + 1] << 8; + k |= buffer[offset]; + k += buffer[offset + 3] << 24 >>> 0; + offset += 4; + + l = (buffer[offset + 2] & 0x01) << 16; + l |= buffer[offset + 1] << 8; + l |= buffer[offset]; + offset += 2; + + var dec = { + s: (buffer[offset + 1] & 0x80) ? -1 : 1, + m: undefined, + e: ((((buffer[offset + 1] << 8) | buffer[offset]) & 0x7ffe) >> 1) - EXP_BIAS + }; + + if (k === 0 && l === 0) { + if (j === 0) { + dec.m = i; + return dec; + } + if (j < INT_2_21 || (j === INT_2_21 && i === 0)) { + dec.m = j * INT_2_32 + i; + return dec; + } + } + + if (i < BASE) { + x0 = i; + x1 = 0; + } else { + x0 = i % BASE; + x1 = Math.floor(i / BASE); + } + + if (j < BASE) { + x0 += j * INT_2_32_0; + x1 += j * INT_2_32_1; + x2 = 0; + } else { + z0 = j % BASE; + z1 = Math.floor(j / BASE); + x0 += z0 * INT_2_32_0; + x1 += z0 * INT_2_32_1 + z1 * INT_2_32_0; + x2 = z1 * INT_2_32_1; + } + + if (k < BASE) { + y0 = k; + y1 = 0; + } else { + y0 = k % BASE; + y1 = Math.floor(k / BASE); + } + + if (l < BASE) { + y0 += l * INT_2_32_0; + y1 += l * INT_2_32_1; + y2 = 0; + } else { + z0 = l % BASE; + z1 = Math.floor(l / BASE); + y0 += z0 * INT_2_32_0; + y1 += z0 * INT_2_32_1 + z1 * INT_2_32_0; + y2 = z1 * INT_2_32_1; + } + + if (y0 >= BASE) { + y1 += Math.floor(y0 / BASE); + y0 %= BASE; + } + if (y1 >= BASE) { + y2 += Math.floor(y1 / BASE); + y1 %= BASE; + } + + x0 += y0 * INT_2_64_0; + x1 += y0 * INT_2_64_1 + y1 * INT_2_64_0; + x2 += y0 * INT_2_64_2 + y1 * INT_2_64_1 + y2 * INT_2_64_0; + x3 = y1 * INT_2_64_2 + y2 * INT_2_64_1; + x4 = y2 * INT_2_64_2; + + if (x0 >= BASE) { + x1 += Math.floor(x0 / BASE); + x0 %= BASE; + } + if (x1 >= BASE) { + x2 += Math.floor(x1 / BASE); + x1 %= BASE; + } + if (x2 >= BASE) { + x3 += Math.floor(x2 / BASE); + x2 %= BASE; + } + if (x3 >= BASE) { + x4 += Math.floor(x3 / BASE); + x3 %= BASE; + } + + if (x4) { + dec.m = '' + x4 + lpad14(x3 * BASE + x2) + lpad14(x1 * BASE + x0); + return dec; + } + if (x3) { + dec.m = '' + (x3 * BASE + x2) + lpad14(x1 * BASE + x0); + return dec; + } + if (x2) { + if (x2 < INT_2_53_2 || (x2 === INT_2_53_2 && (x1 < INT_2_53_1 || (x1 === + INT_2_53_1 && x0 <= INT_2_53_0)))) { + dec.m = (x2 * BASE + x1) * BASE + x0; + return dec; + } + dec.m = '' + x2 + lpad14(x1 * BASE + x0); + return dec; + } + dec.m = x1 * BASE + x0; + return dec; + +} + +function readDecFloat(buffer, offset) { + var value = readDec128(buffer, offset); + if (value.s === -1) { + return '-' + value.m + 'e' + value.e; + } + return value.m + 'e' + value.e; +} + +function readDecFixed(buffer, offset, frac) { + var value = readDec128(buffer, offset); + var d = value.m; + if (typeof d === 'number') { + d = '' + d; + } + if (value.e < 0) { + frac += value.e; + var i = d.length + value.e; + if (i > 0) { + if (frac < 0) { + d = d.substring(0, i) + '.' + d.substring(i, d.length + frac); + } else { + d = d.substring(0, i) + '.' + d.substring(i); + } + } else if (i < 0) { + d = '0.' + ZEROS[-i] + d; + } else { + d = '0.' + d; + } + } else if (value.e > 0) { + d = value.m + ZEROS[value.e]; + } + if (frac > 0) { + d += ZEROS[frac]; + } + if (value.s === -1) { + return '-' + d; + } + return d; +} + +function _writeInt64(buffer, value, offset, unsigned) { + + var l, x, a, b, c, x0, x1, y0, y1, z0, z1, z2, z3; + + var negative = false; + + if (typeof value === 'string') { + + l = value.length; + + if (l > 15) { + + a = +value.substring(l - 7); + b = +value.substring(l - 7, l - 14); + c = +value.substring(0, l - 14); + + if (!unsigned && c < 0) { + c *= -1; + negative = true; + } + + z0 = a % INT_2_16; + z1 = Math.floor(a / INT_2_16); + z2 = z3 = 0; + + y0 = b % INT_2_16; + y1 = Math.floor(b / INT_2_16); + if (y0 >= INT_2_16) { + y1 += Math.floor(y0 / INT_2_16); + y0 %= INT_2_16; + } + + z0 += y0 * BIN_10_7_0; + z1 += y0 * BIN_10_7_1 + y1 * BIN_10_7_0; + z2 += y1 * BIN_10_7_1; + + y0 = c % INT_2_16; + y1 = Math.floor(c / INT_2_16); + if (y0 >= INT_2_16) { + y1 += Math.floor(y0 / INT_2_16); + y0 %= INT_2_16; + } + + z0 += y0 * BIN_10_14_0; + z1 += y0 * BIN_10_14_1 + y1 * BIN_10_14_0; + z2 += y0 * BIN_10_14_2 + y1 * BIN_10_14_1; + z3 += y1 * BIN_10_14_2; + + if (z0 >= INT_2_16) { + z1 += Math.floor(z0 / INT_2_16); + z0 %= INT_2_16; + } + if (z1 >= INT_2_16) { + z2 += Math.floor(z1 / INT_2_16); + z1 %= INT_2_16; + } + if (z2 >= INT_2_16) { + z3 += Math.floor(z2 / INT_2_16); + z2 %= INT_2_16; + } + + x0 = z1 * INT_2_16 + z0; + x1 = z3 * INT_2_16 + z2; + + } else { + x = +value; + + if (!unsigned && x < 0) { + x *= -1; + negative = true; + } + + x0 = x % INT_2_32; + x1 = Math.floor(x / INT_2_32); + } + } else { + x = value; + + if (!unsigned && x < 0) { + x *= -1; + negative = true; + } + + x0 = x % INT_2_32; + x1 = Math.floor(x / INT_2_32); + } + + if (negative) { + x1 = 0xffffffff - x1; + if (x0 === 0) { + x1 += 1; + } else { + x0 = 0xffffffff - x0 + 1; + } + } + + buffer[offset + 3] = (x0 >>> 24) & 0xff; + buffer[offset + 2] = (x0 >>> 16) & 0xff; + buffer[offset + 1] = (x0 >>> 8) & 0xff; + buffer[offset] = x0 & 0xff; + offset += 4; + + buffer[offset + 3] = (x1 >>> 24) & 0xff; + buffer[offset + 2] = (x1 >>> 16) & 0xff; + buffer[offset + 1] = (x1 >>> 8) & 0xff; + buffer[offset] = x1 & 0xff; +} + +function readInt64(buffer, offset) { + return _readInt64(buffer, offset || 0, false); +} + +function readUInt64(buffer, offset) { + return _readInt64(buffer, offset || 0, true); +} + +function writeInt64(buffer, value, offset) { + _writeInt64(buffer, value, offset || 0, false); +} + +function writeUInt64(buffer, value, offset) { + _writeInt64(buffer, value, offset || 0, true); +} + +Buffer.prototype.readInt64LE = function readInt64LE(offset) { + return _readInt64(this, offset || 0, false); +}; + +Buffer.prototype.readUInt64LE = function readUInt64LE(offset) { + return _readInt64(this, offset || 0, true); +}; + +Buffer.prototype.writeInt64LE = function writeInt64LE(value, offset) { + _writeInt64(this, value, offset || 0, false); +}; + +Buffer.prototype.writeUInt64LE = function writeUInt64LE(value, offset) { + _writeInt64(this, value, offset || 0, true); +}; + +exports.readInt64LE = readInt64; +exports.readUInt64LE = readUInt64; +exports.writeInt64LE = writeInt64; +exports.writeUInt64LE = writeUInt64; +exports.readDec128 = readDec128; +exports.readDecFloat = readDecFloat; +exports.readDecFixed = readDecFixed; +exports.lpad2 = lpad2; +exports.lpad4 = lpad4; +exports.lpad7 = lpad7; +exports.lpad14 = lpad14; \ No newline at end of file diff --git a/lib/util/index.js b/lib/util/index.js new file mode 100644 index 0000000..de44e74 --- /dev/null +++ b/lib/util/index.js @@ -0,0 +1,110 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var util = require('util'); + +[ + 'format', + 'debug', + 'error', + 'puts', + 'print', + 'log', + 'inspect', + 'isArray', + 'isRegExp', + 'isDate', + 'isError', + 'inherits' +].forEach(function exportNativeUtils(fn) { + exports[fn] = util[fn]; +}); + +exports.bignum = require('./bignum'); + +exports.Queue = require('./Queue'); + +function extend(obj) { + Array.prototype.slice.call(arguments, 1).forEach(function extendOnce(source) { + /* jshint forin:false */ + if (source) { + for (var prop in source) { + obj[prop] = source[prop]; + } + } + }); + return obj; +} +exports.extend = extend; + +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; + +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; + +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; + +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; + +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; + +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; + +function isBuffer(arg) { + return arg instanceof Buffer; +} +exports.isBuffer = isBuffer; + +function alignLength(length, alignment) { + if (length % alignment === 0) { + return length; + } + return length + alignment - length % alignment; +} +exports.alignLength = alignLength; + +function cc2_(str) { + return str.replace(/([a-z])([A-Z])/g, '$1_$2').toUpperCase(); +} +exports.cc2_ = cc2_; + +function _2cc(str) { + return str.toLowerCase().replace(/_([a-z])/g, function toUpperCase(match, str) { + return str.toUpperCase(); + }); +} +exports._2cc = _2cc; \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..e6d70d0 --- /dev/null +++ b/package.json @@ -0,0 +1,38 @@ +{ + "author": { + "name": "Holger Koser", + "email": "holger.koser@sap.com" + }, + "name": "hdb", + "description": "SAP HANA Database Client for Node", + "version": "0.0.5", + "repository": { + "type": "git", + "url": "git://github.com/sap/node-hdb" + }, + "licenses": [{ + "type": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }], + "keywords": [ + "sap", + "hana", + "database", + "in-memory" + ], + "main": "./index.js", + "scripts": { + "test": "make test" + }, + "engines": { + "node": ">= 0.10.x" + }, + "dependencies": {}, + "devDependencies": { + "should": "~1.2.2", + "mocha": "~1.9.0", + "async": "~0.2.6", + "generic-pool": "~2.0.3" + }, + "optionalDependencies": {} +} \ No newline at end of file diff --git a/test/acceptance/hdb.Dummy.js b/test/acceptance/hdb.Dummy.js new file mode 100644 index 0000000..f0c5b88 --- /dev/null +++ b/test/acceptance/hdb.Dummy.js @@ -0,0 +1,44 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; +/* jshint undef:false */ + +var db = require('../lib').createDatabase(); + +describe('Database', function () { + before(db.connect.bind(db)); + after(db.disconnect.bind(db)); + var client = db.client; + + describe('Table DUMMY', function () { + describe('#Query', function () { + + it('should return a single dummy row', function (done) { + var sql = 'select * from DUMMY'; + client.exec(sql, function (err, rows) { + if (err) { + return done(err); + } + rows.should.have.length(1); + rows[0].should.eql({ + DUMMY: 'X' + }); + done(); + }); + }); + + }); + }); + +}); \ No newline at end of file diff --git a/test/acceptance/hdb.Prepare.js b/test/acceptance/hdb.Prepare.js new file mode 100644 index 0000000..508eaed --- /dev/null +++ b/test/acceptance/hdb.Prepare.js @@ -0,0 +1,129 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; +/* jshint undef:false, expr:true */ + +var async = require('async'); +var Statement = require('../lib').Statement; +var db = require('../lib').createDatabase(); + +describe('Database', function () { + before(db.connect.bind(db)); + after(db.disconnect.bind(db)); + var client = db.client; + + describe('Table NUMBERS', function () { + before(db.createNumbers.bind(db, [0, 25])); + after(db.dropNumbers.bind(db)); + + describe('#PrepareStatement', function () { + + it('should create and execute a prepared statement', function (done) { + var sql = 'select * from NUMBERS where B like ? order by A'; + var statement; + async.series([ + function prepareStatement(callback) { + client.prepare(sql, function onprepare(err, ps) { + statement = ps; + statement.should.be.instanceof(Statement); + var metadata = statement.parameterMetadata; + metadata.should.have.length(1); + var p = metadata[0]; + p.should.have.property('mode', 2); + p.should.have.property('dataType', 9); + p.should.have.property('ioType', 1); + callback(null, statement) + }); + }, + function endsWithTeen(callback) { + statement.exec(['%teen'], function (err, rows) { + if (err) { + return callback(err); + } + rows.should.have.length(7); + callback(); + }); + }, + function endsWithOne(callback) { + statement.exec(['%one'], function (err, rows) { + if (err) { + return callback(err); + } + rows.should.have.length(2); + callback(); + }); + }, + function dropStatement(callback) { + statement.drop(callback); + } + ], done); + }); + + }); + + + describe('#ProcedureWithResult', function () { + before(db.createReadNumbersBetween.bind(db)); + after(db.dropReadNumbersBetween.bind(db)); + + it('should read the numbers between 3 and 5', function (done) { + var sql = 'call READ_NUMBERS_BETWEEN (?, ?, ?)'; + var statement; + async.series([ + function prepareStatement(callback) { + client.prepare(sql, function (err, ps) { + statement = ps; + var metadata = statement.parameterMetadata; + metadata.should.have.length(2); + var p1 = metadata[0]; + p1.should.have.property('mode', 2); + p1.should.have.property('dataType', 3); + p1.should.have.property('ioType', 1); + var p2 = metadata[0]; + p2.should.have.property('mode', 2); + p2.should.have.property('dataType', 3); + p2.should.have.property('ioType', 1); + callback(); + }); + }, + function readNumbersBetween3and5(callback) { + statement.exec([3, 5], function (err, parameters, rows) { + if (err) { + return callback(err); + } + Object.keys(parameters).should.have.length(0); + arguments.should.have.length(3); + rows.should.have.length(3) + .and.eql(db.numbers.slice(3, 6)); + callback(); + }); + }, + function readNumbersBetween8and7(callback) { + statement.exec([8, 7], function (err, parameters, rows) { + if (err) { + return callback(err); + } + rows.should.be.empty + callback(); + }); + }, + function dropStatement(callback) { + statement.drop(callback); + } + ], done); + + }); + }); + }); +}); \ No newline at end of file diff --git a/test/acceptance/hdb.Query.js b/test/acceptance/hdb.Query.js new file mode 100644 index 0000000..def0d14 --- /dev/null +++ b/test/acceptance/hdb.Query.js @@ -0,0 +1,138 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; +/* jshint undef:false, expr:true */ + +var Readable = require('stream').Readable; +var Writable = require('stream').Writable; +var ResultSet = require('../lib').ResultSet; +var db = require('../lib').createDatabase(); + +describe('Database', function () { + before(db.connect.bind(db)); + after(db.disconnect.bind(db)); + var client = db.client; + + describe('Table NUMBERS', function () { + before(db.createNumbers.bind(db, [0, 100])); + after(db.dropNumbers.bind(db)); + + describe('#Execute', function () { + + it('should return all numbers via callback', function (done) { + var sql = 'select * from NUMBERS order by A'; + client.exec(sql, function (err, rows) { + if (err) { + return done(err); + } + rows.should + .have.length(db.numbers.length) + .and.eql(db.numbers); + done(); + }); + }); + + it('should return all numbers via fetch', function (done) { + var sql = 'select * from NUMBERS order by A'; + client.exec(sql, false, function (err, rs) { + if (err) { + return done(err); + } + rs.should.be.an.instanceof(ResultSet); + rs.fetch(function onfetch(err, rows) { + rows.should + .have.length(db.numbers.length) + .and.eql(db.numbers); + rs.closed.should.be.true; + done(); + }); + }); + }); + + it('should return all numbers via createReadStream', function (done) { + var sql = 'select * from NUMBERS order by A'; + client.exec(sql, false, function onexec(err, rs) { + if (err) { + return done(err); + } + rs.should.be.an.instanceof(ResultSet); + var readable = rs.createReadStream(); + readable.should.be.an.instanceof(Readable); + var rows = []; + readable.once('error', function onerror() { + done(err); + }).on('readable', function onreadable() { + rows = rows.concat(this.read()); + }).once('end', function onend() { + rows.should + .have.length(db.numbers.length) + .and.eql(db.numbers); + done(); + }); + }); + }); + + it('should return all numbers via pipe', function (done) { + var sql = 'select * from NUMBERS order by A'; + client.exec(sql, false, function onexec(err, rs) { + if (err) { + return done(err); + } + rs.should.be.an.instanceof(ResultSet); + var readable = rs.createReadStream(); + readable.should.be.an.instanceof(Readable); + var rows = []; + var writable = new Writable({ + objectMode: true + }); + writable._write = function (chunk, encoding, callback) { + rows = rows.concat(chunk); + callback(); + }; + writable.once('finish', function onfinish() { + rows.should + .have.length(db.numbers.length) + .and.eql(db.numbers); + done(); + }) + readable.once('error', function onreadable() { + done(err); + }).pipe(writable); + }); + }); + }); + + describe('#ProcedureWithResult', function () { + before(db.createReadNumbersBetween.bind(db)); + after(db.dropReadNumbersBetween.bind(db)); + + it('should read the numbers between 3 and 5', function (done) { + var sql = + 'select * from READ_NUMBERS_BETWEEN_VIEW with parameters (' + + '\'placeholder\' = (\'$$a$$\', \'3\'),' + + '\'placeholder\' = (\'$$b$$\', \'5\'))'; + client.exec(sql, function (err, rows) { + if (err) { + return done(err); + } + rows.should + .have.length(3) + .and.eql(db.numbers.slice(3, 6)); + done(); + }); + }); + }); + + }); +}); \ No newline at end of file diff --git a/test/auth.scramsha256.js b/test/auth.scramsha256.js new file mode 100644 index 0000000..6284785 --- /dev/null +++ b/test/auth.scramsha256.js @@ -0,0 +1,73 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var lib = require('./lib'); +var scramsha256 = lib.auth.SCRAMSHA256; +var Authentication = scramsha256.Authentication; +var PartKind = lib.common.PartKind; +var Fields = lib.data[PartKind.AUTHENTICATION]; + +describe('Authentication', function () { + + describe('#SCRAMSHA256', function () { + + var reqOptions = { + user: 'SYSTEM', + algorithm: 'SCRAMSHA256', + clientChallenge: new Buffer([0x01, 0x02, 0x03, 0x04]) + }; + var reqPart = { + argumentCount: 1, + buffer: new Buffer([ + 0x03, 0x00, + 0x06, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, + 0x0b, 0x53, 0x43, 0x52, 0x41, 0x4d, 0x53, 0x48, + 0x41, 0x32, 0x35, 0x36, + 0x04, 0x01, 0x02, 0x03, 0x04 + ]) + }; + + var replyOptions = { + algorithm: 'SCRAMSHA256', + salt: new Buffer([ + 0x80, 0x96, 0x4f, 0xa8, 0x54, 0x28, 0xae, 0x3a, + 0x81, 0xac, 0xd3, 0xe6, 0x86, 0xa2, 0x79, 0x33 + ]), + serverChallenge: new Buffer([ + 0x41, 0x06, 0x51, 0x50, 0x11, 0x7e, 0x45, 0x5f, + 0xec, 0x2f, 0x03, 0xf6, 0xf4, 0x7c, 0x19, 0xd4, + 0x05, 0xad, 0xe5, 0x0d, 0xd6, 0x57, 0x31, 0xdc, + 0x0f, 0xb3, 0xf7, 0x95, 0x4d, 0xb6, 0x2c, 0x8a, + 0xa6, 0x7a, 0x7e, 0x82, 0x5e, 0x13, 0x00, 0xbe, + 0xe9, 0x75, 0xe7, 0x45, 0x18, 0x23, 0x8c, 0x9a + ]) + }; + + var replyPart = Fields.write({}, [ + replyOptions.algorithm, [replyOptions.salt, replyOptions.serverChallenge] + ]); + it('should write an authentication request', function () { + var part = Authentication.write({}, reqOptions); + part.should.eql(reqPart); + }); + + it('should read an authentication reply', function () { + var options = Authentication.read(replyPart); + options.should.eql(replyOptions); + }); + + }); + +}); \ No newline at end of file diff --git a/test/data.Options.js b/test/data.Options.js new file mode 100644 index 0000000..d111e7c --- /dev/null +++ b/test/data.Options.js @@ -0,0 +1,94 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var lib = require('../lib'); +var PartKind = lib.common.PartKind; +var Options = lib.data[PartKind.CONNECT_OPTIONS]; + +describe('Data', function () { + + var optsPart = { + argumentCount: 10, + buffer: new Buffer([ + 0x01, 0x03, 0x67, 0x15, 0x03, 0x00, + 0x0b, 0x1d, 0x03, 0x00, 0x58, 0x53, 0x45, + 0x0c, 0x03, 0x01, 0x00, 0x00, 0x00, + 0x17, 0x03, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x03, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x1c, 0x01, + 0x0f, 0x03, 0x02, 0x00, 0x00, 0x00, + 0x13, 0x1c, 0x01, + 0x14, 0x1c, 0x01, + 0x15, 0x03, 0x01, 0x00, 0x00, 0x00 + ]) + }; + + var opts = [{ + name: 1, + type: 3, + value: 202087 + }, { + name: 11, + type: 29, + value: 'XSE' + }, { + name: 12, + type: 3, + value: 1 + }, { + name: 23, + type: 3, + value: 1 + }, { + name: 16, + type: 3, + value: 3 + }, { + name: 2, + type: 28, + value: true + }, { + name: 15, + type: 3, + value: 2 + }, { + name: 19, + type: 28, + value: true + }, { + name: 20, + type: 28, + value: true + }, { + name: 21, + type: 3, + value: 1 + }]; + + describe('#Options', function () { + + it('should write an Options part', function () { + var part = Options.write({}, opts); + part.should.eql(optsPart); + }); + + it('should read an Options part', function () { + var options = Options.read(optsPart); + options.should.eql(opts); + }); + + }); + +}); \ No newline at end of file diff --git a/test/data.ParameterMetadata.js b/test/data.ParameterMetadata.js new file mode 100644 index 0000000..835b9e6 --- /dev/null +++ b/test/data.ParameterMetadata.js @@ -0,0 +1,73 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var lib = require('../lib'); +var PartKind = lib.common.PartKind; +var ParameterMetadata = lib.data[PartKind.PARAMETER_METADATA]; + +describe('Data', function () { + + var paramsPart = { + argumentCount: 4, + buffer: new Buffer( + '020b01000000000000010000500a0000' + + '020b01000b0000000001000000000000' + + '020b01001b0000000001000000000000' + + '02030400240000000a00000000004d7f' + + '0a504152454e5455554944' + + '0f5345415243485f4352495445524941' + + '085355425f54595045' + + '0a4552524f525f434f4445', 'hex') + }; + var paramsMetadata = [{ + mode: 2, + dataType: 11, + ioType: 1, + length: 256, + fraction: 0, + name: 'PARENTUUID' + }, { + mode: 2, + dataType: 11, + ioType: 1, + length: 256, + fraction: 0, + name: 'SEARCH_CRITERIA' + }, { + mode: 2, + dataType: 11, + ioType: 1, + length: 256, + fraction: 0, + name: 'SUB_TYPE' + }, { + mode: 2, + dataType: 3, + ioType: 4, + length: 10, + fraction: 0, + name: 'ERROR_CODE' + }]; + + describe('#ParameterMetadata', function () { + + it('shoul read parameter metadata', function () { + var metadata = ParameterMetadata.read(paramsPart); + metadata.should.eql(paramsMetadata); + }); + + }); + +}); \ No newline at end of file diff --git a/test/data.Parameters.js b/test/data.Parameters.js new file mode 100644 index 0000000..e153cb6 --- /dev/null +++ b/test/data.Parameters.js @@ -0,0 +1,33 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var lib = require('../lib'); +var PartKind = lib.common.PartKind; +var Parameters = lib.data[PartKind.PARAMETERS]; + +var data = require('./fixtures/parametersData').DEFAULT; + +describe('Data', function () { + + describe('#Parameters', function () { + + it('should write parameters', function () { + var params = Parameters.write({}, data.values); + params.should.eql(data.part); + }); + + }); + +}); \ No newline at end of file diff --git a/test/data.ReadLob.js b/test/data.ReadLob.js new file mode 100644 index 0000000..2a94a5a --- /dev/null +++ b/test/data.ReadLob.js @@ -0,0 +1,67 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var lib = require('../lib'); +var PartKind = lib.common.PartKind; +var ReadLobReply = lib.data[PartKind.READ_LOB_REPLY]; +var ReadLobRequest = lib.data[PartKind.READ_LOB_REQUEST]; + +describe('Data', function () { + + var reqPart = { + argumentCount: 1, + buffer: new Buffer([ + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x18, 0x03, 0x00, + 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x0d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 + ]) + }; + + var reqOptions = { + locatorId: 871844001349632, + offset: 1025, + length: 200000 + }; + + var replyPart = { + argumentCount: 1, + buffer: new Buffer([ + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x18, 0x03, 0x00, + 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x61, 0x62, 0x63, 0x00, 0x00, 0x00, 0x00 + ]) + }; + + var replyOptions = { + locatorId: new Buffer([0x00, 0x00, 0x00, 0x00, 0xf0, 0x18, 0x03, 0x00]), + options: 1, + chunk: new Buffer([0x60, 0x61, 0x62, 0x63]) + }; + + describe('#ReadLob', function () { + + it('should write a ReadLob request', function () { + var part = ReadLobRequest.write({}, reqOptions); + part.should.eql(reqPart); + }); + + it('should read a ReadLob reply', function () { + var options = ReadLobReply.read(replyPart); + options.should.eql(replyOptions); + }); + + }); + +}); \ No newline at end of file diff --git a/test/data.ResultSet.js b/test/data.ResultSet.js new file mode 100644 index 0000000..52cd941 --- /dev/null +++ b/test/data.ResultSet.js @@ -0,0 +1,46 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var lib = require('../lib'); +var Parser = lib.Parser; + +var metadata = require('./fixtures/resultSetMetadata').TABLES; +var data = require('./fixtures/resultSetData').TABLES; + +describe('Data', function () { + + describe('#ResultSet', function () { + + var count = data.part.argumentCount; + var first = 0; + var last = count - 1; + + it('deserialize from part buffer', function (done) { + var rows = []; + var parser = new Parser(metadata.columns); + parser.parse(data.part.buffer, rows, function (err) { + if (err) { + throw err; + } + rows.should.have.length(count); + rows[first].should.eql(data.rows[first]); + rows[last].should.eql(data.rows[last]); + done(); + }); + }); + + }); + +}); \ No newline at end of file diff --git a/test/data.ResultSetMetadata.js b/test/data.ResultSetMetadata.js new file mode 100644 index 0000000..99c9f82 --- /dev/null +++ b/test/data.ResultSetMetadata.js @@ -0,0 +1,33 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var lib = require('../lib'); +var PartKind = lib.common.PartKind; +var ResultSetMetadata = lib.data[PartKind.RESULT_SET_METADATA]; + +var data = require('./fixtures/resultSetMetadata').VERSION_AND_CURRENT_USER; + +describe('Data', function () { + + describe('#ResultSetMetadata', function () { + + it('should read resultSet metadata', function () { + var columnMetadata = ResultSetMetadata.read(data.part); + columnMetadata.should.eql(data.columns); + }); + + }); + +}); \ No newline at end of file diff --git a/test/data.TopogolyInformation.js b/test/data.TopogolyInformation.js new file mode 100644 index 0000000..81fa1fa --- /dev/null +++ b/test/data.TopogolyInformation.js @@ -0,0 +1,40 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var lib = require('../lib'); +var PartKind = lib.common.PartKind; +var MultilineOptions = lib.data[PartKind.TOPOLOGY_INFORMATION]; + +var data = require('./fixtures/topogolyInformation').DEFAULT; + +describe('Data', function () { + + describe('#TopologyInformation', function () { + + it('should write topology information', function () { + + var part = MultilineOptions.write({}, data.options); + part.should.eql(data.part); + }); + + it('should deserialize options from buffer', function () { + var topologyInformation = MultilineOptions.read(data.part); + topologyInformation.should.eql(data.options); + + }); + + }); + +}); \ No newline at end of file diff --git a/test/fixtures/numbers.js b/test/fixtures/numbers.js new file mode 100644 index 0000000..c6d708f --- /dev/null +++ b/test/fixtures/numbers.js @@ -0,0 +1,125 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +module.exports = [ + 'zero', + 'one', + 'two', + 'three', + 'four', + 'five', + 'six', + 'seven', + 'eight', + 'nine', + 'ten', + 'eleven', + 'twelve', + 'thirteen', + 'fourteen', + 'fifteen', + 'sixteen', + 'seventeen', + 'eighteen', + 'nineteen', + 'twenty', + 'twenty-one', + 'twenty-two', + 'twenty-three', + 'twenty-four', + 'twenty-five', + 'twenty-six', + 'twenty-seven', + 'twenty-eight', + 'twenty-nine', + 'thirty', + 'thirty-one', + 'thirty-two', + 'thirty-three', + 'thirty-four', + 'thirty-five', + 'thirty-six', + 'thirty-seven', + 'thirty-eight', + 'thirty-nine', + 'fourty', + 'fourty-one', + 'fourty-two', + 'fourty-three', + 'fourty-four', + 'fourty-five', + 'fourty-six', + 'fourty-seven', + 'fourty-eight', + 'fourty-nine', + 'fifty', + 'fifty-one', + 'fifty-two', + 'fifty-three', + 'fifty-four', + 'fifty-five', + 'fifty-six', + 'fifty-seven', + 'fifty-eight', + 'fifty-nine', + 'sixty', + 'sixty-one', + 'sixty-two', + 'sixty-three', + 'sixty-four', + 'sixty-five', + 'sixty-six', + 'sixty-seven', + 'sixty-eight', + 'sixty-nine', + 'seventy', + 'seventy-one', + 'seventy-two', + 'seventy-three', + 'seventy-four', + 'seventy-five', + 'seventy-six', + 'seventy-seven', + 'seventy-eight', + 'seventy-nine', + 'eighty', + 'eighty-one', + 'eighty-two', + 'eighty-three', + 'eighty-four', + 'eighty-five', + 'eighty-six', + 'eighty-seven', + 'eighty-eight', + 'eighty-nine', + 'ninety', + 'ninety-one', + 'ninety-two', + 'ninety-three', + 'ninety-four', + 'ninety-five', + 'ninety-six', + 'ninety-seven', + 'ninety-eight', + 'ninety-nine', + 'one-hundred' +].map(indexAndDescription); + +function indexAndDescription(n, i) { + return { + A: i, + B: n + }; +} \ No newline at end of file diff --git a/test/fixtures/parametersData.js b/test/fixtures/parametersData.js new file mode 100644 index 0000000..c5095a9 --- /dev/null +++ b/test/fixtures/parametersData.js @@ -0,0 +1,65 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var lib = require('../lib'); +var TypeCode = lib.common.TypeCode; + +exports.DEFAULT = { + part: { + argumentCount: 1, + buffer: new Buffer( + '1e03616c6c' + + '1d0464617465' + + '1d0464657363' + + '0301000000' + + '0328000000' + + '0100' + + '1e03616c6c' + + '1e00' + + '1e00' + + '1e00', 'hex') + }, + values: [{ + type: TypeCode.NSTRING, + value: 'all' + }, { + type: TypeCode.STRING, + value: 'date' + }, { + type: TypeCode.STRING, + value: 'desc' + }, { + type: TypeCode.INT, + value: 1 + }, { + type: TypeCode.INT, + value: 40 + }, { + type: TypeCode.TINYINT, + value: 0 + }, { + type: TypeCode.NSTRING, + value: 'all' + }, { + type: TypeCode.NSTRING, + value: '' + }, { + type: TypeCode.NSTRING, + value: '' + }, { + type: TypeCode.NSTRING, + value: '' + }] +}; \ No newline at end of file diff --git a/test/fixtures/resultSetData.js b/test/fixtures/resultSetData.js new file mode 100644 index 0000000..853c6fc --- /dev/null +++ b/test/fixtures/resultSetData.js @@ -0,0 +1,420 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +exports.TABLES = { + part: { + argumentCount: 32, + buffer: new Buffer([ + 0x03, 0x53, 0x59, 0x53, // NVARCHAR + 0x0a, 0x52, 0x53, 0x5f, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x53, 0x5f, // NVARCHAR + 0x01, 0x06, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // BIGINT + 0xff, // NVARCHAR + 0x01, 0xb0, 0x01, // SMALLINT + 0x04, 0x54, 0x52, 0x55, 0x45, // VARCHAR + 0x04, 0x54, 0x52, 0x55, 0x45, // VARCHAR + 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, // VARCHAR + 0x03, 0x52, 0x4f, 0x57, // VARCHAR + 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, // VARCHAR + 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, // VARCHAR + 0x04, 0x54, 0x52, 0x55, 0x45, // VARCHAR + 0x04, 0x4e, 0x4f, 0x4e, 0x45, // VARCHAR + 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, // VARCHAR + 0x04, 0x4e, 0x4f, 0x4e, 0x45, // VARCHAR + 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, // VARCHAR + 0x04, 0x54, 0x52, 0x55, 0x45, // VARCHAR + 0x03, 0x01, // NCLOB + 0xff, // VARCHAR + 0xff, // VARCHAR + 0xff, // VARCHAR + 0xff, // VARCHAR + 0xff, // VARCHAR + 0xff, // VARCHAR + 0xff, // VARCHAR + 0xff, // VARCHAR + 0xff, // VARCHAR + 0xff, // VARCHAR + 0x01, 0x00, // TINYINT + 0xff, // VARCHAR + 0x03, 0x53, 0x59, 0x53, 0x0b, 0x52, 0x53, 0x5f, 0x43, 0x4f, 0x4c, 0x55, + 0x4d, 0x4e, 0x53, 0x5f, 0x01, 0x14, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x01, 0x18, 0x01, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x54, + 0x52, 0x55, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, 0x52, 0x4f, + 0x57, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, + 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, + 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, + 0x41, 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x03, 0x01, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x0a, 0x50, 0x5f, 0x49, 0x4e, 0x44, 0x45, 0x58, + 0x45, 0x53, 0x5f, 0x01, 0x26, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x01, 0xb8, 0x00, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x54, 0x52, + 0x55, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, 0x52, 0x4f, 0x57, + 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, + 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, + 0x41, 0x4c, 0x53, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, + 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x03, 0x01, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x09, 0x52, 0x53, 0x5f, 0x56, 0x49, 0x45, 0x57, + 0x53, 0x5f, 0x01, 0x34, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x01, 0xa0, 0x00, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x54, 0x52, 0x55, + 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, 0x52, 0x4f, 0x57, 0x05, + 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, + 0x54, 0x52, 0x55, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, + 0x4c, 0x53, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, + 0x53, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x03, 0x01, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x0a, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, + 0x45, 0x53, 0x5f, 0x01, 0x42, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x01, 0x88, 0x00, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x54, 0x52, + 0x55, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, 0x52, 0x4f, 0x57, + 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, + 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, + 0x41, 0x4c, 0x53, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, + 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x03, 0x01, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x0d, 0x50, 0x5f, 0x50, 0x52, 0x4f, 0x43, 0x45, + 0x44, 0x55, 0x52, 0x45, 0x53, 0x5f, 0x01, 0x4c, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x01, 0xf0, 0x00, 0x04, 0x54, 0x52, 0x55, 0x45, + 0x04, 0x54, 0x52, 0x55, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, + 0x52, 0x4f, 0x57, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, + 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x4e, 0x4f, 0x4e, + 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, + 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x03, + 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, + 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x0d, 0x50, 0x5f, 0x50, 0x52, 0x4f, 0x43, 0x50, + 0x41, 0x52, 0x41, 0x4d, 0x53, 0x5f, 0x01, 0x62, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x01, 0xe0, 0x00, 0x04, 0x54, 0x52, 0x55, 0x45, + 0x04, 0x54, 0x52, 0x55, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, + 0x52, 0x4f, 0x57, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, + 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x4e, 0x4f, 0x4e, + 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, + 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, + 0x03, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x01, 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x0c, 0x50, 0x5f, 0x53, 0x45, 0x51, 0x55, 0x45, + 0x4e, 0x43, 0x45, 0x53, 0x5f, 0x01, 0x74, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x01, 0xb8, 0x00, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, + 0x54, 0x52, 0x55, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, 0x52, + 0x4f, 0x57, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, + 0x53, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, + 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, + 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x03, 0x01, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, + 0xff, + 0x03, 0x53, 0x59, 0x53, 0x0b, 0x50, 0x5f, 0x53, 0x59, 0x4e, 0x4f, 0x4e, + 0x59, 0x4d, 0x53, 0x5f, 0x01, 0x80, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x01, 0x90, 0x00, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x54, + 0x52, 0x55, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, 0x52, 0x4f, + 0x57, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, + 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, + 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, + 0x41, 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x03, 0x01, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x0d, 0x50, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x49, + 0x53, 0x54, 0x49, 0x43, 0x53, 0x5f, 0x01, 0x88, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x01, 0xb8, 0x00, 0x04, 0x54, 0x52, 0x55, 0x45, + 0x04, 0x54, 0x52, 0x55, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, + 0x52, 0x4f, 0x57, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, + 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x4e, 0x4f, 0x4e, + 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, + 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x03, + 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, + 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x0a, 0x50, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x4d, + 0x41, 0x53, 0x5f, 0x01, 0x94, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x01, 0x60, 0x00, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x54, 0x52, + 0x55, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, 0x52, 0x4f, 0x57, + 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, + 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, + 0x41, 0x4c, 0x53, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, + 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, 0x01, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x0d, 0x50, 0x5f, 0x50, 0x52, 0x49, 0x4e, 0x43, + 0x49, 0x50, 0x41, 0x4c, 0x53, 0x5f, 0x01, 0x98, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x01, 0xc0, 0x00, 0x04, 0x54, 0x52, 0x55, 0x45, + 0x04, 0x54, 0x52, 0x55, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, + 0x52, 0x4f, 0x57, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, + 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x4e, 0x4f, 0x4e, + 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, + 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, + 0x03, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x01, 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x11, 0x50, 0x5f, 0x4c, 0x41, 0x53, 0x54, 0x5f, + 0x50, 0x41, 0x53, 0x53, 0x57, 0x4f, 0x52, 0x44, 0x53, 0x5f, 0x01, 0x9e, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x58, 0x00, 0x04, + 0x54, 0x52, 0x55, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x05, 0x46, 0x41, + 0x4c, 0x53, 0x45, 0x03, 0x52, 0x4f, 0x57, 0x05, 0x46, 0x41, 0x4c, 0x53, + 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, + 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, + 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, + 0x41, 0x4c, 0x53, 0x45, 0x03, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x13, 0x50, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, + 0x49, 0x44, 0x5f, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x53, 0x5f, + 0x01, 0xa2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x50, + 0x00, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x05, + 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, 0x52, 0x4f, 0x57, 0x05, 0x46, 0x41, + 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, + 0x55, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, + 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, + 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, 0x01, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x0d, 0x50, 0x5f, 0x50, 0x52, 0x49, 0x56, 0x49, + 0x4c, 0x45, 0x47, 0x45, 0x53, 0x5f, 0x01, 0xa6, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x01, 0x50, 0x00, 0x04, 0x54, 0x52, 0x55, 0x45, + 0x04, 0x54, 0x52, 0x55, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, + 0x52, 0x4f, 0x57, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, + 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x4e, 0x4f, 0x4e, + 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, + 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, + 0x03, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x01, 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x0b, 0x50, 0x5f, 0x4f, 0x42, 0x4a, 0x54, 0x59, + 0x50, 0x45, 0x53, 0x5f, 0x01, 0xaa, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x01, 0x50, 0x00, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x54, + 0x52, 0x55, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, 0x52, 0x4f, + 0x57, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, + 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, + 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, + 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, 0x01, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, + 0xff, + 0x03, 0x53, 0x59, 0x53, 0x0e, 0x50, 0x5f, 0x53, 0x55, 0x42, 0x4f, 0x42, + 0x4a, 0x54, 0x59, 0x50, 0x45, 0x53, 0x5f, 0x01, 0xae, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x50, 0x00, 0x04, 0x54, 0x52, 0x55, + 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, + 0x03, 0x52, 0x4f, 0x57, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, + 0x41, 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x4e, 0x4f, + 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x4e, 0x4f, 0x4e, + 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, + 0x45, 0x03, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x01, 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x0f, 0x50, 0x5f, 0x47, 0x52, 0x41, 0x4e, 0x54, + 0x45, 0x44, 0x50, 0x52, 0x49, 0x56, 0x53, 0x5f, 0x01, 0xb3, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x60, 0x00, 0x04, 0x54, 0x52, + 0x55, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, + 0x45, 0x03, 0x52, 0x4f, 0x57, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, + 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x4e, + 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x4e, 0x4f, + 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, + 0x53, 0x45, 0x03, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x01, 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x10, 0x50, 0x5f, 0x41, 0x53, 0x53, 0x49, 0x47, + 0x4e, 0x45, 0x44, 0x52, 0x4f, 0x4c, 0x45, 0x53, 0x5f, 0x01, 0xbd, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x50, 0x00, 0x04, 0x54, + 0x52, 0x55, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x05, 0x46, 0x41, 0x4c, + 0x53, 0x45, 0x03, 0x52, 0x4f, 0x57, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, + 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, + 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x4e, + 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, + 0x4c, 0x53, 0x45, 0x03, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x01, 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x15, 0x50, 0x5f, 0x45, 0x58, 0x54, 0x45, 0x52, + 0x4e, 0x41, 0x4c, 0x4f, 0x57, 0x4e, 0x45, 0x52, 0x53, 0x48, 0x49, 0x50, + 0x53, 0x5f, 0x01, 0xc2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x01, 0x58, 0x00, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x54, 0x52, 0x55, + 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, 0x52, 0x4f, 0x57, 0x05, + 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, + 0x54, 0x52, 0x55, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, + 0x4c, 0x53, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, + 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, 0x01, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x0f, 0x50, 0x5f, 0x45, 0x58, 0x54, 0x45, 0x52, + 0x4e, 0x41, 0x4c, 0x4f, 0x42, 0x4a, 0x53, 0x5f, 0x01, 0xc8, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x60, 0x00, 0x04, 0x54, 0x52, + 0x55, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, + 0x45, 0x03, 0x52, 0x4f, 0x57, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, + 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x4e, + 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x4e, 0x4f, + 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, + 0x53, 0x45, 0x03, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x01, 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x13, 0x50, 0x5f, 0x50, 0x52, 0x49, 0x56, 0x41, + 0x56, 0x41, 0x49, 0x4c, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x5f, + 0x01, 0xce, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x50, + 0x00, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x05, + 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, 0x52, 0x4f, 0x57, 0x05, 0x46, 0x41, + 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, + 0x55, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, + 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, + 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, 0x01, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x13, 0x50, 0x5f, 0x41, 0x55, 0x54, 0x48, 0x44, + 0x45, 0x50, 0x45, 0x4e, 0x44, 0x45, 0x4e, 0x43, 0x49, 0x45, 0x53, 0x5f, + 0x01, 0xd5, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x68, + 0x00, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x05, + 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, 0x52, 0x4f, 0x57, 0x05, 0x46, 0x41, + 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, + 0x55, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, + 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, + 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, 0x01, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x0f, 0x50, 0x5f, 0x52, 0x45, 0x53, 0x54, 0x52, + 0x49, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x01, 0xe2, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x78, 0x00, 0x04, 0x54, 0x52, + 0x55, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, + 0x45, 0x03, 0x52, 0x4f, 0x57, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, + 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x4e, + 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x4e, 0x4f, + 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, + 0x53, 0x45, 0x03, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x01, 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x0a, 0x50, 0x5f, 0x46, 0x49, 0x4c, 0x54, 0x45, + 0x52, 0x53, 0x5f, 0x01, 0xed, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x01, 0x50, 0x00, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x54, 0x52, + 0x55, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, 0x52, 0x4f, 0x57, + 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, + 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, + 0x41, 0x4c, 0x53, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, + 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, 0x01, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x11, 0x50, 0x5f, 0x46, 0x49, 0x4c, 0x54, 0x45, + 0x52, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x4e, 0x44, 0x53, 0x5f, 0x01, 0xf6, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x60, 0x00, 0x04, + 0x54, 0x52, 0x55, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x05, 0x46, 0x41, + 0x4c, 0x53, 0x45, 0x03, 0x52, 0x4f, 0x57, 0x05, 0x46, 0x41, 0x4c, 0x53, + 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, + 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, + 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, + 0x41, 0x4c, 0x53, 0x45, 0x03, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x0a, 0x50, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, + 0x54, 0x53, 0x5f, 0x01, 0xfb, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x01, 0x60, 0x00, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x54, 0x52, + 0x55, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, 0x52, 0x4f, 0x57, + 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, + 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, + 0x41, 0x4c, 0x53, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, + 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x03, 0x01, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x0d, 0x41, 0x46, 0x4c, 0x5f, 0x50, 0x41, 0x43, + 0x4b, 0x41, 0x47, 0x45, 0x53, 0x5f, 0x01, 0x01, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x01, 0xe8, 0x00, 0x04, 0x54, 0x52, 0x55, 0x45, + 0x04, 0x54, 0x52, 0x55, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, + 0x52, 0x4f, 0x57, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, + 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x4e, 0x4f, 0x4e, + 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, + 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x03, + 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, + 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x14, 0x43, 0x53, 0x5f, 0x49, 0x4e, 0x56, 0x41, + 0x4c, 0x49, 0x44, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x52, 0x45, 0x46, 0x53, + 0x5f, 0x01, 0x0c, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, + 0x88, 0x00, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, + 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, 0x52, 0x4f, 0x57, 0x05, 0x46, + 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x54, + 0x52, 0x55, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, + 0x53, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, + 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x03, 0x01, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x14, 0x50, 0x5f, 0x46, 0x55, 0x4c, 0x4c, 0x5f, + 0x54, 0x45, 0x58, 0x54, 0x5f, 0x49, 0x4e, 0x44, 0x45, 0x58, 0x45, 0x53, + 0x5f, 0x01, 0x15, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, + 0xc0, 0x00, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, + 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, 0x52, 0x4f, 0x57, 0x05, 0x46, + 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x54, + 0x52, 0x55, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, + 0x53, 0x45, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, + 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x03, 0x01, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x0f, 0x50, 0x5f, 0x41, 0x55, 0x44, 0x49, 0x54, + 0x45, 0x44, 0x55, 0x53, 0x45, 0x52, 0x53, 0x5f, 0x01, 0x26, 0x01, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x58, 0x00, 0x04, 0x54, 0x52, + 0x55, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, + 0x45, 0x03, 0x52, 0x4f, 0x57, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, + 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x04, 0x4e, + 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x4e, 0x4f, + 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, 0x41, 0x4c, + 0x53, 0x45, 0x03, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x01, 0x00, 0xff, + 0x03, 0x53, 0x59, 0x53, 0x11, 0x50, 0x5f, 0x41, 0x55, 0x44, 0x49, 0x54, + 0x45, 0x44, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x01, 0x29, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x58, 0x00, 0x04, + 0x54, 0x52, 0x55, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, 0x05, 0x46, 0x41, + 0x4c, 0x53, 0x45, 0x03, 0x52, 0x4f, 0x57, 0x05, 0x46, 0x41, 0x4c, 0x53, + 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, 0x54, 0x52, 0x55, 0x45, + 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x04, + 0x4e, 0x4f, 0x4e, 0x45, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x05, 0x46, + 0x41, 0x4c, 0x53, 0x45, 0x03, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xff, + ]) + }, + rows: { + 0: { + SCHEMA_NAME: 'SYS', + TABLE_NAME: 'RS_TABLES_', + TABLE_OID: 131078, + COMMENTS: null, + FIXED_PART_SIZE: 432, + IS_LOGGED: 'TRUE', + IS_SYSTEM_TABLE: 'TRUE', + IS_COLUMN_TABLE: 'FALSE', + TABLE_TYPE: 'ROW', + IS_INSERT_ONLY: 'FALSE', + IS_TENANT_SHARED_DATA: 'FALSE', + IS_TENANT_SHARED_METADATA: 'TRUE', + SESSION_TYPE: 'NONE', + IS_TEMPORARY: 'FALSE', + TEMPORARY_TABLE_TYPE: 'NONE', + IS_USER_DEFINED_TYPE: 'FALSE', + HAS_PRIMARY_KEY: 'TRUE', + PARTITION_SPEC: null, + USES_EXTKEY: null, + AUTO_MERGE_ON: null, + USES_DIMFN_CACHE: null, + IS_PUBLIC: null, + AUTO_OPTIMIZE_COMPRESSION_ON: null, + COMPRESSED_EXTKEY: null, + HAS_TEXT_FIELDS: null, + USES_QUEUE_TABLE: null, + IS_PRELOAD: null, + IS_PARTIAL_PRELOAD: null, + UNLOAD_PRIORITY: 0, + HAS_SCHEMA_FLEXIBILITY: null + }, + 31: { + SCHEMA_NAME: 'SYS', + TABLE_NAME: 'P_AUDITEDACTIONS_', + TABLE_OID: 131369, + COMMENTS: null, + FIXED_PART_SIZE: 88, + IS_LOGGED: 'TRUE', + IS_SYSTEM_TABLE: 'TRUE', + IS_COLUMN_TABLE: 'FALSE', + TABLE_TYPE: 'ROW', + IS_INSERT_ONLY: 'FALSE', + IS_TENANT_SHARED_DATA: 'FALSE', + IS_TENANT_SHARED_METADATA: 'TRUE', + SESSION_TYPE: 'NONE', + IS_TEMPORARY: 'FALSE', + TEMPORARY_TABLE_TYPE: 'NONE', + IS_USER_DEFINED_TYPE: 'FALSE', + HAS_PRIMARY_KEY: 'FALSE', + PARTITION_SPEC: null, + USES_EXTKEY: null, + AUTO_MERGE_ON: null, + USES_DIMFN_CACHE: null, + IS_PUBLIC: null, + AUTO_OPTIMIZE_COMPRESSION_ON: null, + COMPRESSED_EXTKEY: null, + HAS_TEXT_FIELDS: null, + USES_QUEUE_TABLE: null, + IS_PRELOAD: null, + IS_PARTIAL_PRELOAD: null, + UNLOAD_PRIORITY: 0, + HAS_SCHEMA_FLEXIBILITY: null + } + } +}; \ No newline at end of file diff --git a/test/fixtures/resultSetMetadata.js b/test/fixtures/resultSetMetadata.js new file mode 100644 index 0000000..a7c44a7 --- /dev/null +++ b/test/fixtures/resultSetMetadata.js @@ -0,0 +1,344 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var lib = require('../lib'); +var TypeCode = lib.common.TypeCode; +var ParameterMode = lib.common.ParameterMode; + +exports.VERSION_AND_CURRENT_USER = { + part: { + argumentCount: 2, + buffer: new Buffer([ + // first column + 0x02, + 0x09, + 0x00, 0x00, + 0x20, 0x00, + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, + // second column + 0x01, + 0x09, + 0x00, 0x00, + 0x06, 0x00, + 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0x14, 0x00, 0x00, 0x00, + // texts + 0x0b, 0x4d, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, + 0x07, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, + 0x0c, 0x43, 0x55, 0x52, 0x52, 0x45, 0x4e, 0x54, 0x5f, 0x55, 0x53, 0x45, + 0x52 + ]) + }, + columns: [{ + mode: 2, + dataType: 9, + fraction: 0, + length: 32, + tableName: 'M_DATABASE_', + schemaName: undefined, + columnName: 'VERSION', + columnDisplayName: 'VERSION' + }, { + mode: 1, + dataType: 9, + fraction: 0, + length: 6, + tableName: undefined, + schemaName: undefined, + columnName: undefined, + columnDisplayName: 'CURRENT_USER' + }] +}; + +exports.TABLES = { + columns: [{ + mode: ParameterMode.MANDATORY, + dataType: TypeCode.NVARCHAR, + fraction: 0, + length: 256, + tableName: 'TABLES', + schemaName: '', + columnName: 'SCHEMA_NAME', + columnDisplayName: 'SCHEMA_NAME' + }, { + mode: ParameterMode.MANDATORY, + dataType: TypeCode.NVARCHAR, + fraction: 0, + length: 256, + tableName: 'TABLES', + schemaName: '', + columnName: 'TABLE_NAME', + columnDisplayName: 'TABLE_NAME' + }, { + mode: ParameterMode.OPTIONAL, + dataType: TypeCode.BIGINT, + fraction: 0, + length: 19, + tableName: 'TABLES', + schemaName: '', + columnName: 'TABLE_OID', + columnDisplayName: 'TABLE_OID' + }, { + mode: ParameterMode.OPTIONAL, + dataType: TypeCode.NVARCHAR, + fraction: 0, + length: 5000, + tableName: 'TABLES', + schemaName: '', + columnName: 'COMMENTS', + columnDisplayName: 'COMMENTS' + }, { + mode: ParameterMode.OPTIONAL, + dataType: TypeCode.SMALLINT, + fraction: 0, + length: 5, + tableName: 'TABLES', + schemaName: '', + columnName: 'FIXED_PART_SIZE', + columnDisplayName: 'FIXED_PART_SIZE' + }, { + mode: ParameterMode.OPTIONAL, + dataType: TypeCode.VARCHAR1, + fraction: 0, + length: 5, + tableName: 'TABLES', + schemaName: '', + columnName: 'IS_LOGGED', + columnDisplayName: 'IS_LOGGED' + }, { + mode: ParameterMode.OPTIONAL, + dataType: TypeCode.VARCHAR1, + fraction: 0, + length: 5, + tableName: 'TABLES', + schemaName: '', + columnName: 'IS_SYSTEM_TABLE', + columnDisplayName: 'IS_SYSTEM_TABLE' + }, { + mode: ParameterMode.MANDATORY, + dataType: TypeCode.VARCHAR1, + fraction: 0, + length: 5, + tableName: 'TABLES', + schemaName: '', + columnName: 'IS_COLUMN_TABLE', + columnDisplayName: 'IS_COLUMN_TABLE' + }, { + mode: ParameterMode.OPTIONAL, + dataType: TypeCode.VARCHAR1, + fraction: 0, + length: 16, + tableName: 'TABLES', + schemaName: '', + columnName: 'TABLE_TYPE', + columnDisplayName: 'TABLE_TYPE' + }, { + mode: ParameterMode.OPTIONAL, + dataType: TypeCode.VARCHAR1, + fraction: 0, + length: 5, + tableName: 'TABLES', + schemaName: '', + columnName: 'IS_INSERT_ONLY', + columnDisplayName: 'IS_INSERT_ONLY' + }, { + mode: ParameterMode.OPTIONAL, + dataType: TypeCode.VARCHAR1, + fraction: 0, + length: 5, + tableName: 'TABLES', + schemaName: '', + columnName: 'IS_TENANT_SHARED_DATA', + columnDisplayName: 'IS_TENANT_SHARED_DATA' + }, { + mode: ParameterMode.OPTIONAL, + dataType: TypeCode.VARCHAR1, + fraction: 0, + length: 5, + tableName: 'TABLES', + schemaName: '', + columnName: 'IS_TENANT_SHARED_METADATA', + columnDisplayName: 'IS_TENANT_SHARED_METADATA' + }, { + mode: ParameterMode.OPTIONAL, + dataType: TypeCode.VARCHAR1, + fraction: 0, + length: 7, + tableName: 'TABLES', + schemaName: '', + columnName: 'SESSION_TYPE', + columnDisplayName: 'SESSION_TYPE' + }, { + mode: ParameterMode.MANDATORY, + dataType: TypeCode.VARCHAR1, + fraction: 0, + length: 5, + tableName: 'TABLES', + schemaName: '', + columnName: 'IS_TEMPORARY', + columnDisplayName: 'IS_TEMPORARY' + }, { + mode: ParameterMode.OPTIONAL, + dataType: TypeCode.VARCHAR1, + fraction: 0, + length: 8, + tableName: 'TABLES', + schemaName: '', + columnName: 'TEMPORARY_TABLE_TYPE', + columnDisplayName: 'TEMPORARY_TABLE_TYPE' + }, { + mode: ParameterMode.OPTIONAL, + dataType: TypeCode.VARCHAR1, + fraction: 0, + length: 5, + tableName: 'TABLES', + schemaName: '', + columnName: 'IS_USER_DEFINED_TYPE', + columnDisplayName: 'IS_USER_DEFINED_TYPE' + }, { + mode: ParameterMode.MANDATORY, + dataType: TypeCode.VARCHAR1, + fraction: 0, + length: 5, + tableName: 'TABLES', + schemaName: '', + columnName: 'HAS_PRIMARY_KEY', + columnDisplayName: 'HAS_PRIMARY_KEY' + }, { + mode: ParameterMode.OPTIONAL, + dataType: TypeCode.NCLOB, + fraction: 0, + length: -1, + tableName: 'TABLES', + schemaName: '', + columnName: 'PARTITION_SPEC', + columnDisplayName: 'PARTITION_SPEC' + }, { + mode: ParameterMode.OPTIONAL, + dataType: TypeCode.VARCHAR1, + fraction: 0, + length: 5, + tableName: 'TABLES', + schemaName: '', + columnName: 'USES_EXTKEY', + columnDisplayName: 'USES_EXTKEY' + }, { + mode: ParameterMode.OPTIONAL, + dataType: TypeCode.VARCHAR1, + fraction: 0, + length: 5, + tableName: 'TABLES', + schemaName: '', + columnName: 'AUTO_MERGE_ON', + columnDisplayName: 'AUTO_MERGE_ON' + }, { + mode: ParameterMode.OPTIONAL, + dataType: TypeCode.VARCHAR1, + fraction: 0, + length: 5, + tableName: 'TABLES', + schemaName: '', + columnName: 'USES_DIMFN_CACHE', + columnDisplayName: 'USES_DIMFN_CACHE' + }, { + mode: ParameterMode.OPTIONAL, + dataType: TypeCode.VARCHAR1, + fraction: 0, + length: 5, + tableName: 'TABLES', + schemaName: '', + columnName: 'IS_PUBLIC', + columnDisplayName: 'IS_PUBLIC' + }, { + mode: ParameterMode.OPTIONAL, + dataType: TypeCode.VARCHAR1, + fraction: 0, + length: 5, + tableName: 'TABLES', + schemaName: '', + columnName: 'AUTO_OPTIMIZE_COMPRESSION_ON', + columnDisplayName: 'AUTO_OPTIMIZE_COMPRESSION_ON' + }, { + mode: ParameterMode.OPTIONAL, + dataType: TypeCode.VARCHAR1, + fraction: 0, + length: 5, + tableName: 'TABLES', + schemaName: '', + columnName: 'COMPRESSED_EXTKEY', + columnDisplayName: 'COMPRESSED_EXTKEY' + }, { + mode: ParameterMode.OPTIONAL, + dataType: TypeCode.VARCHAR1, + fraction: 0, + length: 5, + tableName: 'TABLES', + schemaName: '', + columnName: 'HAS_TEXT_FIELDS', + columnDisplayName: 'HAS_TEXT_FIELDS' + }, { + mode: ParameterMode.OPTIONAL, + dataType: TypeCode.VARCHAR1, + fraction: 0, + length: 5, + tableName: 'TABLES', + schemaName: '', + columnName: 'USES_QUEUE_TABLE', + columnDisplayName: 'USES_QUEUE_TABLE' + }, { + mode: ParameterMode.OPTIONAL, + dataType: TypeCode.VARCHAR1, + fraction: 0, + length: 5, + tableName: 'TABLES', + schemaName: '', + columnName: 'IS_PRELOAD', + columnDisplayName: 'IS_PRELOAD' + }, { + mode: ParameterMode.OPTIONAL, + dataType: TypeCode.VARCHAR1, + fraction: 0, + length: 5, + tableName: 'TABLES', + schemaName: '', + columnName: 'IS_PARTIAL_PRELOAD', + columnDisplayName: 'IS_PARTIAL_PRELOAD' + }, { + mode: ParameterMode.OPTIONAL, + dataType: TypeCode.TINYINT, + fraction: 0, + length: 3, + tableName: 'TABLES', + schemaName: '', + columnName: 'UNLOAD_PRIORITY', + columnDisplayName: 'UNLOAD_PRIORITY' + }, { + mode: ParameterMode.OPTIONAL, + dataType: TypeCode.VARCHAR1, + fraction: 0, + length: 5, + tableName: 'TABLES', + schemaName: '', + columnName: 'HAS_SCHEMA_FLEXIBILITY', + columnDisplayName: 'HAS_SCHEMA_FLEXIBILITY' + }] +}; \ No newline at end of file diff --git a/test/fixtures/topogolyInformation.js b/test/fixtures/topogolyInformation.js new file mode 100644 index 0000000..b383d80 --- /dev/null +++ b/test/fixtures/topogolyInformation.js @@ -0,0 +1,144 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var lib = require('../lib'); +var TypeCode = lib.common.TypeCode; + +exports.DEFAULT = { + part: { + argumentCount: 2, + buffer: new Buffer([ + // first row + 0x0b, 0x00, + 0x01, 0x1d, 0x08, 0x00, 0x76, 0x65, 0x68, 0x78, 0x73, 0x30, 0x30, 0x31, + 0x02, 0x03, 0x3f, 0x75, 0x00, 0x00, + 0x03, 0x1d, 0x00, 0x00, + 0x04, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, + 0x05, 0x03, 0x02, 0x00, 0x00, 0x00, + 0x06, 0x1c, 0x01, + 0x07, 0x1c, 0x01, + 0x08, 0x03, 0x03, 0x00, 0x00, 0x00, + 0x09, 0x1d, 0x11, 0x00, 0x64, 0x68, 0x63, 0x70, 0x2e, 0x77, 0x64, 0x66, + 0x2e, 0x73, 0x61, 0x70, 0x2e, 0x63, 0x6f, 0x72, 0x70, + 0x0b, 0x1d, 0x17, 0x00, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, + 0x32, 0x2c, 0x31, 0x30, 0x2e, 0x36, 0x36, 0x2e, 0x31, 0x39, 0x30, 0x2e, + 0x32, 0x32, 0x31, + 0x0c, 0x1d, 0x08, 0x00, 0x76, 0x65, 0x68, 0x78, 0x73, 0x30, 0x30, 0x31, + // second row + 0x0a, 0x00, + 0x01, 0x1d, 0x08, 0x00, 0x76, 0x65, 0x68, 0x78, 0x73, 0x30, 0x30, 0x31, + 0x02, 0x03, 0x41, 0x75, 0x00, 0x00, + 0x03, 0x1d, 0x00, 0x00, + 0x04, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, + 0x05, 0x03, 0x03, 0x00, 0x00, 0x00, + 0x06, 0x1c, 0x01, + 0x08, 0x03, 0x04, 0x00, 0x00, 0x00, + 0x09, 0x1d, 0x11, 0x00, 0x64, 0x68, 0x63, 0x70, 0x2e, 0x77, 0x64, 0x66, + 0x2e, 0x73, 0x61, 0x70, 0x2e, 0x63, 0x6f, 0x72, 0x70, + 0x0b, 0x1d, 0x17, 0x00, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, + 0x32, 0x2c, 0x31, 0x30, 0x2e, 0x36, 0x36, 0x2e, 0x31, 0x39, 0x30, 0x2e, + 0x32, 0x32, 0x31, + 0x0c, 0x1d, 0x08, 0x00, 0x76, 0x65, 0x68, 0x78, 0x73, 0x30, 0x30, 0x31 + ]) + }, + options: [ + [{ + name: 1, + value: 'vehxs001', + type: TypeCode.STRING + }, { + name: 2, + value: 30015, + type: TypeCode.INT + }, { + name: 3, + value: '', + type: TypeCode.STRING + }, { + name: 4, + value: 1, + type: TypeCode.DOUBLE + }, { + name: 5, + value: 2, + type: TypeCode.INT + }, { + name: 6, + value: true, + type: TypeCode.BOOLEAN + }, { + name: 7, + value: true, + type: TypeCode.BOOLEAN + }, { + name: 8, + value: 3, + type: TypeCode.INT + }, { + name: 9, + value: 'dhcp.wdf.sap.corp', + type: TypeCode.STRING + }, { + name: 11, + value: '127.0.0.2,10.66.190.221', + type: TypeCode.STRING + }, { + name: 12, + value: 'vehxs001', + type: TypeCode.STRING + }], + [{ + name: 1, + value: 'vehxs001', + type: TypeCode.STRING + }, { + name: 2, + value: 30017, + type: TypeCode.INT + }, { + name: 3, + value: '', + type: TypeCode.STRING + }, { + name: 4, + value: 1, + type: TypeCode.DOUBLE + }, { + name: 5, + value: 3, + type: TypeCode.INT + }, { + name: 6, + value: true, + type: TypeCode.BOOLEAN + }, { + name: 8, + value: 4, + type: TypeCode.INT + }, { + name: 9, + value: 'dhcp.wdf.sap.corp', + type: TypeCode.STRING + }, { + name: 11, + value: '127.0.0.2,10.66.190.221', + type: TypeCode.STRING + }, { + name: 12, + value: 'vehxs001', + type: TypeCode.STRING + }] + ] +}; \ No newline at end of file diff --git a/test/lib.bignum.js b/test/lib.bignum.js new file mode 100644 index 0000000..9fac8de --- /dev/null +++ b/test/lib.bignum.js @@ -0,0 +1,197 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var lib = require('./lib'); +var bignum = lib.util.bignum; + +function readInt64(hex) { + return bignum.readInt64LE(new Buffer(hex, 'hex'), 0); +} + +function writeInt64(value) { + var buffer = new Buffer(8); + bignum.writeInt64LE(buffer, value, 0); + return buffer.toString('hex'); +} + +function readUInt64(hex) { + return bignum.readUInt64LE(new Buffer(hex, 'hex'), 0); +} + +function writeUInt64(value) { + var buffer = new Buffer(8); + bignum.writeUInt64LE(buffer, value, 0); + return buffer.toString('hex'); +} + +function readDec128(hex) { + return bignum.readDec128(new Buffer(hex, 'hex'), 0); +} +describe('BigNum', function () { + + describe('#Int64', function () { + + it('read negative numbers', function () { + readInt64('ffffffffffffffff').should.equal(-1); + readInt64('feffffffffffffff').should.equal(-2); + readInt64('0000ffffffffffff').should.equal(-Math.pow(2, 16)); + readInt64('00000000ffffffff').should.equal(-Math.pow(2, 32)); + readInt64('000000000000ffff').should.equal(-Math.pow(2, 48)); + readInt64('000000000000f0ff').should.equal(-Math.pow(2, 52)); + readInt64('000000000000e0ff').should.equal(-Math.pow(2, 53)); + readInt64('ffffffffffffdfff').should.equal('-9007199254740993'); + }); + + it('read positive numbers', function () { + readInt64('0100000000000000').should.equal(1); + readInt64('0200000000000000').should.equal(2); + readInt64('0001000000000000').should.equal(256); + readInt64('0000000001000000').should.equal(Math.pow(2, 32)); + readInt64('0000000000000100').should.equal(Math.pow(2, 48)); + readInt64('0000000000001000').should.equal(Math.pow(2, 52)); + readInt64('0000000000002000').should.equal(Math.pow(2, 53)); + readInt64('bd34a75e47780300').should.equal(976672856159421); + readInt64('0100000000002000').should.equal('9007199254740993'); + readInt64('7708530509f40102').should.equal('144664982633777271'); + }); + + it('write negative numbers', function () { + 'ffffffffffffffff'.should.equal(writeInt64(-1)); + 'feffffffffffffff'.should.equal(writeInt64(-2)); + '00000000ffffffff'.should.equal(writeInt64(-Math.pow(2, 32))); + '000000000000f0ff'.should.equal(writeInt64(-Math.pow(2, 52))); + '000000000000e0ff'.should.equal(writeInt64(-Math.pow(2, 53))); + 'ffffffffffffdfff'.should.equal(writeInt64('-9007199254740993')); + }); + + it('write positive numbers', function () { + '7708530509f40102'.should.equal(writeInt64('144664982633777271')); + '0100000000000000'.should.equal(writeInt64(1)); + '0001000000000000'.should.equal(writeInt64(256)); + 'bd34a75e47780300'.should.equal(writeInt64(976672856159421)); + '0000000000002000'.should.equal(writeInt64(Math.pow(2, 53))); + }); + + }); + + describe('#Unsigned Int64', function () { + + it('read numbers', function () { + readUInt64('7708530509f40102').should.equal('144664982633777271'); + readUInt64('0000000000000000').should.equal(0); + readUInt64('0000000000002000').should.equal(Math.pow(2, 53)); + readUInt64('ffffffff00000000').should.equal(Math.pow(2, 32) - 1); + readUInt64('ffffffffffffffff').should.equal('18446744073709551615'); + }); + + it('write numbers', function () { + '7708530509f40102'.should.equal(writeUInt64('144664982633777271')); + '0000000000002000'.should.equal(writeUInt64(Math.pow(2, 53))); + 'bd34a75e47780300'.should.equal(writeUInt64(976672856159421)); + 'feffffffffffffff'.should.equal(writeUInt64('18446744073709551614')); + 'ffffffffffffffff'.should.equal(writeUInt64('18446744073709551615')); + }); + + }); + + describe('#Decimal', function () { + + it('read null', function () { + (readDec128('ffffffffffffffffffffffffffffffff') === null).should.be + .true; + (readDec128('00000000000000000000000000000077') === null).should.be + .true; + }); + + it('read zero', function () { + readDec128('00000000000000000000000000004030').should.eql({ + s: 1, + m: 0, + e: 0 + }); + readDec128('000000000000000000000000000040b0').should.eql({ + s: -1, + m: 0, + e: 0 + }); + readDec128('00000000000000000000000000000000').should.eql({ + s: 1, + m: 0, + e: -6176 + }); + readDec128('0000000000000000000000000000feef').should.eql({ + s: -1, + m: 0, + e: 8159 + }); + }); + + it('read positive numbers', function () { + readDec128('63000000000000000000000000004230').should.eql({ + s: 1, + m: 99, + e: 1 + }); + readDec128('ae080000000000000000000000004430').should.eql({ + s: 1, + m: 2222, + e: 2 + }); + readDec128('7708530509f401027708530509f44130').should.eql({ + s: 1, + m: '10141919503094329964824243895208055', + e: 0 + }); + readDec128('ffffffffffffffffffffffffffffff6f').should.eql({ + s: 1, + m: '10384593717069655257060992658440191', + e: 8159 + }); + }); + + it('read negative numbers', function () { + readDec128('63000000000000000000000000003eb0').should.eql({ + s: -1, + m: 99, + e: -1 + }); + readDec128('ae080000000000000000000000003cb0').should.eql({ + s: -1, + m: 2222, + e: -2 + }); + readDec128('ffffffffffffffffffffffffffffffef').should.eql({ + s: -1, + m: '10384593717069655257060992658440191', + e: 8159 + }); + }); + + it('read m strictly', function () { + readDec128('63000000000000000000000000004030').m.should.equal(99); + readDec128('ae0800000000000000000000000040b0').m.should.equal(2222); + readDec128('00000000000020000000000000004030').m.should.equal(Math.pow( + 2, 53)); + readDec128('000000000000200000000000000040b0').m.should.equal(Math.pow( + 2, 53)); + readDec128('01000000000020000000000000004030').m.should.equal( + '9007199254740993'); + readDec128('ffffffffffffffffffffffffffffffef').m.should.equal( + '10384593717069655257060992658440191'); + }); + + }); + +}); \ No newline at end of file diff --git a/test/lib/config.tpl.json b/test/lib/config.tpl.json new file mode 100644 index 0000000..271a226 --- /dev/null +++ b/test/lib/config.tpl.json @@ -0,0 +1,6 @@ +{ + "host": "localhost", + "port": 30015, + "user": "USER", + "password": "PASSWORD" +} \ No newline at end of file diff --git a/test/lib/index.js b/test/lib/index.js new file mode 100644 index 0000000..30cdc60 --- /dev/null +++ b/test/lib/index.js @@ -0,0 +1,128 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var async = require('async'); +var lib = require(process.env.HDB_COV ? '../../lib-cov' : '../../lib'); +var util = lib.util; +util.extend(exports, lib); + +var NUMBERS = exports.NUMBERS = require('../fixtures/numbers'); + +var options = JSON.parse(fs.readFileSync(path.join(__dirname, 'config.json'))); + +exports.createDatabase = function createDatabase() { + return new Database(); +}; + +function Database() { + this.client = new lib.Client(options); + this.numbers = undefined; +} + +Database.prototype.connect = function connect(done) { + var db = this; + this.client.connect(function onconnect(err) { + if (err) { + return done(err); + } + var schema = options.user.toUpperCase(); + var sql = util.format('set schema', schema); + db.client.exec(sql, done); + }); +}; + +Database.prototype.disconnect = function disconnect(done) { + this.client.disconnect(done); +}; + +Database.prototype.createNumbers = function createNumbers(range, done) { + var db = this; + + if (typeof range === 'function') { + done = range; + range = [0, 100]; + } + db.numbers = NUMBERS.slice(range[0], range[1] + 1); + + function createTable(callback) { + var sql = 'drop table NUMBERS cascade'; + + function ondroptable() { + // ignore err + var sql = 'create table NUMBERS (a int, b varchar(16))'; + db.client.exec(sql, callback); + } + db.client.exec(sql, ondroptable); + } + + function insertNumbers(statement) { + function createNumberInsertTask(num) { + return async.apply(statement.exec.bind(statement), [num.A, num.B]); + } + var tasks = db.numbers.map(createNumberInsertTask); + + function onresult(err) { + statement.drop(); + done(err); + } + async.series(tasks, onresult); + } + + function ontable(err) { + if (err) { + return done(err); + } + var sql = 'insert into NUMBERS values (?, ?)'; + db.client.prepare(sql, function onprepare(err, statement) { + if (err) { + return done(err); + } + insertNumbers(statement); + }); + } + createTable(ontable); +}; + +Database.prototype.dropNumbers = function dropNumbers(done) { + var db = this; + + var sql = 'drop table NUMBERS cascade'; + db.client.exec(sql, done); +}; + +Database.prototype.createReadNumbersBetween = function createReadNumbersBetween( + done) { + var db = this; + + var sql = [ + 'create procedure READ_NUMBERS_BETWEEN (in a int, in b int, out nums NUMBERS)', + 'language sqlscript', + 'reads sql data with result view READ_NUMBERS_BETWEEN_VIEW as', + 'begin', + ' nums = select * from NUMBERS where a between :a and :b;', + 'end;' + ].join('\n'); + db.client.exec(sql, done); +}; + +Database.prototype.dropReadNumbersBetween = function dropReadNumbersBetween( + done) { + var db = this; + + var sql = 'drop procedure READ_NUMBERS_BETWEEN cascade'; + db.client.exec(sql, done); +}; \ No newline at end of file diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 0000000..90df8ac --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1,3 @@ +--require should +--slow 50 +--growl \ No newline at end of file diff --git a/test/request.Authenticate.js b/test/request.Authenticate.js new file mode 100644 index 0000000..d94cc06 --- /dev/null +++ b/test/request.Authenticate.js @@ -0,0 +1,82 @@ +// Copyright 2013 SAP AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific +// language governing permissions and limitations under the License. +'use strict'; + +var lib = require('./lib'); +var request = lib.request; +var scramsha256 = lib.auth.SCRAMSHA256; + +describe('Request', function () { + + var segmentHeader = new Buffer([ + 0x48, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, + 0x01, 0x00, + 0x01, + 0x41, + 0x00, + 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ]); + + var partHeader = new Buffer([ + 0x21, + 0x00, + 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x1a, 0x00, 0x00, 0x00, + 0xc8, 0xff, 0x00, 0x00 + ]); + + var filler = new Buffer([ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ]); + + var part = { + kind: lib.common.PartKind.AUTHENTICATION, + attributes: 0, + argumentCount: 1, + buffer: new Buffer([ + 0x03, 0x00, + 0x06, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, + 0x0b, 0x53, 0x43, 0x52, 0x41, 0x4d, 0x53, 0x48, 0x41, 0x32, 0x35, + 0x36, + 0x04, 0x01, 0x02, 0x03, 0x04 + ]) + }; + + var buffer = Buffer.concat([segmentHeader, partHeader, + part.buffer, filler + ]); + + var reqOptions = { + user: 'SYSTEM', + algorithm: 'SCRAMSHA256', + clientChallenge: new Buffer([0x01, 0x02, 0x03, 0x04]) + }; + + describe('#authenticate', function () { + + it('should create an authenticate request', + function () { + var req = request.authenticate(scramsha256, reqOptions); + req.parts.should.have.length(1); + req.parts[0].should.eql(part); + req.toBuffer().should.eql(buffer); + }); + + }); + +}); \ No newline at end of file