Commit cc6a3bb8 by Timothy J Warren

Merge branch 'develop'

2 parents 9326992f ad910997
Showing with 1470 additions and 1018 deletions
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
# Matches multiple files with brace expansion notation
# Set default charset
[*]
charset = utf-8
# Tab indentation (no size specified)
[*]
indent_style = tab
indent_size = 4
# Indentation override for all JS under lib directory
[*.js]
indent_size = 2
# Matches the exact files either package.json or .travis.yml
[*.yml]
indent_style = space
indent_size = 2
......@@ -3,4 +3,7 @@ coverage
npm-debug.log
node_modules/*
.sonar/*
test/config.json
\ No newline at end of file
test/config.json
.DS_store
/.idea/
yarn.lock
\ No newline at end of file
before_script:
# Install dependencies
- bash test/docker_install.sh > /dev/null
- npm install
services:
- mysql:latest
- postgres:latest
variables:
MYSQL_ROOT_PASSWORD: foo-bar-baz
MYSQL_DATABASE: test
MYSQL_USER: test
MYSQL_PASSWORD: test
POSTGRES_DB: test
POSTGRES_USER: test
POSTGRES_PASSWORD: test
# This folder is cached between builds
# http://docs.gitlab.com/ce/ci/yaml/README.html#cache
cache:
paths:
- node_modules/
test:6:
image: node:6
script: npm run test
test:latest:
image: node:latest
script: npm run test
reporting:
print: summary
reports:
- lcov
- lcovonly
- clover
- html
- text
dir: ./coverage
\ No newline at end of file
{
"preset": "airbnb",
"validateIndentation": null,
"requireLineFeedAtFileEnd": null,
"disallowSpaceAfterPrefixUnaryOperators": null,
"disallowMultipleVarDecl": null
}
\ No newline at end of file
# Changelog
# 5.0.0
* Re-added firebird as a database
* Replaced all callback interfaces with promises
## 4.0.0
* Changed connection setup to just use a config object - the appropriate adapter object is created by the library.
* Removed mysql adapter, as mysql2 is very similar and does proper prepared statements
......@@ -11,4 +15,4 @@
* Added back tests for `node-firebird` adapter. Using this adapter with promises is not currently supported.
## 3.1.0
* Added support for promises on query execution methods
\ No newline at end of file
* Added support for promises on query execution methods
......@@ -9,6 +9,7 @@ A node query builder for various SQL databases, based on [CodeIgniter](http://ww
### Supported databases
* Firebird (via `node-firebird`)
* Mysql (via `mysql2`)
* PostgreSQL (via `pg`)
* Sqlite (via `dblite`)
......@@ -38,19 +39,9 @@ const nodeQuery = require('ci-node-query')({
// Get the query builder
const query = nodeQuery.getQuery();
query.select('foo')
.from('bar')
.where('x', 3)
.orWhere({y: 2})
.join('baz', 'baz.boo = bar.foo', 'left')
.orderBy('x', 'DESC')
.limit(2, 3)
.get(function(err, result) {
// Handle Results Here
});
// As of version 3.1.0, you can also get promises
var queryPromise = query.select('foo')
// Version 5.0.0 removes all callback interfaces
const queryPromise = query.select('foo')
.from('bar')
.where('x', 3)
.orWhere({y: 2})
......@@ -93,5 +84,7 @@ As of version 2, `where` and `having` type methods parse the values passed to lo
* Generated documentation is in the docs/ folder
* The API is documented in [API.md](./API.md)
* The `tests/adapters` folder contains examples of how to set up a connection for the appropriate database library
* The documentation generated for the latest dev build is also [Available](https://github.timshomepage.net/node-query/docs/)
* The documentation generated for the latest dev build is also [Available](https://github.timshomepage.net/node-query/docs/index.html)
[![js-happiness-style](https://cdn.rawgit.com/JedWatson/happiness/master/badge.svg)](https://github.com/JedWatson/happiness)
.input {
font-family: inherit;
display: block;
width: 100%;
height: 2rem;
padding: .5rem;
margin-bottom: 1rem;
border: 1px solid #ccc;
font-size: .875rem;
border-radius: 3px;
box-sizing: border-box;
}
No preview for this file type
Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
No preview for this file type
No preview for this file type
@font-face{
font-family: 'Source Code Pro';
font-weight: 400;
font-style: normal;
font-stretch: normal;
src: url('EOT/SourceCodePro-Regular.eot') format('embedded-opentype'),
url('WOFF2/TTF/SourceCodePro-Regular.ttf.woff2') format('woff2'),
url('WOFF/OTF/SourceCodePro-Regular.otf.woff') format('woff'),
url('OTF/SourceCodePro-Regular.otf') format('opentype'),
url('TTF/SourceCodePro-Regular.ttf') format('truetype');
}
@font-face{
font-family: 'Source Code Pro';
font-weight: 700;
font-style: normal;
font-stretch: normal;
src: url('EOT/SourceCodePro-Bold.eot') format('embedded-opentype'),
url('WOFF2/TTF/SourceCodePro-Bold.ttf.woff2') format('woff2'),
url('WOFF/OTF/SourceCodePro-Bold.otf.woff') format('woff'),
url('OTF/SourceCodePro-Bold.otf') format('opentype'),
url('TTF/SourceCodePro-Bold.ttf') format('truetype');
}
......@@ -2,39 +2,107 @@
// add anchor links to headers
anchors.options.placement = 'left';
anchors.add().remove('.no-anchor');
anchors.add('h3');
// Filter UI
var tocElements = document.getElementById('toc').getElementsByTagName('a');
document.getElementById('filter-input').addEventListener('keyup', function(e) {
var tocElements = document.getElementById('toc')
.getElementsByTagName('li');
var i, element;
document.getElementById('filter-input')
.addEventListener('keyup', function (e) {
var i, element, children;
// enter key
if (e.keyCode === 13) {
// go to the first displayed item in the toc
for (i = 0; i < tocElements.length; i++) {
element = tocElements[i];
if (!element.classList.contains('display-none')) {
location.replace(element.firstChild.href);
return e.preventDefault();
}
}
}
var match = function () {
return true;
};
var value = this.value.toLowerCase();
if (!value.match(/^\s*$/)) {
match = function (element) {
return element.firstChild.innerHTML.toLowerCase().indexOf(value) !== -1;
};
}
// enter key
if (e.keyCode === 13) {
// go to the first displayed item in the toc
for (i = 0; i < tocElements.length; i++) {
element = tocElements[i];
if (!element.classList.contains('hide')) {
location.replace(element.href);
return e.preventDefault();
children = Array.from(element.getElementsByTagName('li'));
if (match(element) || children.some(match)) {
element.classList.remove('display-none');
} else {
element.classList.add('display-none');
}
}
});
var toggles = document.getElementsByClassName('toggle-step-sibling');
for (var i = 0; i < toggles.length; i++) {
toggles[i].addEventListener('click', toggleStepSibling);
}
function toggleStepSibling() {
var stepSibling = this.parentNode.parentNode.parentNode.getElementsByClassName('toggle-target')[0];
var klass = 'display-none';
if (stepSibling.classList.contains(klass)) {
stepSibling.classList.remove(klass);
stepSibling.innerHTML = '▾';
} else {
stepSibling.classList.add(klass);
stepSibling.innerHTML = '▸';
}
}
var match = function() { return true; },
value = this.value.toLowerCase();
var items = document.getElementsByClassName('toggle-sibling');
for (var j = 0; j < items.length; j++) {
items[j].addEventListener('click', toggleSibling);
}
if (!value.match(/^\s*$/)) {
match = function(text) { return text.toLowerCase().indexOf(value) !== -1; };
function toggleSibling() {
var stepSibling = this.parentNode.getElementsByClassName('toggle-target')[0];
var icon = this.getElementsByClassName('icon')[0];
var klass = 'display-none';
if (stepSibling.classList.contains(klass)) {
stepSibling.classList.remove(klass);
icon.innerHTML = '▾';
} else {
stepSibling.classList.add(klass);
icon.innerHTML = '▸';
}
}
for (i = 0; i < tocElements.length; i++) {
element = tocElements[i];
if (match(element.innerHTML)) {
element.classList.remove('hide');
} else {
element.classList.add('hide');
}
function showHashTarget(targetId) {
var hashTarget = document.getElementById(targetId);
// new target is hidden
if (hashTarget && hashTarget.offsetHeight === 0 &&
hashTarget.parentNode.parentNode.classList.contains('display-none')) {
hashTarget.parentNode.parentNode.classList.remove('display-none');
}
}
window.addEventListener('hashchange', function() {
showHashTarget(location.hash.substring(1));
});
showHashTarget(location.hash.substring(1));
var toclinks = document.getElementsByClassName('pre-open');
for (var k = 0; k < toclinks.length; k++) {
toclinks[k].addEventListener('mousedown', preOpen, false);
}
function preOpen() {
showHashTarget(this.hash.substring(1));
}
.documentation a {
color: #1184CE;
}
.documentation .suppress-p-margin p {
margin:0;
.documentation {
font-family: Helvetica, sans-serif;
color: #666;
line-height: 1.5;
background: #f5f5f5;
}
.force-inline, .force-inline p {
display: inline;
color: #222;
.black {
color: #666;
}
.container-small {
max-width: 58rem;
margin-left: auto;
margin-right: auto;
.bg-white {
background-color: #fff;
}
.max-height-100 {
max-height: 100%;
h4 {
margin: 20px 0 10px 0;
}
.fade {
opacity:0.50;
.documentation h3 {
color: #000;
}
.button-indent {
padding: .25rem 1.5rem;
font-size: 90%;
.border-bottom {
border-color: #ddd;
}
.section-indent {
border-left: 2px solid #eee;
a {
color: #1184CE;
text-decoration: none;
}
.bg-cloudy {
background: #fafafa;
.documentation a[href]:hover {
text-decoration: underline;
}
.force-inline * {
display:inline;
a:hover {
cursor: pointer;
}
section:target h3 {
font-weight:700;
.py1-ul li {
padding: 5px 0;
}
.documentation,
.documentation h1,
.documentation h2,
.documentation h3,
.documentation h4,
.documentation h5,
.documentation h6 {
font-family: 'Source Sans Pro', Helvetica, sans-serif;
.max-height-100 {
max-height: 100%;
}
.documentation pre,
.documentation code,
.documentation samp {
font-family: 'Source Code Pro', monospace;
font-size: 90%;
section:target h3 {
font-weight:700;
}
.documentation td,
......@@ -75,12 +62,9 @@ h4:hover .anchorjs-link {
opacity: 1;
}
.collapsible .collapser {
display:none;
}
.collapsible:target .collapser {
display: block;
.fix-3 {
width: 25%;
max-width: 244px;
}
.fix-3 {
......@@ -93,3 +77,60 @@ h4:hover .anchorjs-link {
margin-left: 25%;
}
}
.pre, pre, code, .code {
font-family: Source Code Pro,Menlo,Consolas,Liberation Mono,monospace;
font-size: 14px;
}
.fill-light {
background: #F9F9F9;
}
.width2 {
width: 1rem;
}
.input {
font-family: inherit;
display: block;
width: 100%;
height: 2rem;
padding: .5rem;
margin-bottom: 1rem;
border: 1px solid #ccc;
font-size: .875rem;
border-radius: 3px;
box-sizing: border-box;
}
table {
border-collapse: collapse;
}
.prose table th,
.prose table td {
text-align: left;
padding:8px;
border:1px solid #ddd;
}
.prose table th:nth-child(1) { border-right: none; }
.prose table th:nth-child(2) { border-left: none; }
.prose table {
border:1px solid #ddd;
}
.prose-big {
font-size: 18px;
line-height: 30px;
}
.quiet {
opacity: 0.7;
}
.minishadow {
box-shadow: 2px 2px 10px #f3f3f3;
}
This diff could not be displayed because it is too large.
'use strict';
const documentation = require('gulp-documentation'),
eslint = require('gulp-eslint'),
gulp = require('gulp'),
istanbul = require('gulp-istanbul'),
jscs = require('gulp-jscs'),
mocha = require('gulp-mocha'),
pipe = require('gulp-pipe'),
sloc = require('gulp-sloc');
const SRC_FILES = ['lib/**/*.js'];
const TEST_FILES = [
'test/*_test.js',
'test/adapters/*_test.js'
];
const MOCHA_OPTIONS = {
ui: 'tdd',
bail: true,
reporter: 'dot',
timeout: 10000,
};
gulp.task('lint', () => {
pipe(gulp.src(SRC_FILES), [
eslint(),
eslint.format(),
eslint.failAfterError()
]);
pipe(gulp.src(SRC_FILES), [
jscs(),
jscs.reporter()
]);
});
gulp.task('lint-tests', ['lint'], () => {
pipe(gulp.src(['test/**/*.js']), [
eslint(),
eslint.format(),
eslint.failAfterError()
]);
pipe(gulp.src(['test/**/*.js']), [
jscs(),
jscs.reporter()
]);
});
gulp.task('sloc', () => gulp.src(SRC_FILES).pipe(sloc()));
gulp.task('test-sloc', () => gulp.src(TEST_FILES).pipe(sloc()));
gulp.task('docs', () => {
gulp.src(['lib/*.js'])
.pipe(documentation({format: 'html'}))
.pipe(gulp.dest('docs'));
gulp.src(['lib/*.js'])
.pipe(documentation({format: 'md'}))
.pipe(gulp.dest('.'));
});
gulp.task('mocha', ['lint-tests', 'sloc'], () => {
return gulp.src(TEST_FILES)
.pipe(mocha(MOCHA_OPTIONS))
.once('error', () => {
process.exit(1);
})
.once('end', () => {
process.exit();
});
});
gulp.task('test', ['test-sloc', 'lint-tests'], function(cb) {
return pipe(gulp.src(SRC_FILES), [
istanbul(),
istanbul.hookRequire()
]).on('finish', () => {
pipe(gulp.src(TEST_FILES), [
mocha(MOCHA_OPTIONS),
istanbul.writeReports({
dir: './coverage',
reporters: ['clover', 'lcov', 'lcovonly', 'html', 'text'],
}),
]);
});
});
gulp.task('default', ['lint', 'sloc', 'docs', 'test']);
\ No newline at end of file
'use strict';
/**
* Class that wraps database connection libraries
*
* @private
* @param {Object} instance - The connection object
* @param {Promise} instance - The connection object
*/
class Adapter {
/**
* Invoke an adapter
*
* @constructor
* @param {Object} instance - The connection object
* @param {Promise} instance - Promise holding connection object
*/
constructor(instance) {
constructor (instance) {
this.instance = instance;
}
......@@ -23,10 +22,9 @@ class Adapter {
*
* @param {String} sql - The sql with placeholders
* @param {Array} params - The values to insert into the query
* @param {Function} [callback] - Callback to run when a response is recieved
* @return {void|Promise} - returns a promise if no callback is passed
* @return {Promise} - returns a promise if no callback is passed
*/
execute(/*sql, params, callback*/) {
execute (sql, params) {
throw new Error('Correct adapter not defined for query execution');
}
......@@ -36,7 +34,7 @@ class Adapter {
* @param {*} originalResult - the original result object from the driver
* @return {Result} - the new result object
*/
transformResult(originalResult) {
transformResult (originalResult) {
throw new Error('Result transformer method not defined for current adapter');
}
......@@ -44,9 +42,9 @@ class Adapter {
* Close the current database connection
* @return {void}
*/
close() {
this.instance.end();
close () {
this.instance.then(conn => conn.end());
}
}
module.exports = Adapter;
\ No newline at end of file
module.exports = Adapter;
......@@ -7,7 +7,7 @@ const helpers = require('./helpers');
*
* @private
*/
let Driver = {
const Driver = {
identifierStartChar: '"',
identifierEndChar: '"',
tablePrefix: null,
......@@ -20,9 +20,9 @@ let Driver = {
* @return {String} - The quoted sql fragment
* @private
*/
_quote(str) {
return (helpers.isString(str)
&& ! (str.startsWith(Driver.identifierStartChar) || str.endsWith(Driver.identifierEndChar))
_quote (str) {
return (helpers.isString(str) &&
!(str.startsWith(Driver.identifierStartChar) || str.endsWith(Driver.identifierEndChar))
)
? `${Driver.identifierStartChar}${str}${Driver.identifierEndChar}`
: str;
......@@ -30,17 +30,16 @@ let Driver = {
/**
* Set the limit clause
* @private
* @param {String} sql - SQL statement to modify
* @param {Number} limit - Maximum number of rows to fetch
* @param {Number} [offset] - Number of rows to skip
* @return {String} - Modified SQL statement
*/
limit(sql, limit, offset) {
sql += ` LIMIT ${limit}`;
limit (sql, limit, offset) {
sql += ` LIMIT ${limit}`;
if (helpers.isNumber(offset))
{
if (helpers.isNumber(offset)) {
sql += ` OFFSET ${offset}`;
}
......@@ -50,10 +49,11 @@ let Driver = {
/**
* Quote database table name, and set prefix
*
* @private
* @param {String} table - Table name to quote
* @return {String} - Quoted table name
*/
quoteTable(table) {
quoteTable (table) {
// Quote after prefix
return Driver.quoteIdentifiers(table);
},
......@@ -61,25 +61,24 @@ let Driver = {
/**
* Use the driver's escape character to quote identifiers
*
* @private
* @param {String|Array} str - String or array of strings to quote identifiers
* @return {String|Array} - Quoted identifier(s)
*/
quoteIdentifiers(str) {
quoteIdentifiers (str) {
let hiers, raw;
let pattern = new RegExp(
`${Driver.identifierStartChar}(`
+ '([a-zA-Z0-9_]+)' + '(\((.*?)\))'
+ `)${Driver.identifierEndChar}`, 'ig');
`${Driver.identifierStartChar}(` +
'([a-zA-Z0-9_]+)' + '(((.*?)))' +
`)${Driver.identifierEndChar}`, 'ig');
// Recurse for arrays of identifiiers
if (Array.isArray(str))
{
if (Array.isArray(str)) {
return str.map(Driver.quoteIdentifiers);
}
// Handle commas
if (str.includes(','))
{
if (str.includes(',')) {
let parts = str.split(',').map(helpers.stringTrim);
str = parts.map(Driver.quoteIdentifiers).join(',');
}
......@@ -89,8 +88,7 @@ let Driver = {
raw = hiers.join('.');
// Fix functions
if (raw.includes('(') && raw.includes(')'))
{
if (raw.includes('(') && raw.includes(')')) {
let funcs = pattern.exec(raw);
// Unquote the function
......@@ -107,10 +105,11 @@ let Driver = {
/**
* Generate SQL to truncate the passed table
*
* @private
* @param {String} table - Table to truncate
* @return {String} - Truncation SQL
*/
truncate(table) {
truncate (table) {
let sql = (Driver.hasTruncate)
? 'TRUNCATE '
: 'DELETE FROM ';
......@@ -123,17 +122,15 @@ let Driver = {
/**
* Generate SQL to insert a group of rows
*
* @private
* @param {String} table - The table to insert to
* @param {Array} [data] - The array of object containing data to insert
* @return {String} - Query and data to insert
*/
insertBatch(table, data) {
let vals = [],
fields = Object.keys(data[0]),
sql = '',
params = [],
paramString = '',
paramList = [];
insertBatch (table, data) {
const vals = [];
const fields = Object.keys(data[0]);
let sql = '';
// Get the data values to insert, so they can
// be parameterized
......@@ -150,17 +147,17 @@ let Driver = {
sql += `INSERT INTO ${table} (${Driver.quoteIdentifiers(fields).join(',')}) VALUES `;
// Create placeholder groups
params = Array(fields.length).fill('?');
paramString = `(${params.join(',')})`;
paramList = Array(data.length).fill(paramString);
let params = Array(fields.length).fill('?');
let paramString = `(${params.join(',')})`;
let paramList = Array(data.length).fill(paramString);
sql += paramList.join(',');
return {
sql: sql,
values: vals,
values: vals
};
},
}
};
module.exports = Driver;
\ No newline at end of file
module.exports = Driver;
'use strict';
const helpers = require('./helpers');
const QueryBuilder = require('./QueryBuilder');