├── .nvmrc ├── .gitignore ├── examples └── voter │ └── voter │ ├── public │ ├── images │ │ └── logo.png │ └── stylesheets │ │ ├── style.css │ │ ├── images │ │ ├── ui-icons_222222_256x240.png │ │ ├── ui-icons_228ef1_256x240.png │ │ ├── ui-icons_ef8c08_256x240.png │ │ ├── ui-icons_ffd27a_256x240.png │ │ ├── ui-icons_ffffff_256x240.png │ │ ├── ui-bg_flat_10_000000_40x100.png │ │ ├── ui-bg_glass_100_f6f6f6_1x400.png │ │ ├── ui-bg_glass_100_fdf5ce_1x400.png │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ ├── ui-bg_gloss-wave_35_f6a828_500x100.png │ │ ├── ui-bg_highlight-soft_75_ffe45c_1x100.png │ │ ├── ui-bg_diagonals-thick_18_b81900_40x40.png │ │ ├── ui-bg_diagonals-thick_20_666666_40x40.png │ │ └── ui-bg_highlight-soft_100_eeeeee_1x100.png │ │ └── jquery-ui-1.8.17.custom.css │ ├── package.json │ ├── views │ ├── layout.jade │ └── index.jade │ ├── routes │ └── index.js │ ├── jsons │ └── votes.js │ ├── app.js │ └── models │ └── volt.js ├── tools └── testdb │ ├── deployment.xml │ ├── ddl-drop.sql │ ├── ddl.sql │ ├── project.xml │ ├── src │ └── com │ │ └── voltdb │ │ └── test │ │ └── typetest │ │ └── proc │ │ ├── InitTestType.java │ │ └── Insert.java │ └── run.sh ├── .eslintrc.json ├── LICENSE ├── package.json ├── lib ├── configuration.js ├── query.js ├── voltconstants.js ├── message.js ├── client.js ├── connection.js ├── parser.js └── ctype.js ├── CONTRIBUTING.md ├── test ├── util │ ├── test-context.js │ └── docker-util.js ├── testrunner.js └── cases │ ├── connections.js │ ├── typestest.js │ └── bufferTest.js ├── writer.js ├── README.md └── voternoui.js /.nvmrc: -------------------------------------------------------------------------------- 1 | 6.11.0 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | tools/testdb/voltdbroot/ 3 | tools/testdb/obj/ 4 | tools/testdb/log/ 5 | tools/testdb/typetest.jar 6 | -------------------------------------------------------------------------------- /examples/voter/voter/public/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VoltDB/voltdb-client-nodejs/HEAD/examples/voter/voter/public/images/logo.png -------------------------------------------------------------------------------- /tools/testdb/deployment.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /examples/voter/voter/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } -------------------------------------------------------------------------------- /examples/voter/voter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Voterjs" 3 | , "version": "0.0.1" 4 | , "private": true 5 | , "dependencies": { 6 | "express": "2.5.8" 7 | , "jade": ">= 0.0.1" 8 | } 9 | } -------------------------------------------------------------------------------- /examples/voter/voter/public/stylesheets/images/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VoltDB/voltdb-client-nodejs/HEAD/examples/voter/voter/public/stylesheets/images/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /examples/voter/voter/public/stylesheets/images/ui-icons_228ef1_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VoltDB/voltdb-client-nodejs/HEAD/examples/voter/voter/public/stylesheets/images/ui-icons_228ef1_256x240.png -------------------------------------------------------------------------------- /examples/voter/voter/public/stylesheets/images/ui-icons_ef8c08_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VoltDB/voltdb-client-nodejs/HEAD/examples/voter/voter/public/stylesheets/images/ui-icons_ef8c08_256x240.png -------------------------------------------------------------------------------- /examples/voter/voter/public/stylesheets/images/ui-icons_ffd27a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VoltDB/voltdb-client-nodejs/HEAD/examples/voter/voter/public/stylesheets/images/ui-icons_ffd27a_256x240.png -------------------------------------------------------------------------------- /examples/voter/voter/public/stylesheets/images/ui-icons_ffffff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VoltDB/voltdb-client-nodejs/HEAD/examples/voter/voter/public/stylesheets/images/ui-icons_ffffff_256x240.png -------------------------------------------------------------------------------- /tools/testdb/ddl-drop.sql: -------------------------------------------------------------------------------- 1 | DROP PROCEDURE com.voltdb.test.typetest.proc.Insert IF EXISTS; 2 | 3 | DROP PROCEDURE com.voltdb.test.typetest.proc.InitTestType IF EXISTS; 4 | 5 | DROP TABLE typetest IF EXISTS; 6 | -------------------------------------------------------------------------------- /examples/voter/voter/public/stylesheets/images/ui-bg_flat_10_000000_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VoltDB/voltdb-client-nodejs/HEAD/examples/voter/voter/public/stylesheets/images/ui-bg_flat_10_000000_40x100.png -------------------------------------------------------------------------------- /examples/voter/voter/public/stylesheets/images/ui-bg_glass_100_f6f6f6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VoltDB/voltdb-client-nodejs/HEAD/examples/voter/voter/public/stylesheets/images/ui-bg_glass_100_f6f6f6_1x400.png -------------------------------------------------------------------------------- /examples/voter/voter/public/stylesheets/images/ui-bg_glass_100_fdf5ce_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VoltDB/voltdb-client-nodejs/HEAD/examples/voter/voter/public/stylesheets/images/ui-bg_glass_100_fdf5ce_1x400.png -------------------------------------------------------------------------------- /examples/voter/voter/public/stylesheets/images/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VoltDB/voltdb-client-nodejs/HEAD/examples/voter/voter/public/stylesheets/images/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /examples/voter/voter/public/stylesheets/images/ui-bg_gloss-wave_35_f6a828_500x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VoltDB/voltdb-client-nodejs/HEAD/examples/voter/voter/public/stylesheets/images/ui-bg_gloss-wave_35_f6a828_500x100.png -------------------------------------------------------------------------------- /examples/voter/voter/public/stylesheets/images/ui-bg_highlight-soft_75_ffe45c_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VoltDB/voltdb-client-nodejs/HEAD/examples/voter/voter/public/stylesheets/images/ui-bg_highlight-soft_75_ffe45c_1x100.png -------------------------------------------------------------------------------- /examples/voter/voter/public/stylesheets/images/ui-bg_diagonals-thick_18_b81900_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VoltDB/voltdb-client-nodejs/HEAD/examples/voter/voter/public/stylesheets/images/ui-bg_diagonals-thick_18_b81900_40x40.png -------------------------------------------------------------------------------- /examples/voter/voter/public/stylesheets/images/ui-bg_diagonals-thick_20_666666_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VoltDB/voltdb-client-nodejs/HEAD/examples/voter/voter/public/stylesheets/images/ui-bg_diagonals-thick_20_666666_40x40.png -------------------------------------------------------------------------------- /examples/voter/voter/public/stylesheets/images/ui-bg_highlight-soft_100_eeeeee_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VoltDB/voltdb-client-nodejs/HEAD/examples/voter/voter/public/stylesheets/images/ui-bg_highlight-soft_100_eeeeee_1x100.png -------------------------------------------------------------------------------- /examples/voter/voter/views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype 2 | html 3 | head 4 | title= title 5 | link(rel='stylesheet', href='/stylesheets/style.css') 6 | link(rel='stylesheet', href='/stylesheets/jquery-ui-1.8.17.custom.css') 7 | script(src='/javascripts/jquery-1.7.1.min.js') 8 | script(src='/javascripts/jquery-ui-1.8.17.custom.min.js') 9 | body!= body -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es6": true, 4 | "node": true 5 | }, 6 | "extends": "eslint:recommended", 7 | "parserOptions": { 8 | "sourceType": "module" 9 | }, 10 | "rules": { 11 | "indent": [ 12 | "warn", 13 | 2 14 | ], 15 | "linebreak-style": [ 16 | "warn", 17 | "unix" 18 | ], 19 | "quotes": [ 20 | "warn", 21 | "double" 22 | ], 23 | "semi": [ 24 | "error", 25 | "always" 26 | ] 27 | } 28 | } -------------------------------------------------------------------------------- /tools/testdb/ddl.sql: -------------------------------------------------------------------------------- 1 | -- test types 2 | CREATE TABLE typetest 3 | ( 4 | test_id integer NOT NULL, 5 | test_tiny tinyint NOT NULL, 6 | test_small smallint NOT NULL, 7 | test_integer integer NOT NULL, 8 | test_big bigint NOT NULL, 9 | test_float float NOT NULL, 10 | test_decimal decimal NOT NULL, 11 | test_varchar varchar(100) NOT NULL, 12 | test_varbinary varbinary(4) NOT NULL, 13 | test_timestamp timestamp NOT NULL, 14 | CONSTRAINT PK_tests PRIMARY KEY 15 | ( 16 | test_id 17 | ) 18 | ); 19 | 20 | PARTITION TABLE typetest ON COLUMN test_id; 21 | 22 | CREATE PROCEDURE FROM CLASS com.voltdb.test.typetest.proc.Insert; 23 | 24 | CREATE PROCEDURE FROM CLASS com.voltdb.test.typetest.proc.InitTestType; 25 | -------------------------------------------------------------------------------- /tools/testdb/project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Nodejs client test 5 | 1 6 | Various test cases 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /examples/voter/voter/views/index.jade: -------------------------------------------------------------------------------- 1 | img(src = '/images/logo.png') 2 | 3 | h1= title 4 | p Welcome to the #{title} 5 | 6 | h2 Description 7 | p This application demonstrates a VoltDB generating data asynchronously, 8 | | reading it and rendering to a live table. 9 | 10 | 11 | table#VOTES 12 | thead 13 | tr: th Candidate 14 | th Votes 15 | tbody 16 | tr: td 17 | td 18 | 19 | script. 20 | $(function() { 21 | setInterval(getVotes, 400); 22 | }); 23 | 24 | function getVotes() { 25 | $.getJSON('results', function(data) { 26 | $.each($('#VOTES tbody tr'), function (index, row) { 27 | $(row).remove(); 28 | }); 29 | 30 | tbody = $('#VOTES'); 31 | 32 | rows = data.rows; 33 | $.each(rows, function( index, row) { 34 | tbody.append('' + row.CONTESTANT_NAME + '' + row.TOTAL_VOTES + ''); 35 | }); 36 | }); 37 | } 38 | 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2008-2018 VoltDB Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 | OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "voltjs", 3 | "version": "2.0.0", 4 | "description": "VoltDB binary driver", 5 | "keywords": [ 6 | "voltdb", 7 | "client", 8 | "driver", 9 | "binary driver" 10 | ], 11 | "bugs": { 12 | "url": "https://github.com/VoltDB/voltdb-client-nodejs/issues", 13 | "email": "support@voltdb.com" 14 | }, 15 | "license": "MIT", 16 | "author": "VoltDB", 17 | "engines": { 18 | "node": ">= 6.11.0" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/VoltDB/voltdb-client-nodejs.git" 23 | }, 24 | "scripts": { 25 | "lint": "eslint test/util", 26 | "test": "node test/testrunner.js -i local" 27 | }, 28 | "dependencies": { 29 | "bignumber": "^1.1.0", 30 | "ctype": "^0.5.0", 31 | "cli": "^1.0.0", 32 | "debug": "^3.0.1", 33 | "supports-color": "^4.4.0" 34 | }, 35 | "devDependencies": { 36 | "eslint": "^4.7.1", 37 | "eslint-config-standard": "^10.2.1", 38 | "eslint-plugin-import": "^2.7.0", 39 | "eslint-plugin-node": "^5.1.1", 40 | "eslint-plugin-promise": "^3.5.0", 41 | "eslint-plugin-standard": "^3.0.1", 42 | "nodeunit": "^0.11.0", 43 | "yargs": "^10.0.3" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /examples/voter/voter/routes/index.js: -------------------------------------------------------------------------------- 1 | /* This file is part of VoltDB. 2 | * Copyright (C) 2008-2018 VoltDB Inc. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 | * OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | exports.index = function(req, res) { 24 | 25 | res.render('index', { 26 | title : 'VoltDB Voter Example' 27 | }) 28 | }; 29 | -------------------------------------------------------------------------------- /lib/configuration.js: -------------------------------------------------------------------------------- 1 | /* This file is part of VoltDB. 2 | * Copyright (C) 2008-2018 VoltDB Inc. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 | * OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | VoltConfiguration = function() { 24 | } 25 | VoltConfiguration.prototype = Object.create({ 26 | host : 'localhost', 27 | port : 21212, 28 | username : 'user', 29 | password : 'password', 30 | service : 'database', 31 | queryTimeout : 600000, 32 | queryTimeoutInterval: 60000, 33 | flushInterval: 1000, 34 | messageQueueSize : 10, 35 | maxConsecutiveWrites: 5000 36 | }); 37 | 38 | module.exports = VoltConfiguration; 39 | -------------------------------------------------------------------------------- /examples/voter/voter/jsons/votes.js: -------------------------------------------------------------------------------- 1 | /* This file is part of VoltDB. 2 | * Copyright (C) 2008-2018 VoltDB Inc. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 | * OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | var volt = require("../models/volt"), 25 | VoltConstants = require(__dirname + '/../../../../lib/voltconstants'); 26 | 27 | exports.votes = function(req, res) { 28 | return volt.getVoteResults(function displayResults(code, event, results) { 29 | if(code == VoltConstants.STATUS_CODES.SUCCESS) { 30 | res.json({ 31 | 'rows' : results.table[0] 32 | }); 33 | } else { 34 | res.json({ 35 | 'critical fault, database down': 500 36 | }); 37 | } 38 | }); 39 | } -------------------------------------------------------------------------------- /tools/testdb/src/com/voltdb/test/typetest/proc/InitTestType.java: -------------------------------------------------------------------------------- 1 | /* This file is part of VoltDB. 2 | * Copyright (C) 2008-2018 VoltDB Inc. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 | * OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | package com.voltdb.test.typetest.proc; 25 | 26 | import java.math.BigDecimal; 27 | import java.math.MathContext; 28 | 29 | import org.voltdb.ProcInfo; 30 | 31 | @ProcInfo (partitionInfo = "typetest.test_id:0", singlePartition = true) 32 | public class InitTestType extends Insert { 33 | 34 | public long run(int blah) { 35 | return super.run(0, (byte)1, (short)2, 3, 4l, 5.1d, new BigDecimal(6.000342d,MathContext.DECIMAL32), 36 | "seven", 37 | new byte[] { (byte)8, (byte)8, (byte)8, (byte)8}, 38 | 1331310436605000l); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thank you for your interest in contributing. VoltDB uses GitHub to manage reviews of pull requests. We welcome your contributions to VoltDB or to any of the related libraries and tools. 4 | 5 | VoltDB uses the standard github workflow, meaning we make use of forking and pull requests. 6 | 7 | * If you have a trivial fix or improvement, go ahead and create a pull request. 8 | 9 | * If you are interested in contributing something more involved, feel free to discuss your ideas in [VoltDB Public](http://chat.voltdb.com/) on Slack. 10 | 11 | ## Contributor License Agreement 12 | 13 | In order to contribute code to VoltDB, you must first sign the [VoltDB Contributor License Agreement (CLA)](https://www.voltdb.com/contributor-license-agreement/) and email an image or PDF of the document including your hand-written signature to [support@voltdb.com](mailto:support@voltdb.com). VoltDB will sign and return the final copy of the agreement for your records. 14 | 15 | ## How to submit code 16 | 17 | The workflow is essentially the following: 18 | 19 | 1. Fork the VoltDB project 20 | 2. Make a branch. Commit your changes to this branch. (See note below) 21 | 3. Issue a pull request on the VoltDB repository. 22 | 23 | Once you have signed the CLA, a VoltDB engineer will review your pull request. 24 | 25 | Note: 26 | 27 | It will be easier to keep your work merge-able (conflict-free) if you don't work directly on your master branch in your VoltDB fork. Rather, keep your master branch in sync with the VoltDB repository and apply your changes on a branch of your fork. 28 | 29 | For further reading: 30 | 31 | * [How to fork a GitHub repository](https://help.github.com/articles/fork-a-repo) 32 | * [Using pull requests](https://help.github.com/articles/using-pull-requests/) 33 | 34 | ## Additional Resources 35 | 36 | * [VoltDB Wiki](https://github.com/VoltDB/voltdb/wiki) on Github 37 | * [VoltDB Public](http://chat.voltdb.com/) on Slack 38 | * [VoltDB Community Forum](https://forum.voltdb.com/) 39 | -------------------------------------------------------------------------------- /test/util/test-context.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Manages the "context" of a test run, which basically means how the tests interact with the outside world. This 3 | * includes things like whether the Volt instance being tested against is a local instance or running in a Docker 4 | * container etc. 5 | * 6 | */ 7 | !(function(global) { // eslint-disable-line no-unused-vars 8 | 9 | "use strict"; 10 | 11 | const yargs = require("yargs"); 12 | const dockerUtil = require("../util/docker-util"); 13 | 14 | const VOLT_CONTAINER_NAME = "node1"; 15 | 16 | // TODO: Should eventually go in VoltConstants. 17 | const VOLT_CLIENT_PORT = 21212; 18 | 19 | const config = { 20 | instance: null 21 | }; 22 | 23 | /** 24 | * Configures the context for a test run. Currently loads this from cli options but could be 25 | * set with a file, env variables, etc. 26 | */ 27 | function _setup(){ 28 | 29 | var argv = yargs 30 | .alias("i", "instance") 31 | .demandOption(["i"]) 32 | .describe("i", "Specify the type of VoltDB instance the tests will be run against. " + 33 | "Can be a local instance [local] or a local instance running in a Docker container [docker]") 34 | .choices("i", ["local", "docker"]) 35 | .argv; 36 | 37 | config.instance = argv.instance; 38 | } 39 | 40 | 41 | /** 42 | * Based on config passed, figures out whether to use the default Volt port or 43 | * look for a Docker container running Volt to run the tests against. 44 | */ 45 | function _port(){ 46 | 47 | var voltPort = -1; 48 | 49 | if(config.instance === "local"){ 50 | voltPort = VOLT_CLIENT_PORT; 51 | } 52 | else if(config.instance === "docker"){ 53 | voltPort = dockerUtil.getExposedVoltPort(VOLT_CONTAINER_NAME); 54 | } 55 | else{ 56 | // This should be caught by arg parser, but we throw here to be cautious 57 | throw new Error(`Unrecognised instance mode ${config.instance}`); 58 | } 59 | 60 | return voltPort; 61 | } 62 | 63 | //Exports 64 | module.exports = { 65 | setup : _setup, 66 | port: _port 67 | }; 68 | 69 | }(this)); -------------------------------------------------------------------------------- /test/testrunner.js: -------------------------------------------------------------------------------- 1 | /* This file is part of VoltDB. 2 | * Copyright (C) 2008-2018 VoltDB Inc. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 | * OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | var nodeunit = require('nodeunit'); 25 | var fs = require('fs'); 26 | var child_process = require('child_process'); 27 | const path = require('path'); 28 | 29 | const testCasesDirectory = path.resolve(__dirname, "cases"); 30 | 31 | var TestRunner = function() { 32 | this.testDirectories = [testCasesDirectory]; 33 | this.fileList = []; 34 | } 35 | tr = TestRunner.prototype; 36 | 37 | tr.loadTests = function() { 38 | console.log(this.testDirectories.length); 39 | for(var index = 0; index < this.testDirectories.length; index++) { 40 | var cases = fs.readdirSync(this.testDirectories[index]) || []; 41 | 42 | for(var inner = 0; inner < cases.length; inner++) { 43 | this.fileList = this.fileList.concat(this.testDirectories[index] + '/' + cases[inner]); 44 | } 45 | } 46 | } 47 | 48 | tr.run = function() { 49 | var reporter = nodeunit.reporters.default; 50 | reporter.run(this.fileList, null, function something() { 51 | process.exit(0); 52 | }); 53 | } 54 | function main() { 55 | var runner = new TestRunner(); 56 | runner.loadTests(); 57 | runner.run(); 58 | }; 59 | 60 | main(); 61 | -------------------------------------------------------------------------------- /tools/testdb/src/com/voltdb/test/typetest/proc/Insert.java: -------------------------------------------------------------------------------- 1 | /* This file is part of VoltDB. 2 | * Copyright (C) 2008-2018 VoltDB Inc. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 | * OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | package com.voltdb.test.typetest.proc; 24 | 25 | import java.math.BigDecimal; 26 | 27 | import org.voltdb.*; 28 | import org.voltdb.types.TimestampType; 29 | 30 | @ProcInfo ( 31 | partitionInfo = "typetest.test_id:0", 32 | singlePartition = true) 33 | public class Insert extends VoltProcedure { 34 | 35 | public static final long SUCCESS = 0; 36 | 37 | public final SQLStmt insertStmt = new SQLStmt( 38 | "insert into typetest " 39 | /*+ " ( test_id" 40 | + ",test_tiny" 41 | + ",test_small" 42 | + ",test_integer" 43 | + ",test_big" 44 | + ",test_float" 45 | + ",test_decimal" 46 | + ",test_varchar" 47 | + ",test_varbinary" 48 | + ",test_timestamp )" */ 49 | + " values ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ? );"); 50 | 51 | public long run( int id, byte tiny, short small, 52 | int integer, long big, double dbl, BigDecimal decimal, 53 | String str, byte[] ba, long timestamp ) throws VoltAbortException { 54 | 55 | voltQueueSQL( insertStmt, id, tiny, small, integer, big, dbl, decimal, 56 | str, ba, timestamp ); 57 | 58 | voltExecuteSQL(); 59 | return Insert.SUCCESS; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /tools/testdb/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | function help() { 4 | echo "Usage: ./run.sh {clean|catalog|server}" 5 | } 6 | 7 | if [ -z "$VOLTDB_HOME" ] 8 | then 9 | echo "VOLTDB_HOME must be set"; 10 | help 11 | exit 1 12 | else 13 | APPNAME="typetest" 14 | CLASSPATH="`ls -x $VOLTDB_HOME/voltdb/voltdb-*.jar | tr '[:space:]' ':'``ls -x $VOLTDB_HOME/lib/*.jar | tr '[:space:]' ':'`" 15 | VOLTDB="$VOLTDB_HOME/bin/voltdb" 16 | VOLTCOMPILER="$VOLTDB_HOME/bin/voltcompiler" 17 | LICENSE="$VOLTDB_HOME/voltdb/license.xml" 18 | LEADER="localhost" 19 | fi 20 | 21 | # remove build artifacts 22 | function clean() { 23 | rm -rf obj debugoutput $APPNAME.jar voltdbroot plannerlog.txt voltdbroot 24 | } 25 | 26 | # compile the source code for procedures and the client 27 | function srccompile() { 28 | mkdir -p obj 29 | javac -classpath $CLASSPATH -d obj \ 30 | -sourcepath ./src \ 31 | ./src/com/voltdb/test/typetest/proc/*.java 32 | jar cvf $APPNAME.jar -C obj . 33 | # stop if compilation fails 34 | if [ $? != 0 ]; then exit; fi 35 | } 36 | 37 | # build an application catalog 38 | function catalog() { 39 | srccompile 40 | 41 | # stop if compilation fails 42 | if [ $? != 0 ]; then exit; fi 43 | } 44 | 45 | # run the voltdb server locally 46 | function server() { 47 | # if a catalog doesn't exist, build one 48 | if [ ! -f $APPNAME.jar ]; then catalog; fi 49 | 50 | DOCKER_QUERY=`docker port node1 21212` 51 | if [ $? -eq 0 ]; then 52 | PORT=`echo ${DOCKER_QUERY} | cut -d: -f2` 53 | # Found a Docker container running Volt, load the procs into the db 54 | echo "Found local Docker container running Volt, loading schema" 55 | echo "load classes typetest.jar;" | sqlcmd --port=$PORT 56 | echo "file ddl-drop.sql;" | sqlcmd --port=$PORT 57 | echo "file ddl.sql;" | sqlcmd --port=$PORT 58 | else 59 | # No Docker container running Volt, start a local instance if we can 60 | echo "Could not find local Docker container running Volt, starting local instance with schema" 61 | # run the server 62 | $VOLTDB init -C deployment.xml -f -j $APPNAME.jar -s ddl.sql 63 | $VOLTDB start 64 | fi 65 | } 66 | 67 | # Run the target passed as the first arg on the command line 68 | # If no first arg, run server 69 | if [ $# -gt 1 ]; then help; exit; fi 70 | if [ $# = 1 ]; then $1; else server; fi -------------------------------------------------------------------------------- /lib/query.js: -------------------------------------------------------------------------------- 1 | /* This file is part of VoltDB. 2 | * Copyright (C) 2008-2018 VoltDB Inc. 3 | * 4 | * This file contains original code and/or modifications of original code. 5 | * Any modifications made by VoltDB Inc. are licensed under the following 6 | * terms and conditions: 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining 9 | * a copy of this software and associated documentation files (the 10 | * "Software"), to deal in the Software without restriction, including 11 | * without limitation the rights to use, copy, modify, merge, publish, 12 | * distribute, sublicense, and/or sell copies of the Software, and to 13 | * permit persons to whom the Software is furnished to do so, subject to 14 | * the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 23 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 24 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | */ 27 | 28 | var EventEmitter = require('events').EventEmitter; 29 | var Message = require('./message').Message; 30 | VoltQuery = function(procName, types) { 31 | this.procName = procName; 32 | this.types = types || []; 33 | this.parameters = []; 34 | this.uid = null; 35 | } 36 | 37 | VoltQuery.prototype.setParameters = function(params) { 38 | this.parameters = params; 39 | } 40 | 41 | VoltQuery.prototype.setUID = function(uid) { 42 | this.uid = uid; 43 | } 44 | 45 | VoltQuery.prototype.getMessage = function() { 46 | var message = new Message(); 47 | message.writeString(this.procName); 48 | message.writeBinary(new Buffer(this.uid)); 49 | message.writeParameterSet(this.types, this.parameters); 50 | return message; 51 | } 52 | VoltProcedure = function(name, types) { 53 | this.name = name; 54 | this.types = types; 55 | } 56 | 57 | VoltProcedure.prototype.getQuery = function() { 58 | return new VoltQuery(this.name, this.types); 59 | } 60 | 61 | module.exports = VoltQuery; 62 | //remove 63 | module.exports = VoltProcedure; 64 | -------------------------------------------------------------------------------- /test/util/docker-util.js: -------------------------------------------------------------------------------- 1 | !(function(global) { // eslint-disable-line no-unused-vars 2 | 3 | "use strict"; 4 | 5 | const childProcess = require("child_process"); 6 | const debug = require("debug")("voltdb-client-nodejs:DockerUtil"); 7 | const os = require('os'); 8 | 9 | /* 10 | * TODO: Should eventually go in VoltConstants. 11 | */ 12 | const VOLT_CLIENT_PORT = 21212; 13 | 14 | /* 15 | * Looks for a docker container with the given name on the local machine and finds 16 | * which port the container's 21212 port has been exposed on. Intended to be used 17 | * with the localhostcluster docker containers defined in the main volt repository. 18 | * 19 | * NOTE: This won't fail if no exposed port can be found, either because of a command 20 | * failure or if there is no docker container running Volt, it will just print a warning 21 | * and return the default Volt client port. 22 | */ 23 | function _getExposedVoltPort(containerName) { 24 | 25 | var clientPort = VOLT_CLIENT_PORT; 26 | 27 | /* 28 | * "docker port node1 21212" will return something like this "0.0.0.0:33164". Just parse out the port at the end. 29 | */ 30 | const command = "docker"; 31 | const args = ["port", containerName, VOLT_CLIENT_PORT]; 32 | const dockerPortResponse = childProcess.spawnSync(command, args); 33 | if(dockerPortResponse.status !== 0){ 34 | // Failure, log a warning and just fall through, returning the default port 35 | console.warn(`Docker port query failure | Will return default port '${VOLT_CLIENT_PORT}'. \ 36 | Docker port query for container '${containerName}' and port '${VOLT_CLIENT_PORT}' failed, error was:${os.EOL} \ 37 | ${dockerPortResponse.stderr.toString()}`); 38 | } 39 | else{ 40 | // Success 41 | const dockerPortResponseString = dockerPortResponse.stdout.toString(); 42 | const dockerPortResponseArray = dockerPortResponseString.replace("\n", "").replace("\r", "").split(":"); 43 | if(dockerPortResponseArray.length === 2){ 44 | debug("Container Found | Name: %o, Client Port: %o, Exposed Client Port: %o ", containerName, VOLT_CLIENT_PORT, clientPort); 45 | clientPort = parseInt(dockerPortResponseArray[1]); 46 | } 47 | else{ 48 | // Failure, log a warning and just fall through, returning the default port 49 | console.warn(`Docker port query failure | Will return default port '${VOLT_CLIENT_PORT}'. \ 50 | Docker port query for container '${containerName}' and port '${VOLT_CLIENT_PORT}' returned unrecognised response, response was:${os.EOL} \ 51 | ${dockerPortResponseString}`); 52 | } 53 | } 54 | 55 | return clientPort; 56 | } 57 | 58 | // Exports 59 | module.exports = { 60 | getExposedVoltPort : _getExposedVoltPort 61 | }; 62 | 63 | }(this)); 64 | -------------------------------------------------------------------------------- /test/cases/connections.js: -------------------------------------------------------------------------------- 1 | /* This file is part of VoltDB. 2 | * Copyright (C) 2008-2018 VoltDB Inc. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 | * OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | var VoltClient = require('../../lib/client'); 25 | var VoltConfiguration = require('../../lib/configuration'); 26 | var util = require('util'); 27 | var testCase = require('nodeunit'); 28 | const testContext = require("../util/test-context"); 29 | const debug = require("debug")("voltdb-client-nodejs:ConnectionsTest"); 30 | 31 | // Setup context 32 | testContext.setup(); 33 | 34 | function goodConfig() { 35 | return config('localhost'); 36 | } 37 | 38 | function badConfig() { 39 | return config('idontexist'); 40 | } 41 | 42 | function config(host) { 43 | debug('this config got called'); 44 | var config = new VoltConfiguration(); 45 | config.host = host; 46 | config.port = testContext.port(); 47 | var configs = []; 48 | configs.push(config); 49 | return configs; 50 | } 51 | 52 | exports.connections = { 53 | 54 | setUp : function(callback) { 55 | debug('connections setup called'); 56 | callback(); 57 | }, 58 | tearDown : function(callback) { 59 | debug('connections teardown called'); 60 | callback(); 61 | }, 62 | 'Bad connection results' : function(test) { 63 | debug('running bad connection test'); 64 | var client = new VoltClient(badConfig()) 65 | client.connect(function startup(code, event, results) { 66 | debug('bad connection test'); 67 | test.expect(1); 68 | test.notEqual(code, null, 'There should not be a host named idontexists'); 69 | client.exit(); 70 | test.done(); 71 | }); 72 | }, 73 | 'Good connection results' : function(test) { 74 | debug('running good connection test'); 75 | var client = new VoltClient(goodConfig()) 76 | client.connect(function startup(code, event, results) { 77 | test.expect(1); 78 | test.equal(code, null, 'Should have been able to connect, is Volt running on localhost?'); 79 | client.exit(); 80 | test.done(); 81 | }); 82 | } 83 | }; 84 | -------------------------------------------------------------------------------- /examples/voter/voter/app.js: -------------------------------------------------------------------------------- 1 | /* This file is part of VoltDB. 2 | * Copyright (C) 2008-2018 VoltDB Inc. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 | * OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | /* 25 | * This version of the sample requires that you have the VoltDB server installed 26 | * and running the voter example database. 27 | * 28 | * This sample will spawn a number( equal to the number of cores divided by 2) 29 | * of cluster nodes that will add votes to a collection of candidates. 30 | * The main cluster node will act as the web server. 31 | * 32 | * Please see the ./modules/volt.js file for a detailed explanation of the 33 | * client code. 34 | */ 35 | 36 | var express = require('express'), 37 | routes = require('./routes'), volt = require('./models/volt'), 38 | votes = require('./jsons/votes'), util = require('util'), 39 | cluster = require('cluster'), numCPUs = require('os').cpus().length; 40 | 41 | function webserverProcess() { 42 | var app = module.exports = express.createServer(); 43 | 44 | // Configuration 45 | 46 | app.configure(function() { 47 | app.set('views', __dirname + '/views'); 48 | app.set('view engine', 'jade'); 49 | app.use(express.bodyParser()); 50 | app.use(express.methodOverride()); 51 | app.use(app.router); 52 | app.use(express.static(__dirname + '/public')); 53 | }); 54 | 55 | app.configure('production', function() { 56 | app.use(express.errorHandler({ 57 | dumpExceptions : true, 58 | showStack : true 59 | })); 60 | }); 61 | 62 | app.configure('production', function() { 63 | app.use(express.errorHandler()); 64 | }); 65 | // Routes 66 | app.get('/', routes.index); 67 | app.get('/results', votes.votes); 68 | 69 | app.listen(3000); 70 | 71 | util.log(util.format("Express server listening on port %d in %s mode", 72 | app.address().port, app.settings.env)); 73 | } 74 | 75 | function startup() { 76 | if(cluster.isMaster) { 77 | numCPUs /=2; 78 | // TODO: Add command line to override whatever numCPUs is set to so we don't 79 | // use all the cores. 80 | util.log("Using CPUs: " + numCPUs); 81 | for(var i = 0; i < (numCPUs); i++) { 82 | cluster.fork(); 83 | } 84 | volt.initClient(false); 85 | webserverProcess(); 86 | } else { 87 | volt.initClient(true); 88 | } 89 | } 90 | 91 | startup(); 92 | -------------------------------------------------------------------------------- /test/cases/typestest.js: -------------------------------------------------------------------------------- 1 | /* This file is part of VoltDB. 2 | * Copyright (C) 2008-2018 VoltDB Inc. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 | * OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | // TODO: Remove a lot of the console/util logging statements being used for 25 | // debugging purposes. 26 | 27 | var VoltClient = require('../../lib/client'); 28 | var VoltConfiguration = require('../../lib/configuration'); 29 | var VoltProcedure = require('../../lib/query'); 30 | var VoltQuery = require('../../lib/query'); 31 | const debug = require("debug")("voltdb-client-nodejs:TypeTest"); 32 | 33 | var util = require('util'); 34 | const testContext = require("../util/test-context"); 35 | var testCase = require('nodeunit'); 36 | 37 | //Setup context 38 | testContext.setup(); 39 | 40 | var client = null; 41 | var initProc = new VoltProcedure('InitTestType', ['int']); 42 | 43 | function config() { 44 | var config = new VoltConfiguration(); 45 | config.host = 'localhost'; 46 | const voltPort = testContext.port(); 47 | config.port = voltPort; 48 | var configs = []; 49 | configs.push(config); 50 | return configs; 51 | } 52 | 53 | exports.typetest = { 54 | 55 | setUp : function(callback) { 56 | debug('typetest setup called'); 57 | client = new VoltClient(config()); 58 | client.connect(function startup(code, event, results) { 59 | debug('dasda connected'); 60 | callback(); 61 | }); 62 | }, 63 | tearDown : function(callback) { 64 | debug('typetest teardown called'); 65 | client.exit(); 66 | callback(); 67 | }, 68 | 'Init test' : function(test) { 69 | debug('init test'); 70 | test.expect(2); 71 | 72 | var initProc = new VoltProcedure('InitTestType', ['int']); 73 | var query = initProc.getQuery(); 74 | query.setParameters([0]); 75 | 76 | client.callProcedure(query, function read(code, event, results) { 77 | debug('results %o', results); 78 | test.equals(code, null , 'did I get called'); 79 | test.done(); 80 | }, function write(code, event, results) { 81 | test.equals(code, null, 'Write didn\'t had an error'); 82 | debug('write ok'); 83 | }); 84 | }, 85 | 'select test' : function(test) { 86 | debug('select test'); 87 | test.expect(11); 88 | 89 | var initProc = new VoltProcedure('TYPETEST.select', ['int']); 90 | var query = initProc.getQuery(); 91 | query.setParameters([0]); 92 | 93 | client.callProcedure(query, function read(code, event, results) { 94 | 95 | var testBuffer = new Buffer(4); 96 | debug('results inspection: %o', results.table[0][0].TEST_TIMESTAMP); 97 | debug('inspect %s', util.inspect(results.table[0][0])); 98 | 99 | test.equals(code, null, 'Invalid status: ' + results.status + 'should be 1'); 100 | 101 | test.equals(results.table[0][0].TEST_ID, 0, 'Wrong row ID, should be 0'); 102 | test.equals(results.table[0][0].TEST_TINY, 1, 'Wrong tiny, should be 1'); 103 | test.equals(results.table[0][0].TEST_SMALL, 2, 'Wrong small, should be 2'); 104 | test.equals(results.table[0][0].TEST_INTEGER, 3, 'Wrong integer, should be 3'); 105 | test.equals(results.table[0][0].TEST_BIG, 4, 'Wrong integer, should be 4'); 106 | test.equals(results.table[0][0].TEST_FLOAT, 5.1, 'Wrong float, should be 5.1'); 107 | test.equals(results.table[0][0].TEST_DECIMAL, 6.000342, 'Wrong decimal, should be 6.000342'); 108 | test.equals(results.table[0][0].TEST_VARCHAR, 'seven', 'Wrong varchar, should be seven'); 109 | // TODO: Add varbinary buffer comparison code. 110 | //test.equals(results.table[0][0].TEST_VARBINARY, 6.00034231, 111 | // results.table[0][0].TEST_VARBINARY); 112 | test.equals(results.table[0][0].TEST_TIMESTAMP.getTime(), (new Date(1331310436605)).getTime(), (new Date(1331310436605)).toString() + ": " + results.table[0][0].TEST_TIMESTAMP); 113 | 114 | test.done(); 115 | }, function write(code, event, results) { 116 | debug('write ok'); 117 | test.ok(true, 'Write didn\'t get called'); 118 | }); 119 | } 120 | }; 121 | -------------------------------------------------------------------------------- /lib/voltconstants.js: -------------------------------------------------------------------------------- 1 | /* This file is part of VoltDB. 2 | * Copyright (C) 2008-2018 VoltDB Inc. 3 | * 4 | * This file contains original code and/or modifications of original code. 5 | * Any modifications made by VoltDB Inc. are licensed under the following 6 | * terms and conditions: 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining 9 | * a copy of this software and associated documentation files (the 10 | * "Software"), to deal in the Software without restriction, including 11 | * without limitation the rights to use, copy, modify, merge, publish, 12 | * distribute, sublicense, and/or sell copies of the Software, and to 13 | * permit persons to whom the Software is furnished to do so, subject to 14 | * the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 23 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 24 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | */ 27 | 28 | /** 29 | * Message object constants used only when encoding/decoding a message 30 | * between the client and server. 31 | * 32 | */ 33 | var MESSAGE_TYPE = { 34 | UNDEFINED : -1, 35 | LOGIN : 1, 36 | QUERY : 2 37 | }; 38 | 39 | var PRESENT = { 40 | STATUS : 0x20, 41 | EXCEPTION : 0x40, 42 | APP_STATUS : 0x80 43 | }; 44 | 45 | 46 | /** 47 | * Top level event types for all operations. All events have at least the 48 | * SESSION_EVENT type and STATUS_CODE. 49 | * CONNECT: A successful connection to the volt server 50 | * CONNECTION_ERROR: Could not connect, see both the status code and 51 | * the event handler's message parameter. 52 | * QUERY_RESPONSE: Query executed and returned 53 | * QUERY_ALLOWED: Indicates that the application may execute another query. 54 | * Note that this prevents your application from flooding the database and 55 | * the application's code from blocking. 56 | * QUERY_RESPONSE_ERROR: The query was successfully dispatched but the 57 | * VoltDB server either had a critical fault or lost the connection. 58 | * QUERY_DISPATCH_ERROR: The client could not dispatch the query. 59 | * FATAL_ERROR: A critical error occurred that was above and beyond all 60 | * other error conditions. 61 | */ 62 | var SESSION_EVENT = { 63 | CONNECTION : 'CONNECT', 64 | CONNECTION_ERROR: 'CONNECT_ERROR', 65 | QUERY_RESPONSE: 'QUERY_RESPONSE', 66 | QUERY_ALLOWED: 'QUERY_ALLOWED', 67 | QUERY_RESPONSE_ERROR: 'QUERY_RESPONSE_ERROR', 68 | QUERY_DISPATCH_ERROR: 'QUERY_DISPATCH_ERROR', 69 | FATAL_ERROR: 'FATAL_ERROR' 70 | } 71 | 72 | /** 73 | * Each SESSION_EVENT has a STATUS_CODE giving some indication why the 74 | * operation failed. 75 | * 76 | * SUCCESS: Operation succeeded. 77 | * USER_ABORT: The user's stored procedure intentionally threw an exception 78 | * of type UserAbortException 79 | * GRACEFUL_FAILURE: Query had an error that rolled back the transaction. 80 | * UNEXPECTED_FAILURE: Query had an error, rolled back the transaction 81 | * and caused additional errors. 82 | * CONNECTION_LOST: The connection to VoltDB was lost before 83 | * the query returned. This is not issued by the server, but is issued 84 | * by the client.' 85 | * SERVER_UNAVAILABLE: Attempted to use an invalid connection. 86 | * CONNECTION_TIMEOUT: The server stopped replying. 87 | * QUERY_TIMEOUT: The server issues a message saying that the query took 88 | * too long to execute. 89 | * QUERY_TOOK_TOO_LONG: Driver issued message indicating that the server 90 | * has taken too long to respond. 91 | */ 92 | var STATUS_CODES = { 93 | SUCCESS : null, 94 | USER_ABORT : -1, 95 | GRACEFUL_FAILURE : -2, 96 | UNEXPECTED_FAILURE : -3, 97 | CONNECTION_LOST : -4, 98 | SERVER_UNAVAILABLE : -5, 99 | CONNECTION_TIMEOUT : -6, 100 | QUERY_TIMEOUT : -7, 101 | QUERY_TOOK_TOO_LONG : -8 102 | }; 103 | 104 | var STATUS_CODE_STRINGS = { 105 | 1 : 'SUCCESS', 106 | '-1' : 'USER_ABORT', 107 | '-2' : 'GRACEFUL_FAILURE', 108 | '-3' : 'UNEXPECTED_FAILURE', 109 | '-4' : 'CONNECTION_LOST', 110 | '-5' : 'SERVER_UNAVAILABLE', 111 | '-6' : 'CONNECTION_LOST', 112 | '-7' : 'QUERY_TIMEOUT', 113 | '-8' : 'QUERY_TOOK_TOO_LONG' 114 | }; 115 | 116 | var LOGIN_ERRORS = { 117 | 1 : 'Too many connections', 118 | 2 : 'Authentication failed, client took too long to transmit credentials', 119 | 3 : 'Corrupt or invalid login message' 120 | }; 121 | 122 | exports.PRESENT = PRESENT; 123 | exports.LOGIN_ERRORS = LOGIN_ERRORS; 124 | exports.SESSION_EVENT = SESSION_EVENT; 125 | exports.STATUS_CODE_STRINGS = STATUS_CODE_STRINGS; 126 | exports.STATUS_CODES = STATUS_CODES; 127 | exports.MESSAGE_TYPE = MESSAGE_TYPE; -------------------------------------------------------------------------------- /lib/message.js: -------------------------------------------------------------------------------- 1 | /* This file is part of VoltDB. 2 | * Copyright (C) 2008-2018 VoltDB Inc. 3 | * 4 | * This file contains original code and/or modifications of original code. 5 | * Any modifications made by VoltDB Inc. are licensed under the following 6 | * terms and conditions: 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining 9 | * a copy of this software and associated documentation files (the 10 | * "Software"), to deal in the Software without restriction, including 11 | * without limitation the rights to use, copy, modify, merge, publish, 12 | * distribute, sublicense, and/or sell copies of the Software, and to 13 | * permit persons to whom the Software is furnished to do so, subject to 14 | * the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 23 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 24 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | */ 27 | var Parser = require('./parser').Parser, 28 | util = require('util'), 29 | PRESENT = require('./voltconstants').PRESENT, 30 | MESSAGE_TYPE = require('./voltconstants').MESSAGE_TYPE, 31 | STATUS_CODE_STRINGS = require('./voltconstants').STATUS_CODE_STRINGS, 32 | STATUS_CODES = require('./voltconstants').STATUS_CODES; 33 | 34 | function Message(buffer) { 35 | this.type = MESSAGE_TYPE.UNDEFINED; 36 | this.error = false; 37 | Parser.call(this, buffer); 38 | if(!buffer) { 39 | this.writeInt(0); 40 | this.writeByte(0); 41 | } else { 42 | this.readHeader(); 43 | } 44 | } 45 | 46 | Message.prototype = Object.create(Parser.prototype); 47 | 48 | Message.prototype.readHeader = function() { 49 | this.length = this.readInt(); 50 | this.protocol = this.readByte(); 51 | }; 52 | 53 | Message.prototype.writeHeader = function() { 54 | var pos = this.position; 55 | this.position = 0; 56 | this.writeInt(this.buffer.length - 4); 57 | this.writeByte(0); 58 | this.position = pos; 59 | }; 60 | 61 | Message.prototype.toBuffer = function() { 62 | this.writeHeader(); 63 | return new Buffer(this.buffer); 64 | }; 65 | // for getting lengths from incoming data 66 | Message.readInt = function(buffer, offset) { 67 | return Parser.readInt(buffer, offset); 68 | }; 69 | LoginMessage = function(buffer) { 70 | Message.call(this, buffer); 71 | this.type = MESSAGE_TYPE.LOGIN; 72 | this.status = this.readByte(); 73 | this.error = (this.status === 0 ? false : true ); 74 | if(this.error === false) { 75 | this.serverId = this.readInt(); 76 | this.connectionId = this.readLong(); 77 | this.clusterStartTimestamp = new Date(parseInt(this.readLong().toString())); 78 | // not microseonds, milliseconds 79 | this.leaderIP = this.readByte() + '.' + this.readByte() + '.' + this.readByte() + '.' + this.readByte(); 80 | this.build = this.readString(); 81 | } 82 | } 83 | 84 | util.inherits(LoginMessage, Message); 85 | 86 | var lm = LoginMessage.prototype; 87 | lm.toString = function() { 88 | return { 89 | length : this.length, 90 | protocol : this.protocol, 91 | status : this.status, 92 | error : this.error, 93 | serverId : this.serverId, 94 | connectionId : this.connectionId, 95 | clusterStartTimestamp : this.clusterStartTimestamp, 96 | leaderIP : this.leaderIP, 97 | build : this.build 98 | }; 99 | } 100 | QueryMessage = function(buffer) { 101 | Message.call(this, buffer); 102 | this.type = MESSAGE_TYPE.QUERY; 103 | 104 | this.uid = this.readBinary(8).toString(); 105 | this.fieldsPresent = this.readByte(); 106 | // bitfield, use PRESENT values to check 107 | this.status = this.readByte(); 108 | this.statusString = STATUS_CODE_STRINGS[this.status]; 109 | if(this.fieldsPresent & PRESENT.STATUS) { 110 | this.statusString = this.readString(); 111 | } 112 | this.appStatus = this.readByte(); 113 | this.appStatusString = ''; 114 | if(this.fieldsPresent & PRESENT.APP_STATUS) { 115 | this.appStatusString = this.readString(); 116 | } 117 | this.exception 118 | this.exceptionLength = this.readInt(); 119 | if(this.fieldsPresent & PRESENT.EXCEPTION) { 120 | this.exception = this.readException(1); 121 | // seems size doesn't matter, always 1 122 | } else { 123 | // don't parse the rest if there was an exception. Bad material there. 124 | var resultCount = this.readShort(); 125 | // there can be more than one table with rows 126 | this.table = new Array(resultCount); 127 | for(var i = 0; i < resultCount; i++) { 128 | this.table[i] = this.readVoltTable(); 129 | } 130 | 131 | } 132 | } 133 | 134 | util.inherits(QueryMessage, Message); 135 | 136 | var qm = QueryMessage.prototype; 137 | 138 | qm.toString = function() { 139 | return { 140 | length : this.length, 141 | protocol : this.protocol, 142 | status : this.status, 143 | error : this.error, 144 | uid : this.uid, 145 | fieldsPresent : this.fieldsPresent, 146 | status : this.status, 147 | statusString : this.statusString, 148 | appStatus : this.appStatus, 149 | appStatusString : this.appStatusString, 150 | exception : this.exception, 151 | exceptionLength : this.exceptionLength, 152 | results : this.results 153 | }; 154 | } 155 | exports.Message = Message; 156 | exports.LoginMessage = LoginMessage; 157 | exports.QueryMessage = QueryMessage; 158 | -------------------------------------------------------------------------------- /writer.js: -------------------------------------------------------------------------------- 1 | /* This file is part of VoltDB. 2 | * Copyright (C) 2008-2018 VoltDB Inc. 3 | * 4 | * This file contains original code and/or modifications of original code. 5 | * Any modifications made by VoltDB Inc. are licensed under the following 6 | * terms and conditions: 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining 9 | * a copy of this software and associated documentation files (the 10 | * "Software"), to deal in the Software without restriction, including 11 | * without limitation the rights to use, copy, modify, merge, publish, 12 | * distribute, sublicense, and/or sell copies of the Software, and to 13 | * permit persons to whom the Software is furnished to do so, subject to 14 | * the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 23 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 24 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | */ 27 | 28 | /* 29 | * Authors: 30 | * Andy Wilson [www.voltdb.com] 31 | * Henning Diedrich [www.eonblast.com] 32 | * 33 | */ 34 | 35 | var os = require('os') 36 | var cli = require('cli'); 37 | var util = require('util'); 38 | var cluster = require('cluster'); 39 | var VoltClient = require('./lib/client'); 40 | var VoltProcedure = require('./lib/query'); 41 | var VoltQuery = require('./lib/query'); 42 | 43 | var numCPUs = os.cpus().length 44 | var logTag = "master " 45 | 46 | var client = null; 47 | var resultsProc = new VoltProcedure('Results'); 48 | var initProc = new VoltProcedure('Initialize', ['int', 'string']); 49 | var writeProc = new VoltProcedure('Vote', ['long', 'int', 'long']); 50 | var throughput = 0; 51 | 52 | var options = cli.parse({ 53 | loops : ['c', 'Number of loops to run', 'number', 10000], 54 | voltGate : ['h', 'VoltDB host (any if multi-node)', 'string', 'localhost'], 55 | workers : ['f', 'client worker forks', 'number', numCPUs], 56 | verbose : ['v', 'verbose output'], 57 | debug : ['d', 'debug output'] 58 | }); 59 | 60 | var workers = options.workers; 61 | var vlog = options.verbose || options.debug ? log : function() { 62 | } 63 | var vvlog = options.debug ? log : function() { 64 | } 65 | var cargos = 'a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z'; 66 | 67 | if(cluster.isMaster) 68 | master_main(); 69 | else 70 | worker_main(); 71 | 72 | function master_main() { 73 | 74 | log("-- Crude Forking Write Benchmark Client --") 75 | 76 | log("VoltDB host: " + options.voltGate); 77 | log("worker forks: " + workers); 78 | 79 | // fork workers 80 | for(var i = 0; i < workers; i++) { 81 | vvlog('forking worker #' + i) 82 | var worker = cluster.fork() 83 | 84 | // result counter 85 | worker.on('message', function(msg) { 86 | if(msg.cmd && msg.cmd == 'result') { 87 | throughput += msg.throughput 88 | } 89 | }); 90 | } 91 | 92 | // track exits, print total 93 | var exited = 0; 94 | cluster.on('death', function(worker) { 95 | vlog('worker (pid ' + worker.pid + ') exits.') 96 | exited++; 97 | if(exited == workers) { 98 | log("Total: " + Math.round(throughput) + " TPS") 99 | } 100 | }) 101 | } 102 | 103 | function worker_main() { 104 | logTag = 'worker ' + process.env.NODE_WORKER_ID 105 | vvlog('worker main') 106 | 107 | // define and start a Volt client 108 | client = new VoltClient([{ 109 | host : options.voltGate, 110 | port : 21212, 111 | username : 'user', 112 | password : 'password', 113 | service : 'database', 114 | queryTimeout : 50000 115 | 116 | }]); 117 | client.connect(function startup(results) { 118 | vvlog('Node up'); 119 | voltInit(); 120 | }, function loginError(results) { 121 | vvlog('Node up (on Error)'); 122 | voltInit(); 123 | }); 124 | vvlog('connected') 125 | } 126 | 127 | function voltInit() { 128 | vvlog('voltInit'); 129 | var query = initProc.getQuery(); 130 | query.setParameters([cargos.length, cargos]); 131 | client.call(query, function initWriter(results) { 132 | var job = { 133 | loops : options.loops, 134 | steps : getSteps() 135 | }; 136 | step(job); 137 | }); 138 | } 139 | 140 | function getSteps() { 141 | var steps = []; 142 | steps.push(writeResults); 143 | steps.push(writeInsertLoop); 144 | steps.push(writeResults); 145 | steps.push(writeEnd); 146 | return steps; 147 | } 148 | 149 | function writeResults(job) { 150 | vvlog('writeResults'); 151 | var query = resultsProc.getQuery(); 152 | client.call(query, function displayResults(results) { 153 | var mytotalwrites = 0; 154 | var msg = ''; 155 | var longestString = 0; 156 | var rows = results.table[0]; 157 | for(var i = 0; i < rows.length; i++) { 158 | mytotalwrites += rows[i].TOTAL_VOTES; 159 | msg += util.format("%s\t%d", rows[i].CONTESTANT_NUMBER, rows[i].TOTAL_VOTES); 160 | } 161 | // msg += util.format("%d writes", mytotalwrites); 162 | // log(msg); 163 | step(job); 164 | }); 165 | } 166 | 167 | function connectionStats() { 168 | client.connectionStats(); 169 | } 170 | 171 | function writeEnd(job) { 172 | client.connectionStats(); 173 | vvlog('writeEnd'); 174 | process.exit(); 175 | } 176 | 177 | function getCandidate() { 178 | return Math.floor(Math.random() * cargos.length) + 1; 179 | } 180 | 181 | function writeInsertLoop(job) { 182 | 183 | var index = 0; 184 | var reads = job.loops; 185 | var startTime = new Date().getTime(); 186 | var chunkTime = new Date().getTime(); 187 | var doneWith = 0; 188 | 189 | var innerLoop = function() { 190 | var query = writeProc.getQuery(); 191 | if(index < job.loops) { 192 | 193 | query.setParameters([getRand(1E6), getCandidate(), 200000]); 194 | client.call(query, function displayResults(results) { 195 | vvlog("reads ", reads); 196 | reads--; 197 | if(reads == 0) { 198 | logTime(startTime, job.loops, "Results"); 199 | step(job); 200 | } else { 201 | vvlog("reads ", reads); 202 | } 203 | }, function readyToWrite() { 204 | 205 | if(index < job.loops) { 206 | if(index && index % 5000 == 0) { 207 | vlog('Executed ' + index + ' writes in ' + ((new Date().getTime()) - chunkTime) + 'ms ' + util.inspect(process.memoryUsage())); 208 | chunkTime = new Date().getTime(); 209 | } 210 | index++; 211 | process.nextTick(innerLoop); 212 | } else { 213 | log(doneWith++, 'Time to stop voting: ', index); 214 | } 215 | }); 216 | } else { 217 | vvlog('Index is: ' + index + ' and ' + job.loops); 218 | } 219 | }; 220 | // void stack, yield 221 | process.nextTick(innerLoop); 222 | 223 | } 224 | 225 | function logTime(startTime, writes, typeString) { 226 | 227 | var endTimeMS = Math.max(1, new Date().getTime() - startTime); 228 | var throughput = writes * 1000 / endTimeMS; 229 | 230 | log(util.format('%d transactions in %d milliseconds.\n' + '%d transactions per second', writes, endTimeMS, throughput)); 231 | 232 | process.send({ 233 | cmd : 'result', 234 | throughput : throughput 235 | }); 236 | } 237 | 238 | function step(job) { 239 | 240 | if(job.steps.length > 0) { 241 | var method = job.steps.shift(); 242 | method(job); 243 | } 244 | } 245 | 246 | function log(tx) { 247 | tx = tx.replace(/\n/g, "\n" + logTag + ": "); 248 | console.log(logTag + ": " + tx); 249 | } 250 | 251 | function getRand(max) { 252 | return Math.floor(Math.random() * max); 253 | } -------------------------------------------------------------------------------- /lib/client.js: -------------------------------------------------------------------------------- 1 | /* This file is part of VoltDB. 2 | * Copyright (C) 2008-2018 VoltDB Inc. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 | * OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | var EventEmitter = require('events').EventEmitter, 25 | util = require('util'), 26 | VoltConnection = require('./connection'), 27 | VoltConstants = require('./voltconstants'); 28 | const debug = require("debug")("voltdb-client-nodejs:VoltClient"); 29 | 30 | VoltClient = function(configuration) { 31 | EventEmitter.call(this); 32 | 33 | this.config = configuration; 34 | 35 | this.connect = this.connect.bind(this); 36 | this._getConnections = this.connect.bind(this); 37 | this.call = this.call.bind(this); 38 | this.callProcedure = this.callProcedure.bind(this); 39 | this.exit = this.exit.bind(this); 40 | this.connectionStats = this.connectionStats.bind(this); 41 | 42 | this._connectListener = this._connectListener.bind(this); 43 | this._connectErrorListener=this._connectErrorListener.bind(this); 44 | this._queryResponseListener=this._queryResponseListener.bind(this); 45 | this._queryResponseErrorListener=this._queryResponseErrorListener.bind(this); 46 | this._queryDispatchErrorListener=this._queryDispatchErrorListener.bind(this); 47 | this._fatalErrorListener=this._fatalErrorListener.bind(this); 48 | 49 | this._connections = []; 50 | this._badConnections = []; 51 | this._connectionCounter = 0; 52 | } 53 | 54 | util.inherits(VoltClient, EventEmitter); 55 | 56 | VoltClient.prototype.connect = function(callback) { 57 | var self = this; 58 | 59 | var connectionCount = this.config.length; 60 | var connectionResults = []; 61 | for(var index = 0; index < this.config.length; index++) { 62 | var con = new VoltConnection(this.config[index]); 63 | 64 | con.on(VoltConstants.SESSION_EVENT.CONNECTION,this._connectListener); 65 | con.on(VoltConstants.SESSION_EVENT.CONNECTION, callback); 66 | con.on(VoltConstants.SESSION_EVENT.CONNECTION_ERROR, callback); 67 | con.on(VoltConstants.SESSION_EVENT.QUERY_RESPONSE,this._queryResponseListener); 68 | con.on(VoltConstants.SESSION_EVENT.QUERY_RESPONSE_ERROR,this._queryResponseErrorListener); 69 | con.on(VoltConstants.SESSION_EVENT.QUERY_DISPATCH_ERROR,this._queryDispatchErrorListener); 70 | con.on(VoltConstants.SESSION_EVENT.FATAL_ERROR,this._fatalErrorListener); 71 | 72 | /** 73 | * Need to register the connection even before the socket connects otherwise 74 | * it can't be torn down in the event of a socket failure. 75 | */ 76 | this._connections.push(con); 77 | 78 | con.connect(); 79 | } 80 | } 81 | 82 | VoltClient.prototype._getConnection = function(callback) { 83 | var length = this._connections.length; 84 | var connection = null; 85 | for(var index = 0; index < length; index++) { 86 | 87 | // creates round robin 88 | this._connectionCounter++; 89 | if(this._connectionCounter >= length) { 90 | this._connectionCounter = 0; 91 | } 92 | connection = this._connections[this._connectionCounter]; 93 | // validates that the connection is good and not blocked on reads 94 | if(connection == null || connection.isValidConnection() == false) { 95 | this._badConnections.push(connection); 96 | this._connections[this._connectionCounter] = null; 97 | } else if(connection.isBlocked() == false) { 98 | break; 99 | } 100 | } 101 | return connection; 102 | } 103 | 104 | VoltClient.prototype.callProcedure = function(query, readCallback, writeCallback) { 105 | var con = this._getConnection(); 106 | if(con) { 107 | if(con.callProcedure(query, readCallback, writeCallback) == false) { 108 | this.emit(VoltConstants.SESSION_EVENT.CONNECTION_ERROR, 109 | VoltConstants.STATUS_CODES.CONNECTION_TIMEOUT, 110 | VoltConstants.SESSION_EVENT.CONNECTION_ERROR, 111 | 'Invalid connection in connection pool'); 112 | } 113 | 114 | } else { 115 | this.emit(VoltConstants.SESSION_EVENT.CONNECTION_ERROR, 116 | VoltConstants.STATUS_CODES.CONNECTION_TIMEOUT, 117 | VoltConstants.SESSION_EVENT.CONNECTION_ERROR, 118 | 'No valid VoltDB connections, verify that the database is online'); 119 | } 120 | } 121 | 122 | VoltClient.prototype.call = function(query, readCallback, writeCallback) { 123 | this.callProcedure(query, readCallback, writeCallback); 124 | } 125 | 126 | VoltClient.prototype.exit = function(callback) { 127 | 128 | debug("Exiting | Connections Length: %o", this._connections.length); 129 | 130 | while(this._connections.length > 0){ 131 | var c = this._connections[0]; 132 | c.close(); 133 | this._connections.splice(0, 1); 134 | } 135 | 136 | if(callback) callback(); 137 | } 138 | 139 | VoltClient.prototype.connectionStats = function() { 140 | util.log('Good connections:'); 141 | this._displayConnectionArrayStats(this._connections); 142 | 143 | util.log('Bad connections:'); 144 | this._displayConnectionArrayStats(this._badConnections); 145 | } 146 | 147 | VoltClient.prototype._displayConnectionArrayStats = function(array) { 148 | for(var index = 0; index < array.length; index++) { 149 | var connection = array[index]; 150 | if(connection != null) { 151 | util.log('Connection: ', 152 | connection.config.host, ': ', 153 | connection.invocations, ' Alive: ', 154 | connection.isValidConnection()); 155 | } 156 | } 157 | } 158 | 159 | /** 160 | * TODO: Not sure why SUCCESS can be both null and 1. Will leave it as is until 161 | * I know why and just brute force map the null to 1 to get it as a String. 162 | */ 163 | function statusCodeToString(code){ 164 | return code === null ? VoltConstants.STATUS_CODE_STRINGS[1] : VoltConstants.STATUS_CODE_STRINGS[code]; 165 | } 166 | 167 | VoltClient.prototype._connectListener = function(code, event, connection) { 168 | 169 | debug("Connected | Code: %o, Event: %o", statusCodeToString(code), event); 170 | 171 | this.emit(VoltConstants.SESSION_EVENT.CONNECTION, 172 | code, 173 | event, 174 | connection.config.host); 175 | } 176 | 177 | VoltClient.prototype._connectErrorListener = function(code, event, message) { 178 | this.emit(VoltConstants.SESSION_EVENT.CONNECTION_ERROR, 179 | code, 180 | event, 181 | message); 182 | } 183 | 184 | VoltClient.prototype._queryResponseListener = function(code,event, message) { 185 | this.emit(VoltConstants.SESSION_EVENT.QUERY_RESPONSE, 186 | code, 187 | event, 188 | message); 189 | } 190 | 191 | VoltClient.prototype._queryResponseErrorListener = function(code, event, message) { 192 | this.emit(VoltConstants.SESSION_EVENT.QUERY_RESPONSE_ERROR, 193 | code, 194 | event, 195 | message); 196 | } 197 | 198 | VoltClient.prototype._queryDispatchErrorListener = function(code, event, message) { 199 | this.emit(VoltConstants.SESSION_EVENT.QUERY_DISPATCH_ERROR, 200 | code, 201 | event, 202 | message); 203 | } 204 | 205 | VoltClient.prototype._fatalErrorListener = function(code, event, message) { 206 | this.emit(VoltConstants.SESSION_EVENT.FATAL_ERROR, 207 | code, 208 | event, 209 | message); 210 | } 211 | 212 | module.exports = VoltClient; 213 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | VoltDB NodeJS Wire Protocol Driver 2.0 2 | ====================================== 3 | 4 | Requirements 5 | ============ 6 | 7 | Node.js 6.11.0 or later 8 | VoltDB 7.5 or later 9 | 10 | 11 | Installation 12 | ============ 13 | Run NPM to install all the dependencies for the driver itself. The example application does not automatically install its dependencies, so do `npm install` in both the directory where the node.js driver is installed and the example/voter/voter folder as well. 14 | 15 | npm install 16 | 17 | Introduction 18 | ============ 19 | The VoltDB is a high throughput, ACID compliant database that works best when using an asynchronous client. This wire driver runs in asynchronous mode only. 20 | 21 | Please see the documentation for administering and using VoltDB on the [VoltDB community page][1] 22 | 23 | 24 | Example Application 25 | =================== 26 | 27 | The example uses the voter example server included in the VoltDB distribution. It is found in `VOLTDB_HOME/examples/voter`. 28 | 29 | 1\. Start the server 30 | 31 | ``` 32 | ./run.sh server 33 | ``` 34 | 35 | 2\. In a second terminal, run the sample 36 | 37 | ``` 38 | cd ./examples/voter/voter 39 | node ./app.js 40 | ``` 41 | 42 | 3\. Open a browser and connect to localhost:3000 and watch the voting results. 43 | 44 | The example include detailed comments about how the application runs in the app.js and volt.js files. 45 | 46 | You may want to set the number of VoltDB *sites per host* to something interesting in `VOLTDB_HOME/examples/voter/deployment.xml`. The 47 | *sites per host* value is equivalent to the number of cores that VoltDB will use for the server when you are running a single instance on localhost. 48 | 49 | 50 | Driver Modules 51 | ============== 52 | ---------- 53 | 54 | 55 | VoltClient 56 | ========== 57 | This provides the interface for connecting to the VoltDB server, managing event processing and executing stored procedures. 58 | 59 | Methods 60 | ----------- 61 | VoltClient(configurationArray) 62 | ------------------------- 63 | 64 | - configurationArray: A collection of configurations for connecting to and managing all of the nodes within the VoltDB cluster. 65 | 66 | Creates an instance of the client and sets internal properties, but does not perform the actual connection operation. 67 | 68 | VoltClient.connect(callback) 69 | ---------------------------- 70 | 71 | - callback: A handler that gets the success and failure statuses for each of the servers specified in the `configurationArray` in the `VoltClient` 72 | 73 | Attempts to connect to each of the VoltDB server configurations passed into the constructor. The callback will be invoked for each connection. Connections may fail and the callback will receive error information. See the **Callbacks** section for callback parameter information. 74 | 75 | VoltClient.callProcedure(query, readCallback, writeCallback) 76 | ------------------------------------------------------------ 77 | - query: An instance of a `VoltQuery` object. 78 | - readCallback: callback that receives the results of a query. 79 | - writeCallback: callback that is invoked when the client driver is able to write again to the driver. 80 | 81 | This function manages all queries to the VoltDB server. This includes whether it is safe to allow for more writes, encoding of query parameters and management of the outgoing query queue. 82 | 83 | Calling this function in a loop is generally discouraged since it can lead to blocking socket reads. The VoltDB server will drop a connection if data lingers on the socket for too long. The `writeCallback` handler is mechanism for avoiding that particular problem is it will get invoked each time the driver is able to issue another query. 84 | 85 | See the **Callbacks** section for callback parameter information. 86 | 87 | VoltConfiguration 88 | ================= 89 | Create a VoltConfiguration object for each server within a VoltDB cluster. Pass this configuration into the `VoltClient(configurationArray)` constructor. 90 | 91 | 92 | - VoltConfiguration.host: The server name 93 | - VoltConfiguration.port: The server port (optional) 94 | - VoltConfiguration.username: Username if not using the default. (optional) 95 | - VoltConfiguration.password: Password if not using the default. (optional) 96 | - VoltConfiguration.service: Service if not using the default. (optional) 97 | - VoltConfiguration.queryTimeout: How long a query should be allowed to be pending before the driver issues an error message. (optional) 98 | - VoltConfiguration.queryTimeoutInterval: The interval for checking for queries that have timed out. Also cleans out the queue of processed queries. (optional) 99 | - VoltConfiguration.flushInterval: How long to wait before flushing the query queue. (optional) 100 | - VoltConfiguration.messageQueueSize: Maximum number of messages to place in the driver's internal message queue before writing them to the outgoing socket. The default value is set to 10 and it is not recommended to create queue much larger as the gains become less significant as the queue grows. (optional) 101 | - VoltConfiguration.maxConsecutiveWrites: The driver will only allow this number of writes to the outgoing queue and socket before requiring a read operation. This is necessary to ensure that read operations are not blocked. The default value is 5000 queries. If you find that connections between the client and server are being dropped by the server, then lower this value to ensure more frequent reads. (optional) 102 | 103 | VoltProcedure 104 | ============= 105 | Defines a template for a query, which includes the name of the procedure and the parameter types that can be passed to the query. 106 | 107 | Methods 108 | ------- 109 | VoltProcedure(name, types) 110 | -------------------------- 111 | 112 | - name: The name of the stored procedure. 113 | - types: An array of types specified as a set of strings. 114 | 115 | **Example** 116 | ``` 117 | var resultsProc = new VoltProcedure('Results'); 118 | var initProc = new VoltProcedure('Initialize', ['int', 'string']); 119 | var voteProc = new VoltProcedure('Vote', ['long', 'int', 'long']); 120 | ``` 121 | 122 | A complete list of types is specified in the **Data Types** section 123 | 124 | 125 | VoltProcedure.getQuery() 126 | -------------------------------- 127 | Returns an instance of a VoltQuery. 128 | 129 | VoltQuery 130 | ========= 131 | VoltQuery is necessary for query invocation, but the application developer does very little with it. A VoltQuery is produced by the `VoltProcedure.getQuery()` function. That object is then passed to the `VoltClient.callProcedure`function. 132 | 133 | VoltQuery(procName, types) 134 | -------------------------- 135 | Called only by the VoltProcedure object. 136 | 137 | 138 | Callbacks 139 | ========= 140 | --------- 141 | All callbacks take follow the same structure: 142 | (errorCode, eventCode, result) 143 | - errorCode: Will be set to null if successful or a numeric value if an error occurred. See **Error Codes** for details on each error code. 144 | 145 | **Example 1: Using status codes** 146 | ``` 147 | client.callProcedure(query, displayResults(errorCode, eventCode, results) { 148 | if(errorCode == VoltConstants.STATUS_CODES.SUCCESS) { 149 | // Success! 150 | } else { 151 | // Error handling 152 | } 153 | }); 154 | ``` 155 | 156 | 157 | **Example 2: No status codes** 158 | ``` 159 | client.callProcedure(query, displayResults(errorCode, eventCode, results) { 160 | if(errorCode) { 161 | // Error Handling 162 | } else { 163 | // Success! 164 | } 165 | }); 166 | ``` 167 | 168 | 169 | Events 170 | ====== 171 | ---------- 172 | 173 | SESSION\_EVENT 174 | -------------- 175 | - SESSION\_EVENT.CONNECTION: A successful connection to the volt server 176 | - SESSION\_EVENT.CONNECTION\_ERROR: Could not connect, see both the status code and the event handler's message parameter. 177 | - SESSION\_EVENT.QUERY\_RESPONSE: Query executed and returned. 178 | - SESSION\_EVENT.QUERY\_ALLOWED: Indicates that the application may execute another query Note that this prevents your application from flooding the database and the application's code from blocking. 179 | - SESSION\_EVENT.QUERY\_RESPONSE\_ERROR:The query was successfully dispatched but theVoltDB server either had a critical fault or dropped the connection. 180 | - SESSION\_EVENT.QUERY\_DISPATCH\_ERROR: The client could not dispatch the query. 181 | - SESSION\_EVENT.FATAL\_ERROR: A critical error occurred that was above and beyond all other error conditions. 182 | 183 | Error Codes 184 | =========== 185 | ---------- 186 | 187 | STATUS\_CODES 188 | ============ 189 | - STATUS\_CODES.SUCCESS: Operation succeeded. Note that the value of this constant is null. 190 | - STATUS\_CODES.USER\_ABORT: The user's stored procedure intentionally threw an exception of type `UserAbortException`. 191 | - STATUS\_CODES.GRACEFUL\_FAILURE: Query had an error that rolled back the transaction. 192 | - STATUS\_CODES.UNEXPECTED\_FAILURE: Query had an error, rolled back the transaction and caused additional errors. 193 | - STATUS\_CODES.CONNECTION\_LOST: The connection to VoltDB was lost before the query returned. This is not issued by the server, but is issued by the client. 194 | - STATUS\_CODES.SERVER\_UNAVAILABLE: Attempted to use an invalid connection. 195 | - STATUS\_CODES.CONNECTION\_TIMEOUT: The server stopped replying. 196 | - STATUS\_CODES.QUERY\_TIMEOUT: The server issues a message saying that the query took too long to execute. 197 | - STATUS\_CODES.QUERY\_TOOK\_TOO\_LONG: Driver issued message indicating that the server has taken too long to respond. 198 | 199 | Data Types 200 | =========== 201 | ---------- 202 | These data types are not JavaScript data types. The driver uses these type specifiers to encode a JavaScript type into a VoltDB type. 203 | - null: Use `null` 204 | - byte: Use a number, not a string 205 | - tinyint: Use a number, not a string 206 | - short: Use a number, not a string 207 | - smallint: Use a number, not a string 208 | - int: Use a number, not a string 209 | - integer: Use a number, not a string 210 | - long: Use a number, not a string 211 | - bigint: Use a number, not a string 212 | - double: Use a number, not a string 213 | - float: Use a number, not a string 214 | - string: Use a string 215 | - date: Use a number, not a string 216 | - timestamp: Use a number, not a string 217 | - decimal: Use a number, not a string 218 | - varbinary: Use a `Buffer` 219 | 220 | 221 | 222 | [1]: http://community.voltdb.com/ 223 | -------------------------------------------------------------------------------- /test/cases/bufferTest.js: -------------------------------------------------------------------------------- 1 | !(function (global) { // eslint-disable-line no-unused-vars 2 | 3 | "use strict"; 4 | 5 | const VoltClient = require("../../lib/client"); 6 | const VoltConfiguration = require("../../lib/configuration"); 7 | const VoltConstants = require("../../lib/voltconstants"); 8 | const VoltProcedure = require("../../lib/query"); 9 | 10 | 11 | require("nodeunit"); 12 | const testContext = require("../util/test-context"); 13 | const debug = require("debug")("voltdb-client-nodejs:BufferTest"); 14 | 15 | //Setup context 16 | testContext.setup(); 17 | 18 | /** 19 | * A "good" client config that points to a volt instance on localhost 20 | */ 21 | function configs() { 22 | 23 | const configs = []; 24 | 25 | const config = new VoltConfiguration(); 26 | config.host = "localhost"; 27 | config.port = testContext.port(); 28 | 29 | configs.push(config); 30 | 31 | return configs; 32 | } 33 | 34 | /** 35 | * Promise style function for connecting to a Volt instance 36 | */ 37 | function connect(client){ 38 | 39 | const p = new Promise(function(resolve, reject) { 40 | client.connect(function(code, event, results) { 41 | if(code === VoltConstants.STATUS_CODES.SUCCESS){ 42 | resolve({errorCode: code, eventCode: event, results: results}); 43 | } 44 | else{ 45 | debug("Connect Failure | Code: %o, Event: %o", code, event); 46 | reject({errorCode: code, eventCode: event, results: results}); 47 | } 48 | }); 49 | }); 50 | 51 | return p; 52 | } 53 | 54 | /** 55 | * Promise style function for calling a procedure. An alternative to the old 56 | * callback style functions that returns both a write and a read promise. 57 | */ 58 | function query(query, client){ 59 | 60 | var writeResolve = null; 61 | var writeReject = null; 62 | 63 | const writePromise = new Promise(function(resolve, reject){ 64 | writeResolve = resolve; 65 | writeReject = reject; 66 | }); 67 | 68 | const readPromise = new Promise(function(resolve, reject){ 69 | 70 | client.callProcedure(query, function read(code, event, results) { 71 | // debug("AdHocQuery Complete | errorCode: %o, eventCode: %o, results: 72 | // %O", code, event, results); 73 | if(code === VoltConstants.STATUS_CODES.SUCCESS){ 74 | // The results code is 1 for SUCCESS so can't use voltconstants 75 | if(results.status === PROC_STATUS_CODE_SUCCESS){ 76 | resolve({errorCode: code, eventCode: event, results: results}); 77 | } 78 | else{ 79 | debug("AdHocQuery Failure | Read Error. errorCode: %o, eventCode: %o, results: %O", statusCodeToString(code), event, results); 80 | reject({errorCode: code, eventCode: event, results: results}); 81 | } 82 | } 83 | else{ 84 | debug("AdHocQuery Failure | Read Error. errorCode: %o, eventCode: %o, results: %O", statusCodeToString(code), event, results); 85 | reject({errorCode: code, eventCode: event, results: results}); 86 | } 87 | 88 | }, function write(code, event, results) { 89 | if(code === VoltConstants.STATUS_CODES.SUCCESS){ 90 | writeResolve({errorCode: code, eventCode: event, results: results}); 91 | } 92 | else{ 93 | debug("AdHocQuery Failure | Write Error. errorCode: %o, eventCode: %o, results: %O", statusCodeToString(code), event, results); 94 | writeReject({errorCode: code, eventCode: event, results: results}); 95 | } 96 | }); 97 | }); 98 | 99 | return { writePromise: writePromise, readPromise: readPromise }; 100 | } 101 | 102 | /** 103 | * Sugar for running an adhoc query 104 | */ 105 | function adHocQuery(queryString, client){ 106 | 107 | debug("Query | query: %o", queryString); 108 | 109 | const p = new VoltProcedure("@AdHoc", [ "string" ]); 110 | 111 | const q = p.getQuery(); 112 | q.setParameters([queryString]); 113 | 114 | return query(q, client); 115 | } 116 | 117 | /** 118 | * 119 | */ 120 | function statusCodeToString(code){ 121 | return code === null ? VoltConstants.STATUS_CODE_STRINGS[PROC_STATUS_CODE_SUCCESS] : VoltConstants.STATUS_CODE_STRINGS[code]; 122 | } 123 | 124 | /** 125 | * Utility method for volt queries that return a write and a read promise. 126 | * Useful for when you want to fire off a bunch of writes and then wait on the 127 | * read at the end. Executes the query and collects the read promises in the 128 | * given array. Returns both the write and read promise. 129 | */ 130 | function queryCollect(queryString, readPromises, client){ 131 | const p = adHocQuery(queryString, client); 132 | readPromises.push(p.readPromise); 133 | return p; 134 | } 135 | 136 | /** 137 | * 138 | */ 139 | const PROC_STATUS_CODE_SUCCESS = 1; 140 | 141 | // Exports 142 | module.exports = { 143 | setUp : function(callback){ 144 | callback(); 145 | }, 146 | tearDown : function(callback){ 147 | callback(); 148 | }, 149 | readTest : function(test){ 150 | 151 | debug("readTest"); 152 | 153 | const client = new VoltClient(configs()); 154 | 155 | debug("Connecting"); 156 | 157 | connect(client) 158 | .then(function(value){ 159 | test.ok(value.errorCode === VoltConstants.STATUS_CODES.SUCCESS); 160 | return Promise.resolve(null); 161 | }) 162 | .then(function(){ 163 | return adHocQuery("DROP TABLE PLAYERS IF EXISTS;", client).readPromise; 164 | }) 165 | .then(function(){ 166 | return adHocQuery("CREATE TABLE PLAYERS (" + 167 | "playerID integer NOT NULL, " + 168 | "teamid varchar(100) NOT NULL " + 169 | ");", client).readPromise; 170 | }) 171 | .then(function(){ 172 | return adHocQuery("DROP TABLE TEAM_PLAYERS IF EXISTS;", client).readPromise; 173 | }) 174 | .then(function(){ 175 | return adHocQuery("CREATE TABLE TEAM_PLAYERS (" + 176 | "id integer NOT NULL, " + 177 | "uid varchar(100) NOT NULL, " + 178 | "name varchar(100) NOT NULL, " + 179 | "avatar varbinary(12000) NOT NULL" + 180 | ");", client).readPromise; 181 | }) 182 | .then(function(){ 183 | 184 | const readPromises = []; 185 | 186 | return Promise.resolve() 187 | .then(function() { return queryCollect("INSERT INTO PLAYERS VALUES (0, 'TeamA');", readPromises, client).writePromise; }) 188 | .then(function() { return queryCollect("INSERT INTO PLAYERS VALUES (1, 'TeamA');", readPromises, client).writePromise; }) 189 | .then(function() { return queryCollect("INSERT INTO PLAYERS VALUES (2, 'TeamA');", readPromises, client).writePromise; }) 190 | .then(function() { return queryCollect("INSERT INTO PLAYERS VALUES (3, 'TeamA');", readPromises, client).writePromise; }) 191 | .then(function() { return queryCollect("INSERT INTO PLAYERS VALUES (4, 'TeamA');", readPromises, client).writePromise; }) 192 | .then(function() { return queryCollect("INSERT INTO PLAYERS VALUES (5, 'TeamB');", readPromises, client).writePromise; }) 193 | .then(function() { return queryCollect("INSERT INTO PLAYERS VALUES (6, 'TeamB');", readPromises, client).writePromise; }) 194 | .then(function() { return queryCollect("INSERT INTO PLAYERS VALUES (7, 'TeamB');", readPromises, client).writePromise; }) 195 | .then(function() { return queryCollect("INSERT INTO PLAYERS VALUES (8, 'TeamB');", readPromises, client).writePromise; }) 196 | .then(function() { return queryCollect("INSERT INTO PLAYERS VALUES (9, 'TeamB');", readPromises, client).writePromise; }) 197 | .then(function() { return Promise.all(readPromises); }); 198 | 199 | }) 200 | .then(function(){ 201 | 202 | const readPromises = []; 203 | 204 | return Promise.resolve() 205 | .then(function() { return queryCollect("INSERT INTO TEAM_PLAYERS VALUES (0, 'GameA', 'TeamA', 'ABCDEF');", readPromises, client).writePromise; }) 206 | .then(function() { return queryCollect("INSERT INTO TEAM_PLAYERS VALUES (1, 'GameB', 'TeamA', 'ABCDEF');", readPromises, client).writePromise; }) 207 | .then(function() { return queryCollect("INSERT INTO TEAM_PLAYERS VALUES (2, 'GameC', 'TeamA', 'ABCDEF');", readPromises, client).writePromise; }) 208 | .then(function() { return queryCollect("INSERT INTO TEAM_PLAYERS VALUES (3, 'GameD', 'TeamA', 'ABCDEF');", readPromises, client).writePromise; }) 209 | .then(function() { return queryCollect("INSERT INTO TEAM_PLAYERS VALUES (4, 'GameE', 'TeamA', 'ABCDEF');", readPromises, client).writePromise; }) 210 | .then(function() { return queryCollect("INSERT INTO TEAM_PLAYERS VALUES (5, 'GameA', 'TeamB', 'ABCDEF');", readPromises, client).writePromise; }) 211 | .then(function() { return queryCollect("INSERT INTO TEAM_PLAYERS VALUES (6, 'GameB', 'TeamB', 'ABCDEF');", readPromises, client).writePromise; }) 212 | .then(function() { return queryCollect("INSERT INTO TEAM_PLAYERS VALUES (7, 'GameC', 'TeamB', 'ABCDEF');", readPromises, client).writePromise; }) 213 | .then(function() { return queryCollect("INSERT INTO TEAM_PLAYERS VALUES (8, 'GameD', 'TeamB', 'ABCDEF');", readPromises, client).writePromise; }) 214 | .then(function() { return queryCollect("INSERT INTO TEAM_PLAYERS VALUES (9, 'GameE', 'TeamB', 'ABCDEF');", readPromises, client).writePromise; }) 215 | .then(function() { return Promise.all(readPromises); }); 216 | 217 | }) 218 | .then(function(){ 219 | return adHocQuery("select A.*, " + 220 | "B.name as name, " + 221 | "B.avatar as avatar " + 222 | "from PLAYERS as A left join TEAM_PLAYERS as B on A.playerID=B.id " + 223 | "where uID='GameA' and A.teamID='TeamA';", client).readPromise; 224 | }) 225 | .then(function(value){ 226 | debug("Result Count: %O", value.results.table.length); 227 | debug("Results: %O", value.results.table[0][0]); 228 | client.exit(); 229 | test.done(); 230 | return Promise.resolve(null); 231 | }) 232 | .catch(function(value){ 233 | debug("Test Failed | Results: %O", value); 234 | client.exit(); 235 | test.ok(false, "Test failed, see previous messages"); 236 | test.done(); 237 | }); 238 | } 239 | }; 240 | 241 | }(this)); 242 | -------------------------------------------------------------------------------- /examples/voter/voter/models/volt.js: -------------------------------------------------------------------------------- 1 | /* This file is part of VoltDB. 2 | * Copyright (C) 2008-2018 VoltDB Inc. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 | * OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | /* 25 | * This following is just a simple integration of VoltDB and a Node application. 26 | * 27 | * The code does the following: 28 | * 1. Exposes the VOLTDB stored procedures 29 | * 2. Creates a connection 30 | * 3. Invokes stored procedures and processes the results. 31 | */ 32 | 33 | var util = require('util'); 34 | var cluster = require('cluster'); 35 | 36 | // VoltClient manages all communication with VoltDB 37 | var VoltClient = require(__dirname + '/../../../../lib/client'); 38 | 39 | // VoltConstants has all the event types, codes and other constants 40 | // that the client and your code will rely upon 41 | var VoltConstants = require(__dirname + '/../../../../lib/voltconstants'); 42 | 43 | // VoltConfiguration sets up the configuration for each VoltDB server 44 | // in your cluster. If you have ten Volt nodes in the cluster, then you should 45 | // create ten configurations. These configurations are used in the construction 46 | // of the client. 47 | var VoltConfiguration = require(__dirname + '/../../../../lib/configuration'); 48 | 49 | // VoltProcedure is a static representation of the stored procedure and 50 | // specifies the procedure's name and the parameter types. The parameter types 51 | // are especially important since they define how the client will marshal the 52 | // the parameters. 53 | var VoltProcedure = require(__dirname + '/../../../../lib/query'); 54 | 55 | // VoltQuery is a specific instance of a VoltProcedure. Your code will 56 | // always call stored procedures using a VoltQuery object. 57 | var VoltQuery = require(__dirname + '/../../../../lib/query'); 58 | 59 | // These are a set of stored procedure definitions. 60 | // See VoltConstants to see the data types supported by the driver. 61 | var resultsProc = new VoltProcedure('Results'); 62 | var initProc = new VoltProcedure('Initialize', ['int', 'string']); 63 | var voteProc = new VoltProcedure('Vote', ['long', 'int', 'long']); 64 | 65 | // The following is just application specific data 66 | var client = null; 67 | 68 | var transactionCounter = 0; 69 | var statsLoggingInterval = 10000; 70 | 71 | var area_codes = [907, 205, 256, 334, 251, 870, 501, 479, 480, 602, 623, 928, 72 | 520, 341, 764, 628, 831, 925, 909, 562, 661, 510, 650, 949, 760, 415, 951, 209, 73 | 669, 408, 559, 626, 442, 530, 916, 627, 714, 707, 310, 323, 213, 424, 747, 818, 74 | 858, 935, 619, 805, 369, 720, 303, 970, 719, 860, 203, 959, 475, 202, 302, 689, 75 | 407, 239, 850, 727, 321, 754, 954, 927, 352, 863, 386, 904, 561, 772, 786, 305, 76 | 941, 813, 478, 770, 470, 404, 762, 706, 678, 912, 229, 808, 515, 319, 563, 641, 77 | 712, 208, 217, 872, 312, 773, 464, 708, 224, 847, 779, 815, 618, 309, 331, 630, 78 | 317, 765, 574, 260, 219, 812, 913, 785, 316, 620, 606, 859, 502, 270, 504, 985, 79 | 225, 318, 337, 774, 508, 339, 781, 857, 617, 978, 351, 413, 443, 410, 301, 240, 80 | 207, 517, 810, 278, 679, 313, 586, 947, 248, 734, 269, 989, 906, 616, 231, 612, 81 | 320, 651, 763, 952, 218, 507, 636, 660, 975, 816, 573, 314, 557, 417, 769, 601, 82 | 662, 228, 406, 336, 252, 984, 919, 980, 910, 828, 704, 701, 402, 308, 603, 908, 83 | 848, 732, 551, 201, 862, 973, 609, 856, 575, 957, 505, 775, 702, 315, 518, 646, 84 | 347, 212, 718, 516, 917, 845, 631, 716, 585, 607, 914, 216, 330, 234, 567, 419, 85 | 440, 380, 740, 614, 283, 513, 937, 918, 580, 405, 503, 541, 971, 814, 717, 570, 86 | 878, 835, 484, 610, 267, 215, 724, 412, 401, 843, 864, 803, 605, 423, 865, 931, 87 | 615, 901, 731, 254, 325, 713, 940, 817, 430, 903, 806, 737, 512, 361, 210, 979, 88 | 936, 409, 972, 469, 214, 682, 832, 281, 830, 956, 432, 915, 435, 801, 385, 434, 89 | 804, 757, 703, 571, 276, 236, 540, 802, 509, 360, 564, 206, 425, 253, 715, 920, 90 | 262, 414, 608, 304, 307]; 91 | 92 | var voteCandidates = 'Edwina Burnam,Tabatha Gehling,Kelly Clauss,' + 93 | 'Jessie Alloway,Alana Bregman,Jessie Eichman,Allie Rogalski,Nita Coster,' + 94 | 'Kurt Walser,Ericka Dieter,Loraine NygrenTania Mattioli'; 95 | 96 | function getCandidate() { 97 | return Math.floor(Math.random() * 6) + 1; 98 | } 99 | 100 | function getAreaCode() { 101 | var tmpNumber = Math.floor((area_codes[Math.floor(Math.random() * 1000) % area_codes.length] * 10000000) + 102 | (Math.random() * 10000000)); 103 | return tmpNumber; 104 | } 105 | 106 | // This will initialize the Voter database by invoking a stored procedure. 107 | function voltInit() { 108 | util.log('voltInit'); 109 | // Start by creating a query instance from the VoltProcedure 110 | var query = initProc.getQuery(); 111 | 112 | // Set the parameter values. 113 | query.setParameters([6, voteCandidates]); 114 | 115 | // Call the stored procedure with the query instance and a callback 116 | // handler to receive the results. 117 | // The callback handler uses the code to indicate whether there is an error 118 | // and the severity. See the VoltConstant source to see all the possible codes 119 | // and their definitions. 120 | // The event indicates what kind of event occurred. Again, check the 121 | // VoltConstant source to see the possible values. 122 | // The result object depends on the operation. Queries will always return a 123 | // VoltTable array 124 | client.callProcedure(query, function initVoter(code, event, results) { 125 | var val = results.table[0][0]; 126 | util.log('Initialized app for ' + val[''] + ' candidates.'); 127 | }); 128 | } 129 | 130 | 131 | // This is a generic event handler. An application can register a common 132 | // event handler for all events emitted by the client. This is very useful 133 | // for trapping all the various error conditions, like connections being 134 | // dropped. 135 | function eventListener(code, event, message) { 136 | util.log(util.format( 'Event %s\tcode: %d\tMessage: %s', event, code, 137 | message)); 138 | } 139 | 140 | // This is a generic configuration object factory. 141 | function getConfiguration(host) { 142 | var cfg = new VoltConfiguration(); 143 | cfg.host = host; 144 | // The messageQueueSize sets how many messages to buffer before dispatching 145 | // them to the server. The messages are dispatched when either a timeout is 146 | // reached or the queue fills up. It is best to keep this number 147 | // relatively small. High volume applications will see a benefit by having a 148 | // queue while low volume applications would be better served with the queue 149 | // size set to 0. Increasing the queue size beyond 20 will only give you 150 | // marginal performance improvements. 151 | cfg.messageQueueSize = 20; 152 | return cfg; 153 | } 154 | 155 | // Connect to the server 156 | exports.initClient = function(startLoop) { 157 | if(client == null) { 158 | var configs = [] 159 | 160 | configs.push(getConfiguration('localhost')); 161 | // The client is only configured at this point. The connection 162 | // is not made until the call to client.connect(). 163 | client = new VoltClient(configs); 164 | 165 | // You can register for a long list of event types, including the results 166 | // of queries. Some developers will prefer a common message loop 167 | // while others will prefer to consume each event in a separate handler. 168 | // Queries can also be processed in a common handler at the client level, 169 | // but would be better handled by using a query callback instead. 170 | client.on(VoltConstants.SESSION_EVENT.CONNECTION,eventListener); 171 | client.on(VoltConstants.SESSION_EVENT.CONNECTION_ERROR,eventListener); 172 | client.on(VoltConstants.SESSION_EVENT.QUERY_RESPONSE_ERROR,eventListener); 173 | client.on(VoltConstants.SESSION_EVENT.QUERY_DISPATCH_ERROR,eventListener); 174 | client.on(VoltConstants.SESSION_EVENT.FATAL_ERROR,eventListener); 175 | 176 | // The actual connection. 177 | // Note, there are two handlers. The first handler will generally indicate 178 | // a success, though it is possible for one of the connections to the 179 | // volt cluster to fail. 180 | // The second handler is more for catastrophic failures. 181 | client.connect(function startup(code, event,results) { 182 | if(code == VoltConstants.STATUS_CODES.SUCCESS) { 183 | util.log('Node connected to VoltDB'); 184 | if(startLoop) { 185 | setInterval(logResults, statsLoggingInterval); 186 | voteInsertLoop(); 187 | } else { 188 | voltInit(); 189 | } 190 | } else { 191 | util.log(`Unexpected status while initClient: ${VoltConstants.STATUS_CODE_STRINGS[code]}`); 192 | process.exit(1); 193 | } 194 | }, function loginError(code, event, results) { 195 | util.log('Node did not connect to VoltDB'); 196 | }); 197 | } 198 | } 199 | 200 | // This method will vote several times and will run in the background. 201 | function voteInsertLoop() { 202 | 203 | // Get the query object. 204 | var query = voteProc.getQuery(); 205 | var innerLoop = function() { 206 | // hard coded limited loop. 207 | // Note, increasing the size of the loop will "blast" the server with a 208 | // large backlog of queries and degrade performance when you are running 209 | // this application against a two node VoltDB cluster. 210 | for(var i = 0; i < 30; i++) { 211 | // Note that you can reuse the query object 212 | query.setParameters([ getAreaCode(), getCandidate(), 20000]); 213 | 214 | // There are two callbacks. The first indicates that the query returned. 215 | // The second indicates that it is safe to query the server again. The 216 | // second handler prevents blocking. You must allow the driver to read 217 | // from the VoltConnection's socket or VoltDB will close your socket. The 218 | // readyToWrite() callback gives you a way to interrupt looping type 219 | // operations so that the socket.read events can be processed by the 220 | // connection. 221 | client.callProcedure(query, 222 | function displayResults(code, event, results) { 223 | transactionCounter++; 224 | }, function readyToWrite(code, event, results) { 225 | 226 | }); 227 | } 228 | setImmediate(innerLoop); 229 | } 230 | process.nextTick(innerLoop); 231 | 232 | } 233 | 234 | // This just displays how many votes we issued every 10 seconds, per node 235 | // instance 236 | function logResults() { 237 | logTime("Voted", statsLoggingInterval, transactionCounter); 238 | transactionCounter = 0; 239 | } 240 | 241 | function logTime(operation, totalTime, count) { 242 | util.log(util.format('%d: %s %d times in %d milliseconds. %d TPS', 243 | process.pid, operation, count, totalTime, 244 | Math.floor((count / totalTime) * 1000))); 245 | } 246 | 247 | // Call the stored proc to collect all votes. 248 | exports.getVoteResults = function(callback) { 249 | var query = resultsProc.getQuery(); 250 | client.callProcedure(query, callback); 251 | } 252 | -------------------------------------------------------------------------------- /voternoui.js: -------------------------------------------------------------------------------- 1 | /* This file is part of VoltDB. 2 | * Copyright (C) 2008-2018 VoltDB Inc. 3 | * 4 | * This file contains original code and/or modifications of original code. 5 | * Any modifications made by VoltDB Inc. are licensed under the following 6 | * terms and conditions: 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining 9 | * a copy of this software and associated documentation files (the 10 | * "Software"), to deal in the Software without restriction, including 11 | * without limitation the rights to use, copy, modify, merge, publish, 12 | * distribute, sublicense, and/or sell copies of the Software, and to 13 | * permit persons to whom the Software is furnished to do so, subject to 14 | * the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 23 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 24 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | */ 27 | 28 | var cli = require('cli'); 29 | var cluster = require ('cluster'); 30 | var VoltClient = require('./lib/client'); 31 | var VoltConfiguration = require('./lib/configuration'); 32 | var VoltProcedure = require('./lib/query'); 33 | var VoltQuery = require('./lib/query'); 34 | 35 | var util = require('util'); 36 | 37 | var client = null; 38 | var resultsProc = new VoltProcedure('Results'); 39 | var initProc = new VoltProcedure('Initialize', ['int', 'string']); 40 | var voteProc = new VoltProcedure('Vote', ['long', 'int', 'long']); 41 | 42 | var options = cli.parse({ 43 | voteCount : ['c', 'Number of votes to run', 'number', 10000], 44 | clusterNode0 : ['h', 'VoltDB host (one of the cluster)', 'string', 'localhost'] 45 | }); 46 | 47 | var area_codes = [907, 205, 256, 334, 251, 870, 501, 479, 480, 602, 623, 928, 48 | 520, 341, 764, 628, 831, 925, 909, 562, 661, 510, 650, 949, 760, 415, 951, 209, 49 | 669, 408, 559, 626, 442, 530, 916, 627, 714, 707, 310, 323, 213, 424, 747, 50 | 818, 858, 935, 619, 805, 369, 720, 303, 970, 719, 860, 203, 959, 475, 202, 51 | 302, 689, 407, 239, 850, 727, 321, 754, 954, 927, 352, 863, 386, 904, 561, 52 | 772, 786, 305, 941, 813, 478, 770, 470, 404, 762, 706, 678, 912, 229, 808, 53 | 515, 319, 563, 641, 712, 208, 217, 872, 312, 773, 464, 708, 224, 847, 779, 54 | 815, 618, 309, 331, 630, 317, 765, 574, 260, 219, 812, 913, 785, 316, 620, 55 | 606, 859, 502, 270, 504, 985, 225, 318, 337, 774, 508, 339, 781, 857, 617, 56 | 978, 351, 413, 443, 410, 301, 240, 207, 517, 810, 278, 679, 313, 586, 947, 57 | 248, 734, 269, 989, 906, 616, 231, 612, 320, 651, 763, 952, 218, 507, 636, 58 | 660, 975, 816, 573, 314, 557, 417, 769, 601, 662, 228, 406, 336, 252, 984, 59 | 919, 980, 910, 828, 704, 701, 402, 308, 603, 908, 848, 732, 551, 201, 862, 60 | 973, 609, 856, 575, 957, 505, 775, 702, 315, 518, 646, 347, 212, 718, 516, 61 | 917, 845, 631, 716, 585, 607, 914, 216, 330, 234, 567, 419, 440, 380, 740, 62 | 614, 283, 513, 937, 918, 580, 405, 503, 541, 971, 814, 717, 570, 878, 835, 63 | 484, 610, 267, 215, 724, 412, 401, 843, 864, 803, 605, 423, 865, 931, 615, 64 | 901, 731, 254, 325, 713, 940, 817, 430, 903, 806, 737, 512, 361, 210, 979, 65 | 936, 409, 972, 469, 214, 682, 832, 281, 830, 956, 432, 915, 435, 801, 385, 66 | 434, 804, 757, 703, 571, 276, 236, 540, 802, 509, 360, 564, 206, 425, 253, 67 | 715, 920, 262, 414, 608, 304, 307]; 68 | var voteCandidates = 'Edwina Burnam,Tabatha Gehling,Kelly Clauss,' + 69 | 'Jessie Alloway,Alana Bregman,Jessie Eichman,Allie Rogalski,Nita Coster,' + 70 | 'Kurt Walser,Ericka Dieter,Loraine NygrenTania Mattioli'; 71 | 72 | 73 | function main() { 74 | 75 | var clusterNodes = [options.clusterNode0]; 76 | var configs = []; 77 | for ( var index = 0; index < clusterNodes.length; index++ ) { 78 | console.log("Host: " + clusterNodes[index]); 79 | var vc = new VoltConfiguration(); 80 | vc.host = clusterNodes[index]; 81 | configs.push(vc); 82 | } 83 | var counter = 0; 84 | 85 | client = new VoltClient(configs); 86 | client.connect(function startup(results) { 87 | console.log('Node up'); 88 | voltInit(); 89 | }, function loginError(results) { 90 | console.log("Error logging in: " + results); 91 | }); 92 | } 93 | 94 | function voltInit() { 95 | console.log('voltInit'); 96 | var query = initProc.getQuery(); 97 | query.setParameters([6, voteCandidates]); 98 | client.callProcedure(query, function initVoter(event, code, results) { 99 | if ( results.error == false ) { 100 | var val = results.table[0][0]; 101 | console.log( 'Initialized app for ' + val[''] + ' candidates.\n\n'); 102 | 103 | var voteJob = {}; 104 | voteJob.voteCount = options.voteCount; 105 | voteJob.steps = getSteps(); 106 | 107 | runNextLink(voteJob); 108 | } 109 | }); 110 | } 111 | 112 | function voteOften(voteJob) { 113 | console.log('voteOften'); 114 | voteInsertLoop(voteJob); 115 | } 116 | 117 | function voteResultsOften(voteJob) { 118 | console.log('voteResultsOften'); 119 | voteResultsLoop(voteJob); 120 | } 121 | 122 | function voteResults(voteJob) { 123 | console.log('voteResults'); 124 | var query = resultsProc.getQuery(); 125 | client.callProcedure(query, function displayResults(event, code, results) { 126 | var mytotalVotes = 0; 127 | 128 | var msg = ''; 129 | var longestString = 0; 130 | var rows = results.table[0]; 131 | for(var i = 0; i < rows.length; i++) { 132 | mytotalVotes += rows[i].TOTAL_VOTES; 133 | msg += util.format("%s\t%s\t%d\n", rows[i].CONTESTANT_NAME, 134 | rows[i].CONTESTANT_NUMBER, rows[i].TOTAL_VOTES); 135 | } 136 | msg += util.format("%d votes\n\n", mytotalVotes); 137 | console.log(msg); 138 | runNextLink(voteJob); 139 | }); 140 | } 141 | 142 | function connectionStats() { 143 | client.connectionStats(); 144 | } 145 | 146 | function voteEnd(voteJob) { 147 | client.connectionStats(); 148 | console.log('voteEnd'); 149 | process.exit(); 150 | } 151 | 152 | function getCandidate() { 153 | return Math.floor(Math.random() * 6) + 1; 154 | } 155 | 156 | function getAreaCode() { 157 | return area_codes[Math.floor(Math.random() * area_codes.length)] * 10000000 158 | + Math.random() * 10000000; 159 | } 160 | 161 | function getSteps() { 162 | var voltTestChain = []; 163 | voltTestChain.push(voteResults); 164 | // Not called because the query does a table scan and is not 165 | // representative of VoltDB's performance 166 | //voltTestChain.push(voteResultsOften); 167 | voltTestChain.push(voteOften); 168 | voltTestChain.push(voteResults); 169 | voltTestChain.push(voteEnd); 170 | 171 | return voltTestChain; 172 | } 173 | 174 | function voteResultsLoop(voteJob) { 175 | 176 | var index = 0; 177 | var reads = voteJob.voteCount; 178 | var startTime = new Date().getTime(); 179 | var chunkTime = new Date().getTime(); 180 | var readyToWriteCounter = 0; 181 | 182 | var innerResultsLoop = function() { 183 | var query = resultsProc.getQuery(); 184 | if(index < voteJob.voteCount) { 185 | client.callProcedure(query, function displayVoteResults(event, code, results) { 186 | reads--; 187 | // results object is not always real 188 | if ( results.status != 1) { 189 | console.log(results); 190 | } 191 | 192 | //console.log('reads left: ', reads); 193 | if(reads == 0) { 194 | logVoteResultsTime(startTime, voteJob.voteCount, "Results"); 195 | runNextLink(voteJob); 196 | } else { 197 | // console.log("reads ", reads); 198 | } 199 | //console.log('read done'); 200 | }, function readyToWrite() { 201 | //console.log('writes left: ', voteJob.voteCount-index); 202 | if(index < voteJob.voteCount) { 203 | if ( index % 5000 == 0 ) { 204 | console.log('Executed ', index, ' queries in ', 205 | (new Date().getTime()) - chunkTime, 'ms ', 206 | util.inspect(process.memoryUsage())); 207 | chunkTime = new Date().getTime(); 208 | } 209 | index++; 210 | if ( index % 20 == 0) { 211 | process.nextTick(innerResultsLoop); 212 | } 213 | } else { 214 | console.log('Time to stop querying: ', index); 215 | } 216 | //console.log('write done'); 217 | }); 218 | } else { 219 | console.log(readyToWriteCounter++, 'Index is: ', index, ' and ', 220 | voteJob.voteCount); 221 | } 222 | }; 223 | process.nextTick(innerResultsLoop); 224 | } 225 | 226 | 227 | 228 | function voteInsertLoop(voteJob) { 229 | 230 | var index = 0; 231 | var reads = voteJob.voteCount; 232 | var startTime = new Date().getTime(); 233 | var chunkTime = new Date().getTime(); 234 | var readyToWriteCounter = 0; 235 | 236 | var innerLoop = function() { 237 | var query = voteProc.getQuery(); 238 | if(index < voteJob.voteCount) { 239 | query.setParameters([getAreaCode(), getCandidate(), 200000]); 240 | client.callProcedure(query, function displayResults(event, code, results) { 241 | //console.log("reads ", reads); 242 | reads--; 243 | if(reads == 0) { 244 | logVoteInsertTime(startTime, voteJob.voteCount, "Results"); 245 | runNextLink(voteJob); 246 | } else { 247 | //console.log("reads ", reads); 248 | } 249 | }, function readyToWrite() { 250 | if( index < voteJob.voteCount ) { 251 | if ( index % 5000 == 0 ) { 252 | console.log('Executed ', index, ' votes in ', 253 | (new Date().getTime()) - chunkTime, 'ms '/*, 254 | util.inspect(process.memoryUsage())*/); 255 | chunkTime = new Date().getTime(); 256 | } 257 | index++; 258 | } 259 | }); 260 | } 261 | setImmediate(innerLoop); 262 | }; 263 | process.nextTick(innerLoop); 264 | 265 | } 266 | 267 | function logVoteInsertTime(startTime, votes, typeString) { 268 | logTime('Voted', startTime, votes, typeString); 269 | } 270 | 271 | function logVoteResultsTime(startTime, votes, typeString) { 272 | logTime('Queried for results', startTime, votes, typeString); 273 | } 274 | 275 | function logTime(operation, startTime, votes, typeString) { 276 | var endTimeMS = new Date().getTime() - startTime; 277 | var endTimeSeconds = Math.floor(endTimeMS / 1000); 278 | endTimeMS = (endTimeMS > 0 ? endTimeMS : 1 ); 279 | endTimeSeconds = (endTimeSeconds > 0 ? endTimeSeconds : 1 ); 280 | 281 | console.log(util.format('%s %d times in %d milliseconds.\n' 282 | + '%s %d times in %d seconds.\n%d milliseconds per transaction\n' 283 | + '%d transactions per millisecond\n%d transactions per second\n\n', 284 | operation, 285 | votes, endTimeMS, 286 | operation, votes, 287 | endTimeSeconds, (endTimeMS / votes), 288 | (votes / endTimeMS), (votes / endTimeSeconds))); 289 | } 290 | 291 | function isValidObject(object) { 292 | return typeof object != 'undefined' && object != null; 293 | } 294 | 295 | function runNextLink(voteJob) { 296 | 297 | if(0 < voteJob.steps.length) { 298 | var method = voteJob.steps.shift(); 299 | if(isValidObject(method) == true) { 300 | method(voteJob); 301 | } 302 | } 303 | } 304 | 305 | main(); 306 | -------------------------------------------------------------------------------- /lib/connection.js: -------------------------------------------------------------------------------- 1 | /* This file is part of VoltDB. 2 | * Copyright (C) 2008-2018 VoltDB Inc. 3 | * 4 | * This file contains original code and/or modifications of original code. 5 | * Any modifications made by VoltDB Inc. are licensed under the following 6 | * terms and conditions: 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining 9 | * a copy of this software and associated documentation files (the 10 | * "Software"), to deal in the Software without restriction, including 11 | * without limitation the rights to use, copy, modify, merge, publish, 12 | * distribute, sublicense, and/or sell copies of the Software, and to 13 | * permit persons to whom the Software is furnished to do so, subject to 14 | * the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 23 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 24 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | */ 27 | 28 | var EventEmitter = require('events').EventEmitter, 29 | Socket = require('net').Socket, 30 | crypto = require('crypto'), 31 | util = require('util'), 32 | Message = require('./message').Message, 33 | VoltConstants = require('./voltconstants'); 34 | const debug = require("debug")("voltdb-client-nodejs:VoltConnection"); 35 | 36 | function VoltMessageManager(configuration) { 37 | EventEmitter.call(this); 38 | this.uid = configuration.uid || null; 39 | this.message = configuration.message || null; 40 | this.error = false; 41 | this.time = null; 42 | index = -1; 43 | } 44 | 45 | util.inherits(VoltMessageManager, EventEmitter); 46 | 47 | 48 | function VoltConnection(configuration) { 49 | EventEmitter.call(this); 50 | this.config = configuration; 51 | 52 | this.onConnect = this.onConnect.bind(this); 53 | this.onError = this.onError.bind(this); 54 | this.onRead = this.onRead.bind(this); 55 | this._send = this._send.bind(this); 56 | this._flush = this._flush.bind(this); 57 | this.isValidConnection = this.isValidConnection.bind(this); 58 | this.isBlocked = this.isBlocked.bind(this); 59 | this._checkQueryTimeout = this._checkQueryTimeout.bind(this); 60 | this._manageOustandingQueries = this._manageOustandingQueries.bind(this); 61 | 62 | this.isWritable = true; 63 | this.isLoggedIn = false; 64 | this._sendQueue = []; 65 | this._calls = {}; 66 | this._callIndex = []; 67 | this._id = 0; 68 | this._overflow = new Buffer(0); 69 | this.sendCounter = 0; 70 | this.timeout = 0; 71 | this.invocations = 0; 72 | this.validConnection = true; 73 | this.blocked = false; 74 | 75 | this.outstandingQueryManager = null; 76 | this.flusher = null; 77 | 78 | } 79 | 80 | util.inherits(VoltConnection, EventEmitter); 81 | 82 | VoltConnection.prototype.initSocket = function(socket) { 83 | this.socket = socket; 84 | this.socket.on('error', this.onError); 85 | this.socket.on('data', this.onRead); 86 | this.socket.on('connect', this.onConnect); 87 | } 88 | 89 | 90 | VoltConnection.prototype.connect = function() { 91 | this.initSocket(new Socket()) 92 | this.socket.connect(this.config.port, this.config.host); 93 | this.outstandingQueryManager = setInterval(this._manageOustandingQueries, this.config.queryTimeoutInterval); 94 | this.flusher = setInterval(this._flush, this.config.flushInterval); 95 | } 96 | 97 | VoltConnection.prototype.isValidConnection = function() { 98 | return this.validConnection; 99 | } 100 | 101 | VoltConnection.prototype.isBlocked = function() { 102 | return this.blocked; 103 | } 104 | 105 | 106 | // Deprecating call in favor of callProcedure 107 | VoltConnection.prototype.call = function(query, readCallback, writeCallback) { 108 | return this.callProcedure(query, readCallback, writeCallback); 109 | } 110 | 111 | VoltConnection.prototype.callProcedure = function(query, readCallback, writeCallback) { 112 | this.invocations++; 113 | 114 | var uid = this._getUID(); 115 | query.setUID(uid); 116 | 117 | var vmm = new VoltMessageManager({ 118 | message: query.getMessage(), 119 | uid: uid}); 120 | 121 | if (readCallback) { 122 | vmm.on(VoltConstants.SESSION_EVENT.QUERY_RESPONSE, readCallback); 123 | vmm.on(VoltConstants.SESSION_EVENT.QUERY_RESPONSE_ERROR, readCallback); 124 | } 125 | 126 | if ( writeCallback ) { 127 | vmm.on(VoltConstants.SESSION_EVENT.QUERY_ALLOWED, writeCallback); 128 | } 129 | return this._send(vmm, true); 130 | } 131 | 132 | 133 | 134 | VoltConnection.prototype._getUID = function() { 135 | var id = String(this._id < 99999999 ? this._id++ : this._id = 0); 136 | var uid = this._zeros(8 - id.length).join('') + id; 137 | return uid; 138 | } 139 | 140 | VoltConnection.prototype._zeros = function(num) { 141 | var arr = new Array(num); 142 | for(var i = 0; i < num; i++) { 143 | arr[i] = 0; 144 | } 145 | return arr; 146 | } 147 | 148 | VoltConnection.prototype.close = function(callback) { 149 | 150 | debug("Closing"); 151 | 152 | if(this.outstandingQueryManager) clearInterval(this.outstandingQueryManager); 153 | if(this.flusher) clearInterval(this.flusher); 154 | 155 | if(this.socket) this.socket.end(); 156 | 157 | if(callback) callback(); 158 | } 159 | 160 | VoltConnection.prototype.onConnect = function(results) { 161 | var service = this.config.service; 162 | var sha1 = crypto.createHash('sha1'); 163 | sha1.update(this.config.password); 164 | var password = new Buffer(sha1.digest('base64'), 'base64'); 165 | 166 | var message = this._getLoginMessage(password); 167 | 168 | // you must connect and send login credentials immediately. 169 | var vmm = new VoltMessageManager({ 170 | message: message, 171 | uid: this._getUID() 172 | }); 173 | 174 | this._send(vmm, false); 175 | } 176 | 177 | VoltConnection.prototype.onError = function(results) { 178 | this.emit( 179 | VoltConstants.SESSION_EVENT.CONNECTION, 180 | VoltConstants.STATUS_CODES.UNEXPECTED_FAILURE, 181 | VoltConstants.SESSION_EVENT.CONNECTION, 182 | this); 183 | } 184 | 185 | VoltConnection.prototype.onRead = function(buffer) { 186 | var results = null; 187 | 188 | if(this.isLoggedIn == false) { 189 | results = this._decodeLoginResult(buffer); 190 | this.isLoggedIn = true; 191 | this.validConnection = true; 192 | this.emit(VoltConstants.SESSION_EVENT.CONNECTION, 193 | VoltConstants.STATUS_CODES.SUCCESS, 194 | VoltConstants.SESSION_EVENT.CONNECTION, 195 | this); 196 | } else { 197 | 198 | var overflow = this._overflow; 199 | var data = new Buffer(overflow.length + buffer.length); 200 | var length; 201 | 202 | overflow.copy(data, 0); 203 | buffer.copy(data, overflow.length, 0); 204 | while(data.length > 4 && data.length >= ( length = Message.readInt(data) + 4)) { 205 | 206 | var msg = data.slice(0, length); 207 | data = data.slice(length); 208 | results = this._decodeQueryResult(msg); 209 | 210 | var vmm = this._calls[results.uid]; 211 | if(vmm) { 212 | delete this._calls[results.uid]; 213 | this.sendCounter--; 214 | vmm.emit(VoltConstants.SESSION_EVENT.QUERY_RESPONSE, 215 | VoltConstants.STATUS_CODES.SUCCESS, 216 | VoltConstants.SESSION_EVENT.QUERY_RESPONSE, 217 | results); 218 | 219 | if(this.blocked == true) { 220 | this.blocked = false; 221 | this._invokeWriteEventHandler(vmm); 222 | } 223 | 224 | } else { 225 | this.emit( 226 | VoltConstants.SESSION_EVENT.QUERY_RESPONSE_ERROR, 227 | VoltConstants.STATUS_CODES.QUERY_TOOK_TOO_LONG, 228 | VoltConstants.SESSION_EVENT.QUERY_RESPONSE_ERROR, 229 | "Query completed after an extended period but query manager was deleted" ); 230 | } 231 | } 232 | this._overflow = data; 233 | } 234 | } 235 | 236 | VoltConnection.prototype._send = function(vmm, track) { 237 | var results = true; 238 | try { 239 | if(track == true) { 240 | this._calls[vmm.uid] = vmm; 241 | this._callIndex.push(vmm.uid); 242 | vmm.time = Date.now(); 243 | } 244 | 245 | this._queue(vmm.message.toBuffer(), track); 246 | 247 | this.sendCounter++; 248 | 249 | if(this.blocked == false) { 250 | this._invokeWriteEventHandler(vmm); 251 | } 252 | } catch (err) { 253 | this.emit(VoltConstants.SESSION_EVENT.QUERY_DISPATCH_ERROR, 254 | VoltConstants.STATUS_CODES.UNEXPECTED_FAILURE, 255 | VoltConstants.SESSION_EVENT.QUERY_DISPATCH_ERROR, 256 | err.message); 257 | this.validConnection = false; 258 | results = false; 259 | } 260 | return results; 261 | } 262 | 263 | VoltConnection.prototype._queue = function(buffer, track) { 264 | this._sendQueue.push(buffer); 265 | 266 | if(!track || this._sendQueue.length > this.config.messageQueueSize) { 267 | this._flush(); 268 | } 269 | }; 270 | 271 | VoltConnection.prototype._flush = function() { 272 | debug("Flushing | Send Queue Length: %o", this._sendQueue.length); 273 | var bytes = this._sendQueue.reduce(function(bytes, buffer) { 274 | return bytes + buffer.length; 275 | }, 0); 276 | var combined = new Buffer(bytes); 277 | 278 | this._sendQueue.reduce(function(offset, buffer) { 279 | buffer.copy(combined, offset); 280 | return offset + buffer.length; 281 | }, 0); 282 | 283 | try { 284 | this.socket.write(combined); 285 | } catch (err) { 286 | this.emit(VoltConstants.SESSION_EVENT.QUERY_DISPATCH_ERROR, 287 | VoltConstants.STATUS_CODES.UNEXPECTED_FAILURE, 288 | VoltConstants.SESSION_EVENT.QUERY_DISPATCH_ERROR, 289 | err.message 290 | + ": Connection dropped to server while dispatching query. Is VoltDB Server up?"); 291 | throw err; 292 | } 293 | 294 | this._sendQueue = []; 295 | }; 296 | 297 | VoltConnection.prototype._invokeWriteEventHandler = function(vmm) { 298 | // only allow more writes if the queue has not breached a limit 299 | if(this.sendCounter < this.config.maxConsecutiveWrites ) { 300 | this.blocked = false; 301 | vmm.emit(VoltConstants.SESSION_EVENT.QUERY_ALLOWED, 302 | VoltConstants.STATUS_CODES.SUCCESS, 303 | VoltConstants.SESSION_EVENT.QUERY_ALLOWED, 304 | null); 305 | } else { 306 | this.blocked = true; 307 | } 308 | } 309 | 310 | VoltConnection.prototype._getLoginMessage = function(password) { 311 | 312 | var message = new Message(); 313 | message.writeString(this.config.service); 314 | message.writeString(this.config.username); 315 | message.writeBinary(password); 316 | message.type = VoltConstants.MESSAGE_TYPE.LOGIN; 317 | return message; 318 | } 319 | 320 | VoltConnection.prototype._decodeLoginResult = function(buffer) { 321 | return new LoginMessage(buffer); 322 | } 323 | 324 | VoltConnection.prototype._decodeQueryResult = function(buffer) { 325 | return new QueryMessage(buffer); 326 | } 327 | 328 | VoltConnection.prototype._manageOustandingQueries = function() { 329 | var tmpCallIndex = []; 330 | var time = Date.now(); 331 | var uid = null; 332 | while( uid = this._callIndex.pop()) { 333 | vmm = this._calls[uid]; 334 | if(vmm && this._checkQueryTimeout(vmm, time) == false) { 335 | tmpCallIndex.push(vmm.uid); 336 | } 337 | } 338 | this._callIndex = tmpCallIndex; 339 | } 340 | 341 | VoltConnection.prototype._checkQueryTimeout = function(vmm, time) { 342 | var queryInvalidated = false; 343 | if(vmm) { 344 | if(time - vmm.time > this.config.queryTimeout) { 345 | queryInvalidated = true; 346 | this.sendCounter--; 347 | vmm.emit(VoltConstants.SESSION_EVENT.QUERY_RESPONSE_ERROR, 348 | VoltConstants.STATUS_CODES.QUERY_TOOK_TOO_LONG, 349 | VoltConstants.SESSION_EVENT.QUERY_RESPONSE_ERROR, 350 | {error : true, 351 | status : VoltConstants.STATUS_CODES.CONNECTION_TIMEOUT, 352 | statusString : 'Query timed out before server responded' 353 | }); 354 | 355 | vmm.emit(VoltConstants.SESSION_EVENT.QUERY_ALLOWED, 356 | VoltConstants.STATUS_CODES.SUCCESS, 357 | VoltConstants.SESSION_EVENT.QUERY_ALLOWED, 358 | null); 359 | delete this._calls[vmm.uid]; 360 | } 361 | } 362 | 363 | return queryInvalidated; 364 | } 365 | 366 | module.exports = VoltConnection; 367 | -------------------------------------------------------------------------------- /lib/parser.js: -------------------------------------------------------------------------------- 1 | /* This file is part of VoltDB. 2 | * Copyright (C) 2008-2018 VoltDB Inc. 3 | * 4 | * This file contains original code and/or modifications of original code. 5 | * Any modifications made by VoltDB Inc. are licensed under the following 6 | * terms and conditions: 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining 9 | * a copy of this software and associated documentation files (the 10 | * "Software"), to deal in the Software without restriction, including 11 | * without limitation the rights to use, copy, modify, merge, publish, 12 | * distribute, sublicense, and/or sell copies of the Software, and to 13 | * permit persons to whom the Software is furnished to do so, subject to 14 | * the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 23 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 24 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | */ 27 | var BigInteger = require('bignumber').BigInteger, 28 | ctype = require('./ctype'), 29 | endian = 'big'; 30 | 31 | var NullValueOf = { 32 | byte: -128, 33 | short: -32768, 34 | int: -2147483648, 35 | long: new BigInteger('-9223372036854775808'), 36 | double: -1.7E+308, 37 | decimal: '-170141183460469231731687303715884105728' 38 | }; 39 | 40 | function Parser(buffer) { 41 | this.buffer = buffer || []; 42 | this.position = 0; 43 | } 44 | 45 | Parser.prototype.readBinary = function(length) { 46 | return this.buffer.slice(this.position, this.position += length); 47 | }; 48 | Parser.prototype.writeBinary = function(buffer) { 49 | for(var i = 0, l = buffer.length, pos = this.position; i < l; i++) { 50 | this.buffer[pos + i] = buffer[i]; 51 | } 52 | this.position += l; 53 | }; 54 | 55 | Parser.prototype.readByte = function() { 56 | var res = ctype.rsint8(this.buffer, endian, this.position++); 57 | if (res === NullValueOf['byte']) { 58 | return null; 59 | } else { 60 | return res; 61 | } 62 | }; 63 | Parser.prototype.writeByte = function(value) { 64 | if (value == null) { 65 | value = NullValueOf['byte']; 66 | } 67 | ctype.wsint8(value, endian, this.buffer, this.position++); 68 | }; 69 | 70 | Parser.prototype.readShort = function() { 71 | var res = ctype.rsint16(this.buffer, endian, (this.position += 2) - 2); 72 | if (res === NullValueOf['short']) { 73 | return null; 74 | } else { 75 | return res; 76 | } 77 | }; 78 | Parser.prototype.writeShort = function(value) { 79 | if (value == null) { 80 | value = NullValueOf['short']; 81 | } 82 | ctype.wsint16(value, endian, this.buffer, (this.position += 2) - 2); 83 | }; 84 | 85 | Parser.prototype.readInt = function() { 86 | var res = ctype.rsint32(this.buffer, endian, (this.position += 4) - 4); 87 | if (res === NullValueOf['int']) { 88 | return null; 89 | } else { 90 | return res; 91 | } 92 | }; 93 | Parser.prototype.writeInt = function(value) { 94 | if (value == null) { 95 | value = NullValueOf['int']; 96 | } 97 | ctype.wsint32(value, endian, this.buffer, (this.position += 4) - 4); 98 | }; 99 | 100 | Parser.prototype.readDouble = function() { 101 | var res = ctype.rdouble(this.buffer, endian, (this.position += 8) - 8); 102 | if (res === NullValueOf['double']) { 103 | return null; 104 | } else { 105 | return res; 106 | } 107 | }; 108 | Parser.prototype.writeDouble = function(value) { 109 | if (value == null) { 110 | value = NullValueOf['double']; 111 | } 112 | ctype.wdouble(value, endian, this.buffer, (this.position += 8) - 8); 113 | }; 114 | 115 | Parser.prototype.readLongBytes = function() { 116 | var bytes = [], numBytes = 8; 117 | for(var i = 0; i < numBytes; i++) { 118 | bytes.push(ctype.ruint8(this.buffer, endian, this.position + i)); 119 | } 120 | this.position += numBytes; 121 | return (new BigInteger(bytes)); 122 | }; 123 | 124 | Parser.prototype.readLong = function() { 125 | var res = this.readLongBytes(); 126 | if (res.equals(NullValueOf['long'])) { 127 | return null; 128 | } else { 129 | return res; 130 | } 131 | }; 132 | Parser.prototype.writeLong = function(value) { 133 | if (value == null) { 134 | value = NullValueOf['long']; 135 | } 136 | var bytes, numBytes = 8; 137 | if( typeof value === 'number') 138 | value = new BigInteger(value.toString()); 139 | if(!( value instanceof BigInteger)) 140 | throw new Error('Long type must be a BigInteger or Number'); 141 | bytes = value.toByteArray(); 142 | if (bytes[0] >= 0) { 143 | while (bytes.length < numBytes) { 144 | bytes.unshift(0); 145 | } 146 | } else { 147 | while (bytes.length < numBytes) { 148 | bytes.unshift(-1); 149 | } 150 | } 151 | 152 | for(var i = 0; i < numBytes; i++) 153 | ctype.wsint8(bytes[i], endian, this.buffer, this.position + i); 154 | this.position += numBytes; 155 | }; 156 | 157 | Parser.prototype.readString = function() { 158 | var length = this.readInt(); 159 | if (length < 0) return null; 160 | return this.buffer.toString('utf8', this.position, this.position += length); 161 | }; 162 | Parser.prototype.writeString = function(value) { 163 | var length; 164 | if (value == null) { 165 | length = -1; 166 | } else { 167 | var strBuf = new Buffer(value, 'utf8'); 168 | length = strBuf.length; 169 | } 170 | this.writeInt(length); 171 | 172 | for(var i = 0, pos = this.position; i < length; i++) { 173 | this.buffer[pos + i] = strBuf[i]; 174 | } 175 | this.position += Math.max(0, length); 176 | }; 177 | 178 | Parser.prototype.readDate = function() { 179 | var bigInt = this.readLongBytes(); 180 | if (bigInt.toString() === NullValueOf['long']) { 181 | return null; 182 | } else { 183 | var intStr = bigInt.divide(thousand).toString(); 184 | return new Date(parseInt(intStr)); 185 | } 186 | }; 187 | 188 | Parser.prototype.writeDate = function(value) { 189 | if (value == null) { 190 | this.writeLong(null); 191 | } else { 192 | var bigInt; 193 | if (value instanceof Date) 194 | value = Date.getTime(); 195 | else if (typeof value !== 'number') 196 | throw new Error('Date type must be a Date or number'); 197 | bigInt = new BigInteger(value.toString()); 198 | 199 | this.writeLong(bigInt.multiply(thousand)); 200 | } 201 | }; 202 | 203 | Parser.prototype.readDecimal = function() { 204 | var bytes = [], bigInt, numBytes = 16, decimalPlaces = 12; 205 | for(var i = 0; i < numBytes; i++) 206 | bytes.push(ctype.ruint8(this.buffer, endian, this.position + i)); 207 | this.position += numBytes; 208 | bigInt = new BigInteger(bytes); 209 | var val = bigInt.toString(); 210 | 211 | // handle the null value case 212 | if(val === NullValueOf['decimal']) { 213 | val = null; 214 | } else if(val.length <= 12) { 215 | // add leading zeros (e.g. 123 to 0.000000000123) 216 | val = zeros(decimalPlaces - val.length).join('') + val; 217 | val = '0.' + val; 218 | } else { 219 | // put the decimal in the right place 220 | val = val.slice(0, -decimalPlaces) + '.' + val.slice(-decimalPlaces); 221 | } 222 | return val; 223 | }; 224 | Parser.prototype.writeDecimal = function(value) { 225 | var bytes, bigInt, numBytes = 16; 226 | if(value == null) { 227 | bigInt = new BigInteger(NullValueOf['decimal']); 228 | } else { 229 | if (typeof value === 'number') 230 | value = value.toString(); 231 | if (typeof value != 'string' || !(/^-?\d*\.?\d*$/).test(value)) 232 | throw new Error('Decimal type must be a numerical string or Number:' + value); 233 | 234 | // add decimal and missing zeros 235 | if (value.startsWith('.')) { 236 | value = '0' + value; 237 | } 238 | bigInt = new BigInteger(sanitizeDecimal(value)); 239 | } 240 | bytes = bigInt.toByteArray(); 241 | if (bytes[0] >= 0) { 242 | while (bytes.length < numBytes) { 243 | bytes.unshift(0); 244 | } 245 | } else { 246 | while (bytes.length < numBytes) { 247 | bytes.unshift(-1); 248 | } 249 | } 250 | 251 | for(var i = 0; i < numBytes; i++) 252 | ctype.wsint8(bytes[i], endian, this.buffer, this.position + i); 253 | this.position += numBytes; 254 | }; 255 | 256 | Parser.prototype.readVarbinary = function() { 257 | var length = this.readInt(); 258 | if (length == -1) { 259 | return null; 260 | } else { 261 | var binary = this.buffer.slice(this.position, this.position + length); 262 | this.position += length; 263 | return binary; 264 | } 265 | } 266 | 267 | Parser.prototype.writeVarbinary = function(value) { 268 | if (value == null) { 269 | this.writeInt(-1); 270 | } else { 271 | this.writeInt(value.length); 272 | this.writeBinary(value); 273 | } 274 | } 275 | 276 | Parser.prototype.readNull = function() { 277 | // a no-op, no reading 278 | return null; 279 | }; 280 | Parser.prototype.writeNull = function(value) { 281 | // a no-op, no writing 282 | }; 283 | 284 | Parser.prototype.readArray = function(type, value) { 285 | type = TYPES_STRINGS[this.readByte()]; 286 | if(type == undefined) 287 | throw new Error('Unsupported type, update driver'); 288 | 289 | var length = (type == 'byte' ? this.readInt() : this.readShort()); 290 | var method = TYPES_READ[type]; 291 | value = new Array(length); 292 | for(var i = 0; i < length; i++) { 293 | value[i] = this[method](); 294 | } 295 | return value; 296 | }; 297 | 298 | Parser.prototype.writeArray = function(type, value) { 299 | if(type.slice(0, 5) != 'array' && !TYPES_NUMBERS.hasOwnProperty(type)) 300 | throw new Error('Type must be one of: array, null tinyint, smallint,' + ' integer, bigint, float, string, timestamp, decimal'); 301 | 302 | if(!( value instanceof Array)) 303 | throw new Error(('Array value must be an Array')); 304 | 305 | var length = value.length, i, match; 306 | 307 | // if it's a subarray (e.g. type = array[string]) 308 | if( match = type.match(arrExp)) { 309 | this.writeByte(TYPES_NUMBERS.array); 310 | // write type 'array' -99 311 | this.writeShort(length); 312 | var arrType = match[1]; 313 | 314 | // write sub-array values 315 | for( i = 0; i < length; i++) { 316 | this.writeArray(arrType, value[i]) 317 | } 318 | } else { 319 | this.writeByte(TYPES_NUMBERS[type]); 320 | // write type 321 | // write length 322 | type == 'byte' ? this.writeInt(length) : this.writeShort(length); 323 | var method = TYPES_WRITE[type]; 324 | 325 | // write values 326 | for( i = 0; i < length; i++) { 327 | this[method](value[i]); 328 | } 329 | } 330 | }; 331 | 332 | Parser.prototype.readVoltTable = function() { 333 | // header 334 | var tableLength = this.readInt(); 335 | var metaLength = this.readInt(); 336 | var status = this.readByte(); 337 | var columnCount = this.readShort(); 338 | var columnTypes = new Array(columnCount); 339 | var columnMethods = new Array(columnCount); 340 | for(var i = 0; i < columnCount; i++) { 341 | var typeByte = this.readByte(); 342 | var type = TYPES_STRINGS[typeByte]; 343 | columnTypes[i] = type; 344 | columnMethods[i] = TYPES_READ[type]; 345 | 346 | } 347 | var columnNames = new Array(columnCount); 348 | for( i = 0; i < columnCount; i++) { 349 | columnNames[i] = this.readString(); 350 | } 351 | var rowCount = this.readInt(); 352 | 353 | // data 354 | var rows = new Array(rowCount); 355 | for( i = 0; i < rowCount; i++) { 356 | var rowLength = this.readInt(); 357 | var row = {}; 358 | for(var j = 0; j < columnCount; j++) { 359 | row[columnNames[j]] = this[columnMethods[j]](); 360 | } 361 | rows[i] = row; 362 | } 363 | 364 | rows.status = status; 365 | rows.columnNames = columnNames; 366 | rows.columnTypes = columnTypes; 367 | return rows; 368 | }; 369 | 370 | Parser.prototype.readException = function(length) { 371 | if(length == 0) 372 | new Error('An exception has occurred'); 373 | var ordinal = this.readByte(); 374 | // they don't have a spec for exceptions at this time, just skip it. 375 | var theRest = this.readBinary(length - 1); 376 | if(ordinal == 1) 377 | return new Error('EEException'); 378 | else if(ordinal == 2) 379 | return new Error('SQLException'); 380 | else if(ordinal == 3) 381 | return new Error('ConstraintFailureException'); 382 | return new Error('An exception has occurred'); 383 | }; 384 | 385 | Parser.prototype.writeParameterSet = function(types, values) { 386 | if(types.length != values.length) 387 | throw new Error('The number of parameters do not match the number of ' + 'types defined in the definition.'); 388 | 389 | var length = values.length, match; 390 | this.writeShort(length); 391 | 392 | for(var i = 0; i < length; i++) { 393 | var type = types[i]; 394 | var value = values[i]; 395 | checkType(type, value); 396 | 397 | // handle the array type 398 | if( match = type.match(arrExp)) { 399 | var arrType = match[1]; 400 | this.writeByte(TYPES_NUMBERS.array); 401 | this.writeArray(arrType, value); 402 | } else { 403 | this.writeByte(TYPES_NUMBERS[type]); 404 | var method = TYPES_WRITE[type]; 405 | this[method](value); 406 | } 407 | } 408 | }; 409 | // for getting lengths from incoming data 410 | Parser.readInt = function(buffer, offset) { 411 | if(offset == undefined) 412 | offset = 0; 413 | return ctype.rsint32(buffer, endian, offset); 414 | }; 415 | 416 | exports.Parser = Parser; 417 | 418 | var arrExp = /array\[(.*)\]/; 419 | 420 | var TYPES_STRINGS = { 421 | '-99' : 'array', 422 | '1' : 'null', 423 | '3' : 'byte', 424 | '4' : 'short', 425 | '5' : 'int', 426 | '6' : 'long', 427 | '8' : 'double', 428 | '9' : 'string', 429 | '11' : 'date', 430 | '22' : 'decimal', 431 | '25' : 'varbinary' 432 | }; 433 | 434 | var TYPES_NUMBERS = { 435 | 'array' : -99, 436 | 'null' : 1, 437 | 'byte' : 3, 438 | 'tinyint' : 3, 439 | 'short' : 4, 440 | 'smallint' : 4, 441 | 'int' : 5, 442 | 'integer' : 5, 443 | 'long' : 6, 444 | 'bigint' : 6, 445 | 'double' : 8, 446 | 'float' : 8, 447 | 'string' : 9, 448 | 'date' : 11, 449 | 'timestamp' : 11, 450 | 'decimal' : 22, 451 | 'varbinary' : 25 452 | }; 453 | 454 | var TYPES_READ = { 455 | 'array' : 'readArray', 456 | 'null' : 'readNull', 457 | 'byte' : 'readByte', 458 | 'tinyint' : 'readByte', 459 | 'short' : 'readShort', 460 | 'smallint' : 'readShort', 461 | 'int' : 'readInt', 462 | 'integer' : 'readInt', 463 | 'long' : 'readLong', 464 | 'bigint' : 'readLong', 465 | 'double' : 'readDouble', 466 | 'float' : 'readDouble', 467 | 'string' : 'readString', 468 | 'date' : 'readDate', 469 | 'timestamp' : 'readDate', 470 | 'decimal' : 'readDecimal', 471 | 'varbinary' : 'readVarbinary' 472 | }; 473 | var TYPES_WRITE = { 474 | 'array' : 'writeArray', 475 | 'null' : 'writeNull', 476 | 'byte' : 'writeByte', 477 | 'tinyint' : 'writeByte', 478 | 'short' : 'writeShort', 479 | 'smallint' : 'writeShort', 480 | 'int' : 'writeInt', 481 | 'integer' : 'writeInt', 482 | 'long' : 'writeLong', 483 | 'bigint' : 'writeLong', 484 | 'double' : 'writeDouble', 485 | 'float' : 'writeDouble', 486 | 'string' : 'writeString', 487 | 'date' : 'writeDate', 488 | 'timestamp' : 'writeDate', 489 | 'decimal' : 'writeDecimal', 490 | 'varbinary' : 'writeVarbinary' 491 | }; 492 | 493 | var NUMERIC_TYPES = { 494 | 'byte' : true, 495 | 'tinyint' : true, 496 | 'short' : true, 497 | 'smallint' : true, 498 | 'int' : true, 499 | 'integer' : true, 500 | 'long' : true, 501 | 'bigint' : true, 502 | 'double' : true, 503 | 'float' : true, 504 | 'date' : true, 505 | 'timestamp' : true, 506 | 'decimal' : true, 507 | 'varbinary' : true 508 | }; 509 | var STRING_TYPES = { 510 | 'string' : true, 511 | 'decimal' : true 512 | }; 513 | var BIGINT_TYPES = { 514 | 'long' : true, 515 | 'bigint' : true 516 | }; 517 | 518 | var thousand = new BigInteger('1000'); 519 | 520 | function zeros(num) { 521 | var arr = new Array(num); 522 | for(var i = 0; i < num; i++) 523 | arr[i] = 0; 524 | return arr; 525 | } 526 | 527 | function ones(num) { 528 | var arr = new Array(num); 529 | for(var i = 0; i < num; i++) 530 | arr[i] = 1; 531 | return arr; 532 | } 533 | 534 | function checkType(type, value) { 535 | if(type == 'array') 536 | throw new Error('Type array must have a subtype. E.g. array[string]'); 537 | 538 | if(type.slice(0, 5) != 'array' && !TYPES_NUMBERS.hasOwnProperty(type)) 539 | throw new Error('Type must be one of: array, null tinyint, smallint, ' + 'integer, bigint, float, string, timestamp, decimal'); 540 | 541 | if( typeof value === 'numeric' && !NUMERIC_TYPES[type]) 542 | throw new Error('Providing a numeric type for a non-numeric field. ' + value + ' can not be a ' + type); 543 | 544 | if( typeof value === 'string' && !STRING_TYPES[type]) 545 | throw new Error('Providing a string type for a non-string field. ' + value + ' can not be a ' + type); 546 | 547 | if( typeof value === 'object' && !( value instanceof Array) && !( value instanceof Uint8Array) && (value != null)) 548 | throw new Error('Cannot provide custom objects as procedure parameters'); 549 | 550 | if( value instanceof Array && type.slice(0, 5) != 'array') 551 | throw new Error('Providing an array type for a non-array field. ' + value + ' can not be a ' + type); 552 | 553 | if(type.slice(0, 5) == 'array' && !( value instanceof Array)) 554 | throw new Error('Providing a non-array value for an array field. ' + value + ' can not be a ' + type); 555 | 556 | if( value instanceof BigInteger && !BIGINT_TYPES[type]) 557 | throw new Error('Providing a BigInteger type for a non-bigint field. ' + value + ' can not be a ' + type); 558 | } 559 | 560 | function sanitizeDecimal(value) { 561 | var MAX_INT_DIGIT = 26, MAX_FRAC_DIGIT = 12; 562 | 563 | var sign = ''; 564 | if (value.startsWith('-')) { 565 | sign = '-'; 566 | value = value.slice(1); 567 | } 568 | var parts = value.split('.'); 569 | // first check if the given value is legal 570 | if (parts[0].length > MAX_INT_DIGIT) { 571 | throw new Error('The integer part should not have more than' + MAX_INT_DIGIT + 'digits.'); 572 | } 573 | if (parts.length == 2 && parts[1].length > MAX_FRAC_DIGIT) { 574 | throw new Error('The fractional part should not have more than' + MAX_FRAC_DIGIT + 'digits.'); 575 | } 576 | 577 | // add trailing zeros 578 | if (parts.length == 1) { 579 | return sign + parts[0] + zeros(MAX_FRAC_DIGIT).join(''); 580 | } else { 581 | return sign + parts[0] + parts[1] + zeros(MAX_FRAC_DIGIT - parts[1].length).join(''); 582 | } 583 | } -------------------------------------------------------------------------------- /lib/ctype.js: -------------------------------------------------------------------------------- 1 | /* This file is part of VoltDB. 2 | * Copyright (C) 2008-2018 VoltDB Inc. 3 | * 4 | * This file contains original code and/or modifications of original code. 5 | * Any modifications made by VoltDB Inc. are licensed under the following 6 | * terms and conditions: 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining 9 | * a copy of this software and associated documentation files (the 10 | * "Software"), to deal in the Software without restriction, including 11 | * without limitation the rights to use, copy, modify, merge, publish, 12 | * distribute, sublicense, and/or sell copies of the Software, and to 13 | * permit persons to whom the Software is furnished to do so, subject to 14 | * the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 23 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 24 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | */ 27 | 28 | /* 29 | * The following license applies to all files unless the file is specified below. 30 | * Each file specified below has its license information embedded in it: 31 | * tools/jsstyle 32 | * 33 | * Copyright 2011, Robert Mustacchi. All rights reserved. 34 | * Copyright 2011, Joyent, Inc. All rights reserved. 35 | * Permission is hereby granted, free of charge, to any person obtaining a copy 36 | * of this software and associated documentation files (the "Software"), to 37 | * deal in the Software without restriction, including without limitation the 38 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 39 | * sell copies of the Software, and to permit persons to whom the Software is 40 | * furnished to do so, subject to the following conditions: 41 | * 42 | * The above copyright notice and this permission notice shall be included in 43 | * all copies or substantial portions of the Software. 44 | * 45 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 46 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 47 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 48 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 49 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 50 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 51 | * IN THE SOFTWARE. 52 | */ 53 | 54 | /* 55 | * rm - Feb 2011 56 | * ctype.js 57 | * 58 | * This module provides a simple abstraction towards reading and writing 59 | * different types of binary data. It is designed to use ctio.js and provide a 60 | * richer and more expressive API on top of it. 61 | * 62 | * By default we support the following as built in basic types: 63 | * int8_t 64 | * int16_t 65 | * int32_t 66 | * uint8_t 67 | * uint16_t 68 | * uint32_t 69 | * float 70 | * double 71 | * char 72 | * char[] 73 | * 74 | * Each type is returned as a Number, with the exception of char and char[] 75 | * which are returned as Node Buffers. A char is considered a uint8_t. 76 | * 77 | * Requests to read and write data are specified as an array of JSON objects. 78 | * This is also the same way that one declares structs. Even if just a single 79 | * value is requested, it must be done as a struct. The array order determines 80 | * the order that we try and read values. Each entry has the following format 81 | * with values marked with a * being optional. 82 | * 83 | * { key: { type: /type/, value*: /value/, offset*: /offset/ } 84 | * 85 | * If offset is defined, we lseek(offset, SEEK_SET) before reading the next 86 | * value. Value is defined when we're writing out data, otherwise it's ignored. 87 | * 88 | */ 89 | 90 | var mod_ctio = require('./ctio.js'); 91 | var ASSERT = require('assert'); 92 | 93 | /* 94 | * This is the set of basic types that we support. 95 | * 96 | * read The function to call to read in a value from a buffer 97 | * 98 | * write The function to call to write a value to a buffer 99 | * 100 | */ 101 | var deftypes = { 102 | 'uint8_t' : { 103 | read : ctReadUint8, 104 | write : ctWriteUint8 105 | }, 106 | 'uint16_t' : { 107 | read : ctReadUint16, 108 | write : ctWriteUint16 109 | }, 110 | 'uint32_t' : { 111 | read : ctReadUint32, 112 | write : ctWriteUint32 113 | }, 114 | 'int8_t' : { 115 | read : ctReadSint8, 116 | write : ctWriteSint8 117 | }, 118 | 'int16_t' : { 119 | read : ctReadSint16, 120 | write : ctWriteSint16 121 | }, 122 | 'int32_t' : { 123 | read : ctReadSint32, 124 | write : ctWriteSint32 125 | }, 126 | 'float' : { 127 | read : ctReadFloat, 128 | write : ctWriteFloat 129 | }, 130 | 'double' : { 131 | read : ctReadDouble, 132 | write : ctWriteDouble 133 | }, 134 | 'char' : { 135 | read : ctReadChar, 136 | write : ctWriteChar 137 | }, 138 | 'char[]' : { 139 | read : ctReadCharArray, 140 | write : ctWriteCharArray 141 | } 142 | }; 143 | 144 | /* 145 | * The following are wrappers around the CType IO low level API. They encode 146 | * knowledge about the size and return something in the expected format. 147 | */ 148 | function ctReadUint8(endian, buffer, offset) { 149 | var val = mod_ctio.ruint8(buffer, endian, offset); 150 | return ( { 151 | value : val, 152 | size : 1 153 | }); 154 | } 155 | 156 | function ctReadUint16(endian, buffer, offset) { 157 | var val = mod_ctio.ruint16(buffer, endian, offset); 158 | return ( { 159 | value : val, 160 | size : 2 161 | }); 162 | } 163 | 164 | function ctReadUint32(endian, buffer, offset) { 165 | var val = mod_ctio.ruint32(buffer, endian, offset); 166 | return ( { 167 | value : val, 168 | size : 4 169 | }); 170 | } 171 | 172 | function ctReadSint8(endian, buffer, offset) { 173 | var val = mod_ctio.rsint8(buffer, endian, offset); 174 | return ( { 175 | value : val, 176 | size : 1 177 | }); 178 | } 179 | 180 | function ctReadSint16(endian, buffer, offset) { 181 | var val = mod_ctio.rsint16(buffer, endian, offset); 182 | return ( { 183 | value : val, 184 | size : 2 185 | }); 186 | } 187 | 188 | function ctReadSint32(endian, buffer, offset) { 189 | var val = mod_ctio.rsint32(buffer, endian, offset); 190 | return ( { 191 | value : val, 192 | size : 4 193 | }); 194 | } 195 | 196 | function ctReadFloat(endian, buffer, offset) { 197 | var val = mod_ctio.rfloat(buffer, endian, offset); 198 | return ( { 199 | value : val, 200 | size : 4 201 | }); 202 | } 203 | 204 | function ctReadDouble(endian, buffer, offset) { 205 | var val = mod_ctio.rdouble(buffer, endian, offset); 206 | return ( { 207 | value : val, 208 | size : 8 209 | }); 210 | } 211 | 212 | /* 213 | * Reads a single character into a node buffer 214 | */ 215 | function ctReadChar(endian, buffer, offset) { 216 | var res = new Buffer(1); 217 | res[0] = mod_ctio.ruint8(buffer, endian, offset); 218 | return ( { 219 | value : res, 220 | size : 1 221 | }); 222 | } 223 | 224 | function ctReadCharArray(length, endian, buffer, offset) { 225 | var ii; 226 | var res = new Buffer(length); 227 | 228 | for( ii = 0; ii < length; ii++) 229 | res[ii] = mod_ctio.ruint8(buffer, endian, offset + ii); 230 | 231 | return ( { 232 | value : res, 233 | size : length 234 | }); 235 | } 236 | 237 | function ctWriteUint8(value, endian, buffer, offset) { 238 | mod_ctio.wuint8(value, endian, buffer, offset); 239 | return (1); 240 | } 241 | 242 | function ctWriteUint16(value, endian, buffer, offset) { 243 | mod_ctio.wuint16(value, endian, buffer, offset); 244 | return (2); 245 | } 246 | 247 | function ctWriteUint32(value, endian, buffer, offset) { 248 | mod_ctio.wuint32(value, endian, buffer, offset); 249 | return (4); 250 | } 251 | 252 | function ctWriteSint8(value, endian, buffer, offset) { 253 | mod_ctio.wsint8(value, endian, buffer, offset); 254 | return (1); 255 | } 256 | 257 | function ctWriteSint16(value, endian, buffer, offset) { 258 | mod_ctio.wsint16(value, endian, buffer, offset); 259 | return (2); 260 | } 261 | 262 | function ctWriteSint32(value, endian, buffer, offset) { 263 | mod_ctio.wsint32(value, endian, buffer, offset); 264 | return (4); 265 | } 266 | 267 | function ctWriteFloat(value, endian, buffer, offset) { 268 | mod_ctio.wfloat(value, endian, buffer, offset); 269 | return (4); 270 | } 271 | 272 | function ctWriteDouble(value, endian, buffer, offset) { 273 | mod_ctio.wdouble(value, endian, buffer, offset); 274 | return (8); 275 | } 276 | 277 | /* 278 | * Writes a single character into a node buffer 279 | */ 280 | function ctWriteChar(value, endian, buffer, offset) { 281 | if(!( value instanceof Buffer)) 282 | throw (new Error('Input must be a buffer')); 283 | 284 | mod_ctio.ruint8(value[0], endian, buffer, offset); 285 | return (1); 286 | } 287 | 288 | /* 289 | * We're going to write 0s into the buffer if the string is shorter than the 290 | * length of the array. 291 | */ 292 | function ctWriteCharArray(value, length, endian, buffer, offset) { 293 | var ii; 294 | 295 | if(!( value instanceof Buffer)) 296 | throw (new Error('Input must be a buffer')); 297 | 298 | if(value.length > length) 299 | throw (new Error('value length greater than array length')); 300 | 301 | for( ii = 0; ii < value.length && ii < length; ii++) 302 | mod_ctio.wuint8(value[ii], endian, buffer, offset + ii); 303 | 304 | for(; ii < length; ii++) 305 | mod_ctio.wuint8(0, endian, offset + ii); 306 | 307 | return (length); 308 | } 309 | 310 | /* 311 | * Construct a per parser specific set of the basic types. That way if this is 312 | * modified, it's okay. The values themselves should never be modified; however, 313 | * the set of keys may change over time. 314 | */ 315 | function ctGetBasicTypes() { 316 | var ret = {}; 317 | var key; 318 | for(key in deftypes) 319 | ret[key] = deftypes[key]; 320 | 321 | return (ret); 322 | } 323 | 324 | /* 325 | * Given a string in the form of type[length] we want to split this into an 326 | * object that extracts that information. We want to note that we could possibly 327 | * have nested arrays so this should only check the furthest one. It may also be 328 | * the case that we have no [] pieces, in which case we just return the current 329 | * type. 330 | */ 331 | function ctParseType(str) { 332 | var begInd, endInd; 333 | var type, len; 334 | if( typeof (str) != 'string') 335 | throw (new Error('type must be a Javascript string')); 336 | endInd = str.lastIndexOf(']'); 337 | if(endInd == -1) { 338 | if(str.lastIndexOf('[') != -1) 339 | throw (new Error('found invalid type with \'[\' but ' + 'no corresponding \']\'')); 340 | 341 | return ( { 342 | type : str 343 | }); 344 | } 345 | begInd = str.lastIndexOf('['); 346 | if(begInd == -1) 347 | throw (new Error('found invalid type with \']\' but ' + 'no corresponding \'[\'')); 348 | 349 | if(begInd >= endInd) 350 | throw (new Error('malformed type, \']\' appears before \'[\'')); 351 | type = str.substring(0, begInd); 352 | len = str.substring(begInd + 1, endInd); 353 | 354 | return ( { 355 | type : type, 356 | len : len 357 | }); 358 | } 359 | 360 | /* 361 | * Given a request validate that all of the fields for it are valid and make 362 | * sense. This includes verifying the following notions: 363 | * - Each type requested is present in types 364 | * - Only allow a name for a field to be specified once 365 | * - If an array is specified, validate that the requested field exists and 366 | * comes before it. 367 | * - If fields is defined, check that each entry has the occurrence of field 368 | */ 369 | function ctCheckReq(def, types, fields) { 370 | var ii, jj; 371 | var req, keys, key, exists; 372 | var found = {}; 373 | 374 | if(!( def instanceof Array)) 375 | throw (new Error('definition is not an array')); 376 | 377 | if(def.length === 0) 378 | throw (new Error('definition must have at least one element')); 379 | 380 | for( ii = 0; ii < def.length; ii++) { 381 | req = def[ii]; 382 | if(!( req instanceof Object)) 383 | throw (new Error('definition must be an array of' + 'objects')); 384 | keys = Object.keys(req); 385 | if(keys.length != 1) 386 | throw (new Error('definition entry must only have ' + 'one key')); 387 | 388 | if(keys[0] in found) 389 | throw (new Error('Specified name already ' + 'specified: ' + keys[0])); 390 | 391 | if(!('type' in req[keys[0]])) 392 | throw (new Error('missing required type definition')); 393 | key = ctParseType(req[keys[0]]['type']); 394 | 395 | /* 396 | * We may have nested arrays, we need to check the validity of 397 | * the types until the len field is undefined in key. However, 398 | * each time len is defined we need to verify it is either an 399 | * integer or corresponds to an already seen key. 400 | */ 401 | while(key['len'] !== undefined) { 402 | if(isNaN(parseInt(key['len'], 10))) { 403 | exists = false; 404 | if(!(key['len'] in found)) 405 | throw (new Error('Given an array ' + 'length without a matching type')); 406 | 407 | } 408 | key = ctParseType(key['type']); 409 | } 410 | 411 | /* Now we can validate if the type is valid */ 412 | if(!(key['type'] in types)) 413 | throw (new Error('type not found or typdefed: ' + key['type'])); 414 | 415 | /* Check for any required fields */ 416 | if(fields !== undefined) { 417 | for( jj = 0; jj < fields.length; jj++) { 418 | if(!(fields[jj] in req[keys[0]])) 419 | throw (new Error('Missing required ' + 'field: ' + fields[jj])); 420 | } 421 | } 422 | 423 | found[keys[0]] = true; 424 | } 425 | } 426 | 427 | /* 428 | * Create a new instance of the parser. Each parser has its own store of 429 | * typedefs and endianness. Conf is an object with the following values: 430 | * 431 | * endian Either 'big' or 'little' do determine the endianness we 432 | * want to read from or write to. 433 | * 434 | */ 435 | function CTypeParser(conf) { 436 | if(!conf) 437 | throw (new Error('missing required argument')); 438 | 439 | if(!('endian' in conf)) 440 | throw (new Error('missing required endian value')); 441 | 442 | if(conf['endian'] != 'big' && conf['endian'] != 'little') 443 | throw (new Error('Invalid endian type')); 444 | 445 | this.endian = conf['endian']; 446 | this.types = ctGetBasicTypes(); 447 | } 448 | 449 | /* 450 | * Sets the current endian value for the Parser. If the value is not valid, 451 | * throws an Error. 452 | * 453 | * endian Either 'big' or 'little' do determine the endianness we 454 | * want to read from or write to. 455 | * 456 | */ 457 | CTypeParser.prototype.setEndian = function(endian) { 458 | if(endian != 'big' || endian != 'little') 459 | throw (new Error('invalid endian type, must be big or ' + ' little')); 460 | 461 | this.endian = endian; 462 | }; 463 | /* 464 | * Returns the current value of the endian value for the parser. 465 | */ 466 | CTypeParser.prototype.getEndian = function() { 467 | return (this.endian); 468 | }; 469 | /* 470 | * A user has requested to add a type, let us honor their request. Yet, if their 471 | * request doth spurn us, send them unto the Hells which Dante describes. 472 | * 473 | * name The string for the type definition we're adding 474 | * 475 | * value Either a string that is a type/array name or an object 476 | * that describes a struct. 477 | */ 478 | CTypeParser.prototype.typedef = function(name, value) { 479 | var type; 480 | 481 | if(name === undefined) 482 | throw (new (Error('missing required typedef argument: name'))); 483 | 484 | if(value === undefined) 485 | throw (new (Error('missing required typedef argument: value'))); 486 | 487 | if( typeof (name) != 'string') 488 | throw (new (Error('the name of a type must be a string'))); 489 | type = ctParseType(name); 490 | 491 | if(type['len'] !== undefined) 492 | throw (new Error('Cannot have an array in the typedef name')); 493 | 494 | if( name in this.types) 495 | throw (new Error('typedef name already present: ' + name)); 496 | 497 | if( typeof (value) != 'string' && !( value instanceof Array)) 498 | throw (new Error('typedef value must either be a string or ' + 'struct')); 499 | 500 | if( typeof (value) == 'string') { 501 | type = ctParseType(value); 502 | if(type['len'] !== undefined) { 503 | if(isNaN(parseInt(type['len'], 10))) 504 | throw (new (Error('typedef value must use ' + 505 | 'fixed size array when outside of a ' + 506 | 'struct'))); 507 | } 508 | 509 | this.types[name] = value; 510 | } else { 511 | /* We have a struct, validate it */ 512 | ctCheckReq(value, this.types); 513 | this.types[name] = value; 514 | } 515 | }; 516 | /* 517 | * Include all of the typedefs, but none of the built in types. This should be 518 | * treated as read-only. 519 | */ 520 | CTypeParser.prototype.lstypes = function() { 521 | var key; 522 | var ret = {}; 523 | 524 | for(key in this.types) { 525 | if( key in deftypes) 526 | continue; 527 | ret[key] = this.types[key]; 528 | } 529 | 530 | return (ret); 531 | }; 532 | /* 533 | * Given a type string that may have array types that aren't numbers, try and 534 | * fill them in from the values object. The object should be of the format where 535 | * indexing into it should return a number for that type. 536 | * 537 | * str The type string 538 | * 539 | * values An object that can be used to fulfill type information 540 | */ 541 | function ctResolveArray(str, values) { 542 | var ret = ''; 543 | var type = ctParseType(str); 544 | 545 | while(type['len'] !== undefined) { 546 | if(isNaN(parseInt(type['len'], 10))) { 547 | if( typeof (values[type['len']]) != 'number') 548 | throw (new Error('cannot sawp in non-number ' + 'for array value')); 549 | ret = '[' + values[type['len']] + ']' + ret; 550 | } else { 551 | ret = '[' + type['len'] + ']' + ret; 552 | } 553 | type = ctParseType(type['type']); 554 | } 555 | ret = type['type'] + ret; 556 | 557 | return (ret); 558 | } 559 | 560 | /* 561 | * [private] Either the typedef resolves to another type string or to a struct. 562 | * If it resolves to a struct, we just pass it off to read struct. If not, we 563 | * can just pass it off to read entry. 564 | */ 565 | CTypeParser.prototype.resolveTypedef = function(type, dispatch, buffer, offset, value) { 566 | var pt; 567 | console.log(type); 568 | ASSERT.ok( type in this.types); 569 | console.log(type + ':' + this.types[type]); 570 | if( typeof (this.types[type]) == 'string') { 571 | pt = ctParseType(this.types[type]); 572 | if(dispatch == 'read') 573 | return (this.readEntry(pt, buffer, offset)); 574 | else if(dispatch == 'write') 575 | return (this.writeEntry(value, pt, buffer, offset)); 576 | else 577 | throw (new Error('invalid dispatch type to ' + 'resolveTypedef')); 578 | } else { 579 | if(dispatch == 'read') 580 | return (this.readStruct(this.types[type], buffer, offset)); 581 | else if(dispatch == 'write') 582 | return (this.readStruct(value, this.types[type], buffer, offset)); 583 | else 584 | throw (new Error('invalid dispatch type to ' + 'resolveTypedef')); 585 | } 586 | 587 | }; 588 | /* 589 | * [private] Try and read in the specific entry. 590 | */ 591 | CTypeParser.prototype.readEntry = function(type, buffer, offset) { 592 | var parse, len; 593 | 594 | /* 595 | * Because we want to special case char[]s this is unfortunately 596 | * a bit uglier than it really should be. We want to special 597 | * case char[]s so that we return a node buffer, thus they are a 598 | * first class type where as all other arrays just call into a 599 | * generic array routine which calls their data-specific routine 600 | * the specified number of times. 601 | * 602 | * The valid dispatch options we have are: 603 | * - Array and char => char[] handler 604 | * - Generic array handler 605 | * - Generic typedef handler 606 | * - Basic type handler 607 | */ 608 | if(type['len'] !== undefined) { 609 | len = parseInt(type['len'], 10); 610 | if(isNaN(len)) 611 | throw (new Error('somehow got a non-numeric length')); 612 | 613 | if(type['type'] == 'char') 614 | parse = deftypes['char[]']['read'](len, this.endian, buffer, offset); 615 | else 616 | parse = this.readArray(type['type'], len, buffer, offset); 617 | } else { 618 | if(type['type'] in deftypes) 619 | parse = deftypes[type['type']]['read'](this.endian, buffer, offset); 620 | else 621 | parse = this.resolveTypedef(type['type'], 'read', buffer, offset); 622 | } 623 | 624 | return (parse); 625 | }; 626 | /* 627 | * [private] Read an array of data 628 | */ 629 | CTypeParser.prototype.readArray = function(type, length, buffer, offset) { 630 | var ii, ent, pt; 631 | var baseOffset = offset; 632 | var ret = new Array(length); 633 | pt = ctParseType(type); 634 | 635 | for( ii = 0; ii < length; ii++) { 636 | ent = this.readEntry(pt, buffer, offset); 637 | offset += ent['size']; 638 | ret[ii] = ent['value']; 639 | } 640 | 641 | return ( { 642 | value : ret, 643 | size : offset - baseOffset 644 | }); 645 | }; 646 | /* 647 | * [private] Read a single struct in. 648 | */ 649 | CTypeParser.prototype.readStruct = function(def, buffer, offset) { 650 | var parse, ii, type, entry, key; 651 | var baseOffset = offset; 652 | var ret = {}; 653 | 654 | /* Walk it and handle doing what's necessary */ 655 | for( ii = 0; ii < def.length; ii++) { 656 | key = Object.keys(def[ii])[0]; 657 | entry = def[ii][key]; 658 | 659 | /* Resolve all array values */ 660 | type = ctParseType(ctResolveArray(entry['type'], ret)); 661 | 662 | if('offset' in entry) 663 | offset = baseOffset + entry['offset']; 664 | parse = this.readEntry(type, buffer, offset); 665 | offset += parse['size']; 666 | ret[key] = parse['value']; 667 | } 668 | 669 | return ( { 670 | values : ret, 671 | size : (offset - baseOffset) 672 | }); 673 | }; 674 | /* 675 | * This is what we were born to do. We read the data from a buffer and return it 676 | * in an object whose keys match the values from the object. 677 | * 678 | * def The array definition of the data to read in 679 | * 680 | * buffer The buffer to read data from 681 | * 682 | * offset The offset to start writing to 683 | * 684 | * Returns an object where each key corresponds to an entry in def and the value 685 | * is the read value. 686 | */ 687 | CTypeParser.prototype.readData = function(def, buffer, offset) { 688 | /* Sanity check for arguments */ 689 | if(def === undefined) 690 | throw (new Error('missing definition for what we should be' + 'parsing')); 691 | 692 | if(buffer === undefined) 693 | throw (new Error('missing buffer for what we should be' + 'parsing')); 694 | 695 | if(offset === undefined) 696 | throw (new Error('missing offset for what we should be' + 'parsing')); 697 | 698 | /* Sanity check the object definition */ 699 | ctCheckReq(def, this.types); 700 | 701 | return (this.readStruct(def, buffer, offset)['values']); 702 | }; 703 | /* 704 | * [private] Write out an array of data 705 | */ 706 | CTypeParser.prototype.writeArray = function(value, type, length, buffer, offset) { 707 | var ii, pt; 708 | var baseOffset = offset; 709 | if(!( value instanceof Array)) 710 | throw (new Error('asked to write an array, but value is not ' + 'an array')); 711 | 712 | if(value.length != length) 713 | throw (new Error('asked to write array of length ' + length + ' but that does not match value length: ' + value.length)); 714 | pt = ctParseType(type); 715 | for( ii = 0; ii < length; ii++) 716 | offset += this.writeEntry(value[ii], pt, buffer, offset); 717 | 718 | return (offset - baseOffset); 719 | }; 720 | /* 721 | * [private] Write the specific entry 722 | */ 723 | CTypeParser.prototype.writeEntry = function(value, type, buffer, offset) { 724 | var len, ret; 725 | 726 | if(type['len'] !== undefined) { 727 | len = parseInt(type['len'], 10); 728 | if(isNaN(len)) 729 | throw (new Error('somehow got a non-numeric length')); 730 | 731 | if(type['type'] == 'char') 732 | ret = deftypes['char[]']['write'](value, len, this.endian, buffer, offset); 733 | else 734 | ret = this.writeArray(value, type['type'], len, buffer, offset); 735 | } else { 736 | if(type['type'] in deftypes) 737 | ret = deftypes[type['type']]['write'](value, this.endian, buffer, offset); 738 | else 739 | ret = this.resolveTypedef(type['type'], 'write', buffer, offset, value); 740 | } 741 | 742 | return (ret); 743 | }; 744 | /* 745 | * [private] Write a single struct out. 746 | */ 747 | CTypeParser.prototype.writeStruct = function(def, buffer, offset) { 748 | var ii, entry, type, key; 749 | var baseOffset = offset; 750 | var vals = {}; 751 | 752 | for( ii = 0; ii < def.length; ii++) { 753 | key = Object.keys(def[ii])[0]; 754 | entry = def[ii][key]; 755 | type = ctParseType(ctResolveArray(entry['type'], vals)); 756 | 757 | if('offset' in entry) 758 | offset = baseOffset + entry['offset']; 759 | offset += this.writeEntry(entry['value'], type, buffer, offset); 760 | 761 | /* Now that we've written it out, we can use it for arrays */ 762 | vals[key] = entry['value']; 763 | } 764 | }; 765 | /* 766 | * This is the second half of what we were born to do, write out the data 767 | * itself. 768 | * 769 | * def The array definition of the data to write out with 770 | * values 771 | * 772 | * buffer The buffer to write to 773 | * 774 | * offset The offset in the buffer to write to 775 | */ 776 | CTypeParser.prototype.write = function(def, buffer, offset) { 777 | if(def === undefined) 778 | throw (new Error('missing definition for what we should be' + 'parsing')); 779 | 780 | if(buffer === undefined) 781 | throw (new Error('missing buffer for what we should be' + 'parsing')); 782 | 783 | if(offset === undefined) 784 | throw (new Error('missing offset for what we should be' + 'parsing')); 785 | 786 | ctCheckReq(def, this.types, ['value']); 787 | 788 | this.writeStruct(def, buffer, offset); 789 | }; 790 | /* 791 | * Functions to go to and from 64 bit numbers in a way that is compatible with 792 | * Javascript limitations. There are two sets. One where the user is okay with 793 | * an approximation and one where they are definitely not okay with an 794 | * approximation. 795 | */ 796 | 797 | /* 798 | * Attempts to convert an array of two integers returned from rsint64 / ruint64 799 | * into an absolute 64 bit number. If however the value would exceed 2^52 this 800 | * will instead throw an error. The mantissa in a double is a 52 bit number and 801 | * rather than potentially give you a value that is an approximation this will 802 | * error. If you would rather an approximation, please see toApprox64. 803 | * 804 | * val An array of two 32-bit integers 805 | */ 806 | function toAbs64(val) { 807 | if(val === undefined) 808 | throw (new Error('missing required arg: value')); 809 | 810 | if(!( val instanceof Array)) 811 | throw (new Error('value must be an array')); 812 | 813 | if(val.length != 2) 814 | throw (new Error('value must be an array of length 2')); 815 | 816 | /* We have 20 bits worth of precision in this range */ 817 | if(val[0] >= 0x100000) 818 | throw (new Error('value would become approximated')); 819 | 820 | return (val[0] * Math.pow(2, 32) + val[1]); 821 | } 822 | 823 | /* 824 | * Will return the 64 bit value as returned in an array from rsint64 / ruint64 825 | * to a value as close as it can. Note that Javascript stores all numbers as a 826 | * double and the mantissa only has 52 bits. Thus this version may approximate 827 | * the value. 828 | * 829 | * val An array of two 32-bit integers 830 | */ 831 | function toApprox64(val) { 832 | if(val === undefined) 833 | throw (new Error('missing required arg: value')); 834 | 835 | if(!( val instanceof Array)) 836 | throw (new Error('value must be an array')); 837 | 838 | if(val.length != 2) 839 | throw (new Error('value must be an array of length 2')); 840 | 841 | return (Math.pow(2, 32) * val[0] + val[1]); 842 | } 843 | 844 | /* 845 | * Export the few things we actually want to. Currently this is just the CType 846 | * Parser and ctio. 847 | */ 848 | exports.Parser = CTypeParser; 849 | 850 | exports.ruint8 = mod_ctio.ruint8; 851 | exports.ruint16 = mod_ctio.ruint16; 852 | exports.ruint32 = mod_ctio.ruint32; 853 | exports.ruint64 = mod_ctio.ruint64; 854 | exports.wuint8 = mod_ctio.wuint8; 855 | exports.wuint16 = mod_ctio.wuint16; 856 | exports.wuint32 = mod_ctio.wuint32; 857 | exports.wuint64 = mod_ctio.wuint64; 858 | 859 | exports.rsint8 = mod_ctio.rsint8; 860 | exports.rsint16 = mod_ctio.rsint16; 861 | exports.rsint32 = mod_ctio.rsint32; 862 | exports.rsint64 = mod_ctio.rsint64; 863 | exports.wsint8 = mod_ctio.wsint8; 864 | exports.wsint16 = mod_ctio.wsint16; 865 | exports.wsint32 = mod_ctio.wsint32; 866 | exports.wsint64 = mod_ctio.wsint64; 867 | 868 | exports.rfloat = mod_ctio.rfloat; 869 | exports.rdouble = mod_ctio.rdouble; 870 | exports.wfloat = mod_ctio.wfloat; 871 | exports.wdouble = mod_ctio.wdouble; 872 | exports.deftypes = deftypes -------------------------------------------------------------------------------- /examples/voter/voter/public/stylesheets/jquery-ui-1.8.17.custom.css: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery UI CSS Framework 1.8.17 3 | * 4 | * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) 5 | * Dual licensed under the MIT or GPL Version 2 licenses. 6 | * http://jquery.org/license 7 | * 8 | * http://docs.jquery.com/UI/Theming/API 9 | */ 10 | 11 | /* Layout helpers 12 | ----------------------------------*/ 13 | .ui-helper-hidden { display: none; } 14 | .ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px, 1px, 1px, 1px); clip: rect(1px,1px,1px,1px); } 15 | .ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } 16 | .ui-helper-clearfix:before, .ui-helper-clearfix:after { content: ""; display: table; } 17 | .ui-helper-clearfix:after { clear: both; } 18 | .ui-helper-clearfix { zoom: 1; } 19 | .ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } 20 | 21 | 22 | /* Interaction Cues 23 | ----------------------------------*/ 24 | .ui-state-disabled { cursor: default !important; } 25 | 26 | 27 | /* Icons 28 | ----------------------------------*/ 29 | 30 | /* states and images */ 31 | .ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } 32 | 33 | 34 | /* Misc visuals 35 | ----------------------------------*/ 36 | 37 | /* Overlays */ 38 | .ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } 39 | 40 | 41 | /* 42 | * jQuery UI CSS Framework 1.8.17 43 | * 44 | * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) 45 | * Dual licensed under the MIT or GPL Version 2 licenses. 46 | * http://jquery.org/license 47 | * 48 | * http://docs.jquery.com/UI/Theming/API 49 | * 50 | * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS,%20Tahoma,%20Verdana,%20Arial,%20sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=f6a828&bgTextureHeader=12_gloss_wave.png&bgImgOpacityHeader=35&borderColorHeader=e78f08&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=03_highlight_soft.png&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=1c94c4&iconColorDefault=ef8c08&bgColorHover=fdf5ce&bgTextureHover=02_glass.png&bgImgOpacityHover=100&borderColorHover=fbcb09&fcHover=c77405&iconColorHover=ef8c08&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=fbd850&fcActive=eb8f00&iconColorActive=ef8c08&bgColorHighlight=ffe45c&bgTextureHighlight=03_highlight_soft.png&bgImgOpacityHighlight=75&borderColorHighlight=fed22f&fcHighlight=363636&iconColorHighlight=228ef1&bgColorError=b81900&bgTextureError=08_diagonals_thick.png&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=08_diagonals_thick.png&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=01_flat.png&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px 51 | */ 52 | 53 | 54 | /* Component containers 55 | ----------------------------------*/ 56 | .ui-widget { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1.1em; } 57 | .ui-widget .ui-widget { font-size: 1em; } 58 | .ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1em; } 59 | .ui-widget-content { border: 1px solid #dddddd; background: #eeeeee url(images/ui-bg_highlight-soft_100_eeeeee_1x100.png) 50% top repeat-x; color: #333333; } 60 | .ui-widget-content a { color: #333333; } 61 | .ui-widget-header { border: 1px solid #e78f08; background: #f6a828 url(images/ui-bg_gloss-wave_35_f6a828_500x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; } 62 | .ui-widget-header a { color: #ffffff; } 63 | 64 | /* Interaction states 65 | ----------------------------------*/ 66 | .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #cccccc; background: #f6f6f6 url(images/ui-bg_glass_100_f6f6f6_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #1c94c4; } 67 | .ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #1c94c4; text-decoration: none; } 68 | .ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #fbcb09; background: #fdf5ce url(images/ui-bg_glass_100_fdf5ce_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #c77405; } 69 | .ui-state-hover a, .ui-state-hover a:hover { color: #c77405; text-decoration: none; } 70 | .ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #fbd850; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #eb8f00; } 71 | .ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #eb8f00; text-decoration: none; } 72 | .ui-widget :active { outline: none; } 73 | 74 | /* Interaction Cues 75 | ----------------------------------*/ 76 | .ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fed22f; background: #ffe45c url(images/ui-bg_highlight-soft_75_ffe45c_1x100.png) 50% top repeat-x; color: #363636; } 77 | .ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; } 78 | .ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #b81900 url(images/ui-bg_diagonals-thick_18_b81900_40x40.png) 50% 50% repeat; color: #ffffff; } 79 | .ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #ffffff; } 80 | .ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #ffffff; } 81 | .ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; } 82 | .ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; } 83 | .ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; } 84 | 85 | /* Icons 86 | ----------------------------------*/ 87 | 88 | /* states and images */ 89 | .ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); } 90 | .ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } 91 | .ui-widget-header .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); } 92 | .ui-state-default .ui-icon { background-image: url(images/ui-icons_ef8c08_256x240.png); } 93 | .ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); } 94 | .ui-state-active .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); } 95 | .ui-state-highlight .ui-icon {background-image: url(images/ui-icons_228ef1_256x240.png); } 96 | .ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_ffd27a_256x240.png); } 97 | 98 | /* positioning */ 99 | .ui-icon-carat-1-n { background-position: 0 0; } 100 | .ui-icon-carat-1-ne { background-position: -16px 0; } 101 | .ui-icon-carat-1-e { background-position: -32px 0; } 102 | .ui-icon-carat-1-se { background-position: -48px 0; } 103 | .ui-icon-carat-1-s { background-position: -64px 0; } 104 | .ui-icon-carat-1-sw { background-position: -80px 0; } 105 | .ui-icon-carat-1-w { background-position: -96px 0; } 106 | .ui-icon-carat-1-nw { background-position: -112px 0; } 107 | .ui-icon-carat-2-n-s { background-position: -128px 0; } 108 | .ui-icon-carat-2-e-w { background-position: -144px 0; } 109 | .ui-icon-triangle-1-n { background-position: 0 -16px; } 110 | .ui-icon-triangle-1-ne { background-position: -16px -16px; } 111 | .ui-icon-triangle-1-e { background-position: -32px -16px; } 112 | .ui-icon-triangle-1-se { background-position: -48px -16px; } 113 | .ui-icon-triangle-1-s { background-position: -64px -16px; } 114 | .ui-icon-triangle-1-sw { background-position: -80px -16px; } 115 | .ui-icon-triangle-1-w { background-position: -96px -16px; } 116 | .ui-icon-triangle-1-nw { background-position: -112px -16px; } 117 | .ui-icon-triangle-2-n-s { background-position: -128px -16px; } 118 | .ui-icon-triangle-2-e-w { background-position: -144px -16px; } 119 | .ui-icon-arrow-1-n { background-position: 0 -32px; } 120 | .ui-icon-arrow-1-ne { background-position: -16px -32px; } 121 | .ui-icon-arrow-1-e { background-position: -32px -32px; } 122 | .ui-icon-arrow-1-se { background-position: -48px -32px; } 123 | .ui-icon-arrow-1-s { background-position: -64px -32px; } 124 | .ui-icon-arrow-1-sw { background-position: -80px -32px; } 125 | .ui-icon-arrow-1-w { background-position: -96px -32px; } 126 | .ui-icon-arrow-1-nw { background-position: -112px -32px; } 127 | .ui-icon-arrow-2-n-s { background-position: -128px -32px; } 128 | .ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } 129 | .ui-icon-arrow-2-e-w { background-position: -160px -32px; } 130 | .ui-icon-arrow-2-se-nw { background-position: -176px -32px; } 131 | .ui-icon-arrowstop-1-n { background-position: -192px -32px; } 132 | .ui-icon-arrowstop-1-e { background-position: -208px -32px; } 133 | .ui-icon-arrowstop-1-s { background-position: -224px -32px; } 134 | .ui-icon-arrowstop-1-w { background-position: -240px -32px; } 135 | .ui-icon-arrowthick-1-n { background-position: 0 -48px; } 136 | .ui-icon-arrowthick-1-ne { background-position: -16px -48px; } 137 | .ui-icon-arrowthick-1-e { background-position: -32px -48px; } 138 | .ui-icon-arrowthick-1-se { background-position: -48px -48px; } 139 | .ui-icon-arrowthick-1-s { background-position: -64px -48px; } 140 | .ui-icon-arrowthick-1-sw { background-position: -80px -48px; } 141 | .ui-icon-arrowthick-1-w { background-position: -96px -48px; } 142 | .ui-icon-arrowthick-1-nw { background-position: -112px -48px; } 143 | .ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } 144 | .ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } 145 | .ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } 146 | .ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } 147 | .ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } 148 | .ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } 149 | .ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } 150 | .ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } 151 | .ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } 152 | .ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } 153 | .ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } 154 | .ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } 155 | .ui-icon-arrowreturn-1-w { background-position: -64px -64px; } 156 | .ui-icon-arrowreturn-1-n { background-position: -80px -64px; } 157 | .ui-icon-arrowreturn-1-e { background-position: -96px -64px; } 158 | .ui-icon-arrowreturn-1-s { background-position: -112px -64px; } 159 | .ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } 160 | .ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } 161 | .ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } 162 | .ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } 163 | .ui-icon-arrow-4 { background-position: 0 -80px; } 164 | .ui-icon-arrow-4-diag { background-position: -16px -80px; } 165 | .ui-icon-extlink { background-position: -32px -80px; } 166 | .ui-icon-newwin { background-position: -48px -80px; } 167 | .ui-icon-refresh { background-position: -64px -80px; } 168 | .ui-icon-shuffle { background-position: -80px -80px; } 169 | .ui-icon-transfer-e-w { background-position: -96px -80px; } 170 | .ui-icon-transferthick-e-w { background-position: -112px -80px; } 171 | .ui-icon-folder-collapsed { background-position: 0 -96px; } 172 | .ui-icon-folder-open { background-position: -16px -96px; } 173 | .ui-icon-document { background-position: -32px -96px; } 174 | .ui-icon-document-b { background-position: -48px -96px; } 175 | .ui-icon-note { background-position: -64px -96px; } 176 | .ui-icon-mail-closed { background-position: -80px -96px; } 177 | .ui-icon-mail-open { background-position: -96px -96px; } 178 | .ui-icon-suitcase { background-position: -112px -96px; } 179 | .ui-icon-comment { background-position: -128px -96px; } 180 | .ui-icon-person { background-position: -144px -96px; } 181 | .ui-icon-print { background-position: -160px -96px; } 182 | .ui-icon-trash { background-position: -176px -96px; } 183 | .ui-icon-locked { background-position: -192px -96px; } 184 | .ui-icon-unlocked { background-position: -208px -96px; } 185 | .ui-icon-bookmark { background-position: -224px -96px; } 186 | .ui-icon-tag { background-position: -240px -96px; } 187 | .ui-icon-home { background-position: 0 -112px; } 188 | .ui-icon-flag { background-position: -16px -112px; } 189 | .ui-icon-calendar { background-position: -32px -112px; } 190 | .ui-icon-cart { background-position: -48px -112px; } 191 | .ui-icon-pencil { background-position: -64px -112px; } 192 | .ui-icon-clock { background-position: -80px -112px; } 193 | .ui-icon-disk { background-position: -96px -112px; } 194 | .ui-icon-calculator { background-position: -112px -112px; } 195 | .ui-icon-zoomin { background-position: -128px -112px; } 196 | .ui-icon-zoomout { background-position: -144px -112px; } 197 | .ui-icon-search { background-position: -160px -112px; } 198 | .ui-icon-wrench { background-position: -176px -112px; } 199 | .ui-icon-gear { background-position: -192px -112px; } 200 | .ui-icon-heart { background-position: -208px -112px; } 201 | .ui-icon-star { background-position: -224px -112px; } 202 | .ui-icon-link { background-position: -240px -112px; } 203 | .ui-icon-cancel { background-position: 0 -128px; } 204 | .ui-icon-plus { background-position: -16px -128px; } 205 | .ui-icon-plusthick { background-position: -32px -128px; } 206 | .ui-icon-minus { background-position: -48px -128px; } 207 | .ui-icon-minusthick { background-position: -64px -128px; } 208 | .ui-icon-close { background-position: -80px -128px; } 209 | .ui-icon-closethick { background-position: -96px -128px; } 210 | .ui-icon-key { background-position: -112px -128px; } 211 | .ui-icon-lightbulb { background-position: -128px -128px; } 212 | .ui-icon-scissors { background-position: -144px -128px; } 213 | .ui-icon-clipboard { background-position: -160px -128px; } 214 | .ui-icon-copy { background-position: -176px -128px; } 215 | .ui-icon-contact { background-position: -192px -128px; } 216 | .ui-icon-image { background-position: -208px -128px; } 217 | .ui-icon-video { background-position: -224px -128px; } 218 | .ui-icon-script { background-position: -240px -128px; } 219 | .ui-icon-alert { background-position: 0 -144px; } 220 | .ui-icon-info { background-position: -16px -144px; } 221 | .ui-icon-notice { background-position: -32px -144px; } 222 | .ui-icon-help { background-position: -48px -144px; } 223 | .ui-icon-check { background-position: -64px -144px; } 224 | .ui-icon-bullet { background-position: -80px -144px; } 225 | .ui-icon-radio-off { background-position: -96px -144px; } 226 | .ui-icon-radio-on { background-position: -112px -144px; } 227 | .ui-icon-pin-w { background-position: -128px -144px; } 228 | .ui-icon-pin-s { background-position: -144px -144px; } 229 | .ui-icon-play { background-position: 0 -160px; } 230 | .ui-icon-pause { background-position: -16px -160px; } 231 | .ui-icon-seek-next { background-position: -32px -160px; } 232 | .ui-icon-seek-prev { background-position: -48px -160px; } 233 | .ui-icon-seek-end { background-position: -64px -160px; } 234 | .ui-icon-seek-start { background-position: -80px -160px; } 235 | /* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ 236 | .ui-icon-seek-first { background-position: -80px -160px; } 237 | .ui-icon-stop { background-position: -96px -160px; } 238 | .ui-icon-eject { background-position: -112px -160px; } 239 | .ui-icon-volume-off { background-position: -128px -160px; } 240 | .ui-icon-volume-on { background-position: -144px -160px; } 241 | .ui-icon-power { background-position: 0 -176px; } 242 | .ui-icon-signal-diag { background-position: -16px -176px; } 243 | .ui-icon-signal { background-position: -32px -176px; } 244 | .ui-icon-battery-0 { background-position: -48px -176px; } 245 | .ui-icon-battery-1 { background-position: -64px -176px; } 246 | .ui-icon-battery-2 { background-position: -80px -176px; } 247 | .ui-icon-battery-3 { background-position: -96px -176px; } 248 | .ui-icon-circle-plus { background-position: 0 -192px; } 249 | .ui-icon-circle-minus { background-position: -16px -192px; } 250 | .ui-icon-circle-close { background-position: -32px -192px; } 251 | .ui-icon-circle-triangle-e { background-position: -48px -192px; } 252 | .ui-icon-circle-triangle-s { background-position: -64px -192px; } 253 | .ui-icon-circle-triangle-w { background-position: -80px -192px; } 254 | .ui-icon-circle-triangle-n { background-position: -96px -192px; } 255 | .ui-icon-circle-arrow-e { background-position: -112px -192px; } 256 | .ui-icon-circle-arrow-s { background-position: -128px -192px; } 257 | .ui-icon-circle-arrow-w { background-position: -144px -192px; } 258 | .ui-icon-circle-arrow-n { background-position: -160px -192px; } 259 | .ui-icon-circle-zoomin { background-position: -176px -192px; } 260 | .ui-icon-circle-zoomout { background-position: -192px -192px; } 261 | .ui-icon-circle-check { background-position: -208px -192px; } 262 | .ui-icon-circlesmall-plus { background-position: 0 -208px; } 263 | .ui-icon-circlesmall-minus { background-position: -16px -208px; } 264 | .ui-icon-circlesmall-close { background-position: -32px -208px; } 265 | .ui-icon-squaresmall-plus { background-position: -48px -208px; } 266 | .ui-icon-squaresmall-minus { background-position: -64px -208px; } 267 | .ui-icon-squaresmall-close { background-position: -80px -208px; } 268 | .ui-icon-grip-dotted-vertical { background-position: 0 -224px; } 269 | .ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } 270 | .ui-icon-grip-solid-vertical { background-position: -32px -224px; } 271 | .ui-icon-grip-solid-horizontal { background-position: -48px -224px; } 272 | .ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } 273 | .ui-icon-grip-diagonal-se { background-position: -80px -224px; } 274 | 275 | 276 | /* Misc visuals 277 | ----------------------------------*/ 278 | 279 | /* Corner radius */ 280 | .ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -khtml-border-top-left-radius: 4px; border-top-left-radius: 4px; } 281 | .ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; -khtml-border-top-right-radius: 4px; border-top-right-radius: 4px; } 282 | .ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; -khtml-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; } 283 | .ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; -khtml-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } 284 | 285 | /* Overlays */ 286 | .ui-widget-overlay { background: #666666 url(images/ui-bg_diagonals-thick_20_666666_40x40.png) 50% 50% repeat; opacity: .50;filter:Alpha(Opacity=50); } 287 | .ui-widget-shadow { margin: -5px 0 0 -5px; padding: 5px; background: #000000 url(images/ui-bg_flat_10_000000_40x100.png) 50% 50% repeat-x; opacity: .20;filter:Alpha(Opacity=20); -moz-border-radius: 5px; -khtml-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; }/* 288 | * jQuery UI Accordion 1.8.17 289 | * 290 | * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) 291 | * Dual licensed under the MIT or GPL Version 2 licenses. 292 | * http://jquery.org/license 293 | * 294 | * http://docs.jquery.com/UI/Accordion#theming 295 | */ 296 | /* IE/Win - Fix animation bug - #4615 */ 297 | .ui-accordion { width: 100%; } 298 | .ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; } 299 | .ui-accordion .ui-accordion-li-fix { display: inline; } 300 | .ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; } 301 | .ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; } 302 | .ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; } 303 | .ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; } 304 | .ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; } 305 | .ui-accordion .ui-accordion-content-active { display: block; } 306 | /* 307 | * jQuery UI Autocomplete 1.8.17 308 | * 309 | * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) 310 | * Dual licensed under the MIT or GPL Version 2 licenses. 311 | * http://jquery.org/license 312 | * 313 | * http://docs.jquery.com/UI/Autocomplete#theming 314 | */ 315 | .ui-autocomplete { position: absolute; cursor: default; } 316 | 317 | /* workarounds */ 318 | * html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */ 319 | 320 | /* 321 | * jQuery UI Menu 1.8.17 322 | * 323 | * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) 324 | * Dual licensed under the MIT or GPL Version 2 licenses. 325 | * http://jquery.org/license 326 | * 327 | * http://docs.jquery.com/UI/Menu#theming 328 | */ 329 | .ui-menu { 330 | list-style:none; 331 | padding: 2px; 332 | margin: 0; 333 | display:block; 334 | float: left; 335 | } 336 | .ui-menu .ui-menu { 337 | margin-top: -3px; 338 | } 339 | .ui-menu .ui-menu-item { 340 | margin:0; 341 | padding: 0; 342 | zoom: 1; 343 | float: left; 344 | clear: left; 345 | width: 100%; 346 | } 347 | .ui-menu .ui-menu-item a { 348 | text-decoration:none; 349 | display:block; 350 | padding:.2em .4em; 351 | line-height:1.5; 352 | zoom:1; 353 | } 354 | .ui-menu .ui-menu-item a.ui-state-hover, 355 | .ui-menu .ui-menu-item a.ui-state-active { 356 | font-weight: normal; 357 | margin: -1px; 358 | } 359 | /* 360 | * jQuery UI Button 1.8.17 361 | * 362 | * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) 363 | * Dual licensed under the MIT or GPL Version 2 licenses. 364 | * http://jquery.org/license 365 | * 366 | * http://docs.jquery.com/UI/Button#theming 367 | */ 368 | .ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */ 369 | .ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */ 370 | button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */ 371 | .ui-button-icons-only { width: 3.4em; } 372 | button.ui-button-icons-only { width: 3.7em; } 373 | 374 | /*button text element */ 375 | .ui-button .ui-button-text { display: block; line-height: 1.4; } 376 | .ui-button-text-only .ui-button-text { padding: .4em 1em; } 377 | .ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; } 378 | .ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; } 379 | .ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; } 380 | .ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; } 381 | /* no icon support for input elements, provide padding by default */ 382 | input.ui-button { padding: .4em 1em; } 383 | 384 | /*button icon element(s) */ 385 | .ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; } 386 | .ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; } 387 | .ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; } 388 | .ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } 389 | .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } 390 | 391 | /*button sets*/ 392 | .ui-buttonset { margin-right: 7px; } 393 | .ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; } 394 | 395 | /* workarounds */ 396 | button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */ 397 | /* 398 | * jQuery UI Dialog 1.8.17 399 | * 400 | * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) 401 | * Dual licensed under the MIT or GPL Version 2 licenses. 402 | * http://jquery.org/license 403 | * 404 | * http://docs.jquery.com/UI/Dialog#theming 405 | */ 406 | .ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; } 407 | .ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; } 408 | .ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; } 409 | .ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; } 410 | .ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; } 411 | .ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; } 412 | .ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; } 413 | .ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; } 414 | .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; } 415 | .ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; } 416 | .ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; } 417 | .ui-draggable .ui-dialog-titlebar { cursor: move; } 418 | /* 419 | * jQuery UI Slider 1.8.17 420 | * 421 | * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) 422 | * Dual licensed under the MIT or GPL Version 2 licenses. 423 | * http://jquery.org/license 424 | * 425 | * http://docs.jquery.com/UI/Slider#theming 426 | */ 427 | .ui-slider { position: relative; text-align: left; } 428 | .ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; } 429 | .ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; } 430 | 431 | .ui-slider-horizontal { height: .8em; } 432 | .ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; } 433 | .ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; } 434 | .ui-slider-horizontal .ui-slider-range-min { left: 0; } 435 | .ui-slider-horizontal .ui-slider-range-max { right: 0; } 436 | 437 | .ui-slider-vertical { width: .8em; height: 100px; } 438 | .ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; } 439 | .ui-slider-vertical .ui-slider-range { left: 0; width: 100%; } 440 | .ui-slider-vertical .ui-slider-range-min { bottom: 0; } 441 | .ui-slider-vertical .ui-slider-range-max { top: 0; }/* 442 | * jQuery UI Tabs 1.8.17 443 | * 444 | * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) 445 | * Dual licensed under the MIT or GPL Version 2 licenses. 446 | * http://jquery.org/license 447 | * 448 | * http://docs.jquery.com/UI/Tabs#theming 449 | */ 450 | .ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ 451 | .ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; } 452 | .ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; } 453 | .ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; } 454 | .ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; } 455 | .ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; } 456 | .ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */ 457 | .ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; } 458 | .ui-tabs .ui-tabs-hide { display: none !important; } 459 | /* 460 | * jQuery UI Datepicker 1.8.17 461 | * 462 | * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) 463 | * Dual licensed under the MIT or GPL Version 2 licenses. 464 | * http://jquery.org/license 465 | * 466 | * http://docs.jquery.com/UI/Datepicker#theming 467 | */ 468 | .ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; } 469 | .ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; } 470 | .ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; } 471 | .ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; } 472 | .ui-datepicker .ui-datepicker-prev { left:2px; } 473 | .ui-datepicker .ui-datepicker-next { right:2px; } 474 | .ui-datepicker .ui-datepicker-prev-hover { left:1px; } 475 | .ui-datepicker .ui-datepicker-next-hover { right:1px; } 476 | .ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; } 477 | .ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; } 478 | .ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; } 479 | .ui-datepicker select.ui-datepicker-month-year {width: 100%;} 480 | .ui-datepicker select.ui-datepicker-month, 481 | .ui-datepicker select.ui-datepicker-year { width: 49%;} 482 | .ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; } 483 | .ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; } 484 | .ui-datepicker td { border: 0; padding: 1px; } 485 | .ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; } 486 | .ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; } 487 | .ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; } 488 | .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; } 489 | 490 | /* with multiple calendars */ 491 | .ui-datepicker.ui-datepicker-multi { width:auto; } 492 | .ui-datepicker-multi .ui-datepicker-group { float:left; } 493 | .ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; } 494 | .ui-datepicker-multi-2 .ui-datepicker-group { width:50%; } 495 | .ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; } 496 | .ui-datepicker-multi-4 .ui-datepicker-group { width:25%; } 497 | .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; } 498 | .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; } 499 | .ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; } 500 | .ui-datepicker-row-break { clear:both; width:100%; font-size:0em; } 501 | 502 | /* RTL support */ 503 | .ui-datepicker-rtl { direction: rtl; } 504 | .ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; } 505 | .ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; } 506 | .ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; } 507 | .ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; } 508 | .ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; } 509 | .ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; } 510 | .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; } 511 | .ui-datepicker-rtl .ui-datepicker-group { float:right; } 512 | .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; } 513 | .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; } 514 | 515 | /* IE6 IFRAME FIX (taken from datepicker 1.5.3 */ 516 | .ui-datepicker-cover { 517 | display: none; /*sorry for IE5*/ 518 | display/**/: block; /*sorry for IE5*/ 519 | position: absolute; /*must have*/ 520 | z-index: -1; /*must have*/ 521 | filter: mask(); /*must have*/ 522 | top: -4px; /*must have*/ 523 | left: -4px; /*must have*/ 524 | width: 200px; /*must have*/ 525 | height: 200px; /*must have*/ 526 | }/* 527 | * jQuery UI Progressbar 1.8.17 528 | * 529 | * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) 530 | * Dual licensed under the MIT or GPL Version 2 licenses. 531 | * http://jquery.org/license 532 | * 533 | * http://docs.jquery.com/UI/Progressbar#theming 534 | */ 535 | .ui-progressbar { height:2em; text-align: left; overflow: hidden; } 536 | .ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; } --------------------------------------------------------------------------------