Skip to content

Commit a01e6fb

Browse files
committed
MySQLTable: Implement a .exists() method
1 parent 6bfa2af commit a01e6fb

File tree

3 files changed

+190
-1
lines changed

3 files changed

+190
-1
lines changed

README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,7 @@ an instance, use [`poolPlus.defineTable()`](#PoolPlus+defineTable) or
540540
* [.pool](#MySQLTable+pool) : <code>[PoolPlus](#PoolPlus)</code>
541541
* [.trxn](#MySQLTable+trxn) : <code>?[Connection](#Connection)</code>
542542
* [.select(columns, [sqlString], [values], [cb])](#MySQLTable+select) ⇒ <code>Promise</code>
543+
* [.exists(sqlString, [values], [cb])](#MySQLTable+exists) ⇒ <code>Promise</code>
543544
* [.insert([data], [sqlString], [values], [cb])](#MySQLTable+insert) ⇒ <code>Promise</code>
544545
* [.insertIfNotExists(data, keyColumns, [cb])](#MySQLTable+insertIfNotExists) ⇒ <code>Promise</code>
545546
* [.update([data], [sqlString], [values], [cb])](#MySQLTable+update) ⇒ <code>Promise</code>
@@ -650,6 +651,44 @@ userTable.select('COUNT(*) AS `highScorers`', 'WHERE `points` > 10000')
650651
```
651652

652653

654+
---
655+
656+
<a name="MySQLTable+exists"></a>
657+
658+
### mySQLTable.exists(sqlString, [values], [cb]) ⇒ <code>Promise</code>
659+
Checks if rows in the table exist.
660+
661+
662+
| Param | Type | Description |
663+
|:--- |:--- |:--- |
664+
| sqlString | <code>string</code> | SQL that specifies rows to check for existence.<br> The first example shows how this parameter is used in the query. |
665+
| [values] | <code>Array</code> | Values to replace the placeholders in `sqlString`. |
666+
| [cb] | <code>[queryCallback](#module_mysql-plus..queryCallback)</code> | A callback that gets called with the results of the query where the `results` will be either `true` or `false`. |
667+
668+
**Returns**: <code>?Promise</code> - If the `cb` parameter is omitted, a promise that will
669+
resolve with either `true` or `false` is returned.
670+
671+
**Example**: Using a promise
672+
```js
673+
userTable.exists('WHERE `id` > 10')
674+
.then(exists => console.log(exists)); // true or false
675+
676+
// SELECT EXISTS (
677+
// SELECT 1 FROM `user`
678+
// WHERE `id` > 10 # This is where `sqlString` gets inserted
679+
// LIMIT 1
680+
// )
681+
```
682+
683+
**Example**: Using a callback and the `values` argument
684+
```js
685+
userTable.exists('WHERE `id` = ?', [10], (err, exists) => {
686+
if (err) throw err;
687+
console.log(exists); // true or false
688+
});
689+
```
690+
691+
653692
---
654693

655694
<a name="MySQLTable+insert"></a>

lib/MySQLTable.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,57 @@ class MySQLTable {
101101
);
102102
}
103103

104+
/**
105+
* Checks if rows in the table exist.
106+
*
107+
* @param {string} sqlString - SQL that specifies rows to check for existence.<br>
108+
* The first example shows how this parameter is used in the query.
109+
* @param {Array} [values] - Values to replace the placeholders in `sqlString`.
110+
* @param {module:mysql-plus~queryCallback} [cb] - A callback that gets called with
111+
* the results of the query where the `results` will be either `true` or `false`.
112+
* @returns {?Promise} If the `cb` parameter is omitted, a promise that will
113+
* resolve with either `true` or `false` is returned.
114+
*
115+
* @example <caption>Using a promise</caption>
116+
* userTable.exists('WHERE `id` > 10')
117+
* .then(exists => console.log(exists)); // true or false
118+
*
119+
* // SELECT EXISTS (
120+
* // SELECT 1 FROM `user`
121+
* // WHERE `id` > 10 # This is where `sqlString` gets inserted
122+
* // LIMIT 1
123+
* // )
124+
*
125+
* @example <caption>Using a callback and the `values` argument</caption>
126+
* userTable.exists('WHERE `id` = ?', [10], (err, exists) => {
127+
* if (err) throw err;
128+
* console.log(exists); // true or false
129+
* });
130+
*/
131+
exists(sqlString, values, cb) {
132+
if (typeof values === 'function') {
133+
cb = values;
134+
values = undefined;
135+
}
136+
137+
sqlString = 'SELECT EXISTS ( SELECT 1 FROM ' + this._escapedName + ' ' + sqlString + ' LIMIT 1 ) as `exists`';
138+
139+
if (!cb) {
140+
return this._db.pquery(sqlString, values).then(checkExists);
141+
}
142+
143+
this._db.query(sqlString, values, (err, rows) => {
144+
if (err) {
145+
cb(err);
146+
return;
147+
}
148+
149+
cb(null, checkExists(rows));
150+
});
151+
152+
return undefined;
153+
}
154+
104155
/**
105156
* Inserts data into a new row in the table.
106157
*
@@ -417,4 +468,10 @@ class MySQLTable {
417468
}
418469
}
419470

471+
function checkExists(rows) {
472+
// Must convert the result to a number because mysql will return a string if the
473+
// user set the "supportBigNumbers" and "bigNumberStrings" options to `true`.
474+
return +rows[0].exists === 1;
475+
}
476+
420477
module.exports = MySQLTable;

test/unit/MySQLTable.test.js

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ const config = require('../config');
88
const should = require('should');
99
const sinon = require('sinon');
1010

11+
const expect = should;
12+
13+
should.Assertion.addChain('to');
1114
should.config.checkProtoEql = false;
1215

1316
describe('MySQLTable', () => {
@@ -70,7 +73,7 @@ describe('MySQLTable', () => {
7073

7174

7275
it('should be undefined if not passed to the constructor', () => {
73-
should(testTable.trxn).be.undefined();
76+
expect(testTable.trxn).to.be.undefined();
7477
});
7578

7679

@@ -763,6 +766,96 @@ describe('MySQLTable', () => {
763766
});
764767

765768

769+
describe('#exists()', () => {
770+
771+
before(done => {
772+
const insertSQL = 'INSERT INTO `mysql_table_test_table` (`email`) VALUES ("one@email.com")';
773+
testTable.query(insertSQL, done);
774+
});
775+
776+
after(resetTable);
777+
778+
describe('with a callback', () => {
779+
780+
it('should resolve with `true` if rows exist (without using the `values` argument)', done => {
781+
testTable.exists('WHERE `id` < 3', (err, exists) => {
782+
if (err) throw err;
783+
exists.should.be.true();
784+
done();
785+
});
786+
});
787+
788+
it('should resolve with `true` if rows exist (with using the `values` argument)', done => {
789+
testTable.exists('WHERE `email` = ?', ['one@email.com'], (err, exists) => {
790+
if (err) throw err;
791+
exists.should.be.true();
792+
done();
793+
});
794+
});
795+
796+
it('should resolve with `false` if rows exist (without using the `values` argument)', done => {
797+
testTable.exists('WHERE `id` > 3', (err, exists) => {
798+
if (err) throw err;
799+
exists.should.be.false();
800+
done();
801+
});
802+
});
803+
804+
it('should resolve with `false` if rows exist (with using the `values` argument)', done => {
805+
testTable.exists('WHERE `email` = ?', ['two@email.com'], (err, exists) => {
806+
if (err) throw err;
807+
exists.should.be.false();
808+
done();
809+
});
810+
});
811+
812+
it('should resolve with an error if an error occurrs', done => {
813+
const error = new Error('exists error');
814+
sinon.stub(pool, 'query').yieldsAsync(error);
815+
816+
testTable.exists('WHERE `email` = 1', (err, exists) => {
817+
err.should.equal(error);
818+
expect(exists).to.be.undefined();
819+
820+
pool.query.restore();
821+
done();
822+
});
823+
});
824+
825+
});
826+
827+
828+
describe('with a promise', () => {
829+
830+
it('should resolve with `true` if rows exist (without using the `values` argument)', () => {
831+
return testTable.exists('WHERE `id` < 3').then(exists => {
832+
exists.should.be.true();
833+
});
834+
});
835+
836+
it('should resolve with `true` if rows exist (with using the `values` argument)', () => {
837+
return testTable.exists('WHERE `email` = ?', ['one@email.com']).then(exists => {
838+
exists.should.be.true();
839+
});
840+
});
841+
842+
it('should resolve with `false` if rows exist (without using the `values` argument)', () => {
843+
return testTable.exists('WHERE `id` > 3').then(exists => {
844+
exists.should.be.false();
845+
});
846+
});
847+
848+
it('should resolve with `false` if rows exist (with using the `values` argument)', () => {
849+
return testTable.exists('WHERE `email` = ?', ['two@email.com']).then(exists => {
850+
exists.should.be.false();
851+
});
852+
});
853+
854+
});
855+
856+
});
857+
858+
766859
describe('#query()', () => {
767860

768861
beforeEach(() => {

0 commit comments

Comments
 (0)