UNPKG

sharp-db

Version:

Classes for running SQL and building select queries for MySQL in Node

239 lines (237 loc) 8.14 kB
const Select = require('../Select/Select.js'); describe('Parser', () => { describe('column handler', () => { it('should recognize asterisk', () => { const query = Select.parse('SELECT * FROM mytable'); expect(query._columns).toEqual(['*']); }); }); describe('comment handler', () => { it('should strip single-line comments -- dashes', () => { const query = Select.parse(` SELECT -- stuff a, -- more stuff b -- and even more stuff FROM mytable `); expect(query.normalized()).toBe('SELECT a, b FROM mytable'); }); it('should strip single-line comments #hash', () => { const query = Select.parse(` SELECT #stuff a, # more stuff b #and even more stuff FROM mytable `); expect(query.normalized()).toBe('SELECT a, b FROM mytable'); }); it('should strip multi-line comments', () => { const query = Select.parse(` SELECT /* stuff */ a, /* more stuff */ b FROM mytable `); expect(query.normalized()).toBe('SELECT a, b FROM mytable'); }); }); describe('subquery handler', () => { it('should handle column subqueries', () => { const normalized = 'SELECT a, (SELECT * FROM tbl2) AS b FROM mytable WHERE mycol = 1'; const query = Select.parse(normalized); expect(query.normalized()).toBe(normalized); }); it('should handle column subqueries with SELECT EXISTS', () => { const normalized = 'SELECT a, (SELECT EXISTS (SELECT * FROM tbl2)) AS b FROM mytable WHERE mycol = 1'; const query = Select.parse(normalized); expect(query.normalized()).toBe(normalized); }); it('should handle IF() column expressions', () => { const normalized = 'SELECT a, IF(b > 0) AS is_gt_0, IF(b > 100) AS is_gt_100 FROM mytable WHERE mycol = 1'; const query = Select.parse(normalized); expect(query.normalized()).toBe(normalized); }); it('should handle IN() expressions in WHERE', () => { const normalized = 'SELECT * FROM mytable WHERE mycol IN (SELECT id FROM othertable)'; const query = Select.parse(normalized); expect(query.normalized()).toBe(normalized); }); it('should handle IN() expressions in JOINs', () => { const normalized = 'SELECT * FROM a INNER JOIN b ON b.id = a.b_id AND b.status IN (SELECT id FROM statuses) WHERE a.id > 10'; const query = Select.parse(normalized); expect(query.normalized()).toBe(normalized); }); }); describe('table handler', () => { it('should parse single table', () => { const query = Select.parse('SELECT * FROM a'); expect(query._tables).toEqual(['a']); }); it('should parse comma-separated tables', () => { const query = Select.parse('SELECT * FROM a, b'); expect(query._tables).toEqual(['a', 'b']); }); }); describe('column handler', () => { it('should parse single column', () => { const query = Select.parse('SELECT a FROM b'); expect(query._columns).toEqual(['a']); }); it('should parse comma-separated columns', () => { const query = Select.parse('SELECT a, b FROM c'); expect(query._columns).toEqual(['a', 'b']); }); it('should handle expressions with one comma', () => { const query = Select.parse("SELECT a, CONCAT('b',b) FROM c"); expect(query._columns).toEqual(['a', "CONCAT('b',b)"]); }); it('should handle expressions with 2 commas', () => { const query = Select.parse( "SELECT a, CONCAT(fname, ' ', lname) FROM users" ); expect(query._columns).toEqual(['a', "CONCAT(fname, ' ', lname)"]); }); }); describe('option handler', () => { it('should save SQL_CALC_FOUND_ROWS as an option', () => { const query = Select.parse('SELECT SQL_CALC_FOUND_ROWS * FROM a'); expect(query._columns).toEqual(['*']); expect(query._options).toEqual(['SQL_CALC_FOUND_ROWS']); }); }); describe('JOIN handler', () => { const joins = [ 'JOIN', 'INNER JOIN', 'LEFT JOIN', 'LEFT OUTER JOIN', 'OUTER JOIN', 'RIGHT JOIN', 'RIGHT OUTER JOIN', 'CROSS JOIN', 'FULL JOIN', 'FULL OUTER JOIN', ]; joins.forEach(join => { it(`should handle ${join}`, () => { const sql = `SELECT * FROM a ${join} b ON b.id = a.b_id`; const query = Select.parse(sql); expect(query.normalized()).toBe(sql); }); }); }); describe('conditions handler', () => { it('should parse WHERE 1', () => { const query = Select.parse('SELECT * FROM mytable WHERE 1'); expect(query._wheres).toEqual(['1']); }); it("should parse WHERE '1'", () => { const query = Select.parse("SELECT * FROM mytable WHERE '1'"); expect(query._wheres).toEqual(["'1'"]); }); it('should parse WHERE true', () => { const query = Select.parse('SELECT * FROM mytable WHERE true'); expect(query._wheres).toEqual(['true']); }); it('should parse single WHERE', () => { const query = Select.parse('SELECT * FROM mytable WHERE mycol = 1'); expect(query._wheres).toEqual(['mycol = 1']); }); it('should parse two WHERE clauses joined by AND', () => { const query = Select.parse( 'SELECT * FROM mytable WHERE mycol = 1 AND scheduled < NOW()' ); expect(query._wheres).toEqual(['mycol = 1', 'scheduled < NOW()']); }); it('should parse two WHERE clauses joined by OR', () => { const query = Select.parse( 'SELECT * FROM mytable WHERE mycol = 1 OR scheduled < NOW()' ); expect(query._wheres).toEqual(['(mycol = 1 OR scheduled < NOW())']); }); it('should parse OR then AND', () => { const query = Select.parse( 'SELECT * FROM mytable WHERE a = 1 OR b = 2 AND c = 3' ); expect(query._wheres).toEqual(['(a = 1 OR b = 2)', 'c = 3']); }); it('should parse OR then AND with parens', () => { const query = Select.parse( 'SELECT * FROM mytable WHERE (a = 1 OR b = 2) AND c = 3' ); expect(query._wheres).toEqual(['(a = 1 OR b = 2)', 'c = 3']); }); it('should parse AND then OR', () => { const query = Select.parse( 'SELECT * FROM mytable WHERE a = 1 AND b = 2 OR c = 3' ); expect(query._wheres).toEqual(['a = 1', '(b = 2 OR c = 3)']); }); }); it('should parse GROUP BY and HAVING', () => { const query = Select.parse( 'SELECT name, COUNT(*) FROM mytable GROUP BY name HAVING COUNT(*) > 1' ); expect(query._groupBys).toEqual(['name']); expect(query._havings).toEqual(['COUNT(*) > 1']); }); it('should parse ORDER BY', () => { const query = Select.parse('SELECT * FROM users ORDER BY id DESC'); expect(query._orderBys).toEqual(['id DESC']); }); describe('pagination', () => { it('should parse LIMIT with number', () => { const query = Select.parse('SELECT * FROM mytable LIMIT 1'); expect(query._limit).toBe(1); }); it('should parse LIMIT with placeholder', () => { const query = Select.parse('SELECT * FROM mytable LIMIT ?'); expect(query._limit).toBe('?'); }); it('should parse LIMIT with named placeholder', () => { const query = Select.parse('SELECT * FROM mytable LIMIT :limit'); expect(query._limit).toBe(':limit'); }); it('should parse OFFSET with number', () => { const query = Select.parse('SELECT * FROM mytable LIMIT 5 OFFSET 10'); expect(query._offset).toBe(10); }); it('should parse OFFSET with placeholder', () => { const query = Select.parse('SELECT * FROM mytable LIMIT 5 OFFSET ?'); expect(query._offset).toBe('?'); }); it('should parse OFFSET with named placeholder', () => { const query = Select.parse( 'SELECT * FROM mytable LIMIT 5 OFFSET :offset' ); expect(query._offset).toBe(':offset'); }); it('should parse OFFSET, LIMIT with numbers', () => { const query = Select.parse('SELECT * FROM mytable LIMIT 10, 5'); expect(query._limit).toBe(5); expect(query._offset).toBe(10); }); it('should parse OFFSET, LIMIT with placeholders', () => { const query = Select.parse('SELECT * FROM mytable LIMIT ?, ?'); expect(query._limit).toBe('?'); expect(query._offset).toBe('?'); }); it('should parse OFFSET, LIMIT with named placeholders', () => { const query = Select.parse('SELECT * FROM mytable LIMIT :offset, :limit'); expect(query._limit).toBe(':limit'); expect(query._offset).toBe(':offset'); }); }); });