Skip to content

Commit

Permalink
Highlight mysql8 bug (#382)
Browse files Browse the repository at this point in the history
* test(mysql8): highlight mysql 8 bug

* test(postgres): fix breaking test for postgres, noissue

* fix(mysql8): workaround bug in subquery limit, noissue
  • Loading branch information
MrSwitch authored Sep 23, 2024
1 parent e44b387 commit 0f3a7cb
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 0 deletions.
12 changes: 12 additions & 0 deletions src/get.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,18 @@ export default function buildQuery(opts, dareInstance) {
throw new DareError(DareError.INVALID_REQUEST, 'Missing fields');
}

/*
* Workaround for MySQL 8.0 bug https://bugs.mysql.com/bug.php?id=109585
* -> When, all fields are aggregates
* -> And, this is a subquery
* -> Then, remove the limit
*/
if (dareInstance.engine?.startsWith('mysql:8') && alias) {
if (fields.every(item => item.agg)) {
opts.limit = null;
};
}

// Put it all together
let sql = SQL`SELECT ${join(sql_fields)}
FROM ${raw(sql_table)} ${raw(sql_alias)}
Expand Down
115 changes: 115 additions & 0 deletions test/integration/disparities.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import Dare from '../../src/index.js';
import Debug from 'debug';
import assert from 'node:assert/strict';
import mysql from 'mysql2/promise';
import db from './helpers/db.js';
import {options} from './helpers/api.js';
import SQL from 'sql-template-tag';
const debug = Debug('sql');

// Connect to db

describe(`Disparities`, () => {
let dare;

beforeEach(() => {
// Initiate
dare = new Dare(options);

// Set a test instance
// eslint-disable-next-line arrow-body-style
dare.execute = query => {
// DEBUG
debug(mysql.format(query.sql, query.values));

return db.query(query);
};
});

it('MySQL 8 fails to correctly count the items in this scenario', async () => {
/*
* See Bug report: https://bugs.mysql.com/bug.php?id=109585
*/
await dare.sql(SQL`
CREATE TABLE members (
id int NOT NULL,
name VARCHAR(30) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE content (
id int NOT NULL,
name VARCHAR(30) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE domains (
id smallint NOT NULL,
name VARCHAR(100) NULL,
PRIMARY KEY (id)
);
CREATE TABLE userContent (
id int NOT NULL,
content_id int NOT NULL,
user_id int NOT NULL,
domain_id smallint NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'notstarted',
PRIMARY KEY (id),
CONSTRAINT content_user_domain_id UNIQUE (content_id,user_id,domain_id),
CONSTRAINT fk_userContent_domain_id FOREIGN KEY (domain_id) REFERENCES domains (id),
CONSTRAINT userContent_content FOREIGN KEY (content_id) REFERENCES content (id) ON DELETE CASCADE,
CONSTRAINT userContent_user FOREIGN KEY (user_id) REFERENCES members (id) ON DELETE CASCADE
);
-- ---------------------------------
-- INSERT
-- ---------------------------------
INSERT INTO domains (id, name) VALUES (1, 'Test');
INSERT INTO content (id, name) VALUES (1, 'A'), (2, 'B'), (3, 'C');
INSERT INTO members (id,name)
VALUES
(11, '[email protected]'),
(12, '[email protected]');
INSERT INTO userContent (id, domain_id,content_id,user_id,status)
VALUES
(1, 1, 3, 11, 'completed'),
(2, 1, 3, 12, 'completed'),
(3, 1, 2, 12, 'completed');
`);

const status = 'completed';
const domain_id = 1;

dare.options.models.userContent = {
schema: {
content_id: ['content.id'],
},
};

// Construct a query which counts these
const resp = await dare.get({
table: 'content',
fields: ['id', {count: 'COUNT(DISTINCT userContent.user_id)'}],
join: {
userContent: {
status,
domain_id,
},
},
limit: 3,
});

assert.deepStrictEqual(resp, [
{id: 1, count: 0},
{id: 2, count: 1},
{id: 3, count: 2},
]);
});
});
7 changes: 7 additions & 0 deletions test/integration/helpers/Postgres.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ import pg from 'pg';
import fs from 'node:fs';
import QueryStream from 'pg-query-stream';

/*
* Aggregate functions are returned as BigInts which by default are converted to strings
* This changes floats and ints to their respective types
*/
pg.types.setTypeParser(1700, parseFloat)
pg.types.setTypeParser(20, parseInt);

const {TEST_DB_DATA_PATH, TEST_DB_SCHEMA_PATH} = process.env;

const schemaSql = fs.readFileSync(TEST_DB_SCHEMA_PATH);
Expand Down
31 changes: 31 additions & 0 deletions test/specs/get-subquery.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -376,4 +376,35 @@ describe('get - subquery', () => {
});
});
});



describe(`Disparities`, () => {

it('MySQL 8 fails to correctly count the items in this scenario', async () => {
/*
* See Bug report: https://bugs.mysql.com/bug.php?id=109585
*/
const dareInst = dare.use({engine: 'mysql:8.0.36'});

dareInst.options.models.userContent = {
schema: {
content_id: ['content.id'],
},
};

dareInst.sql = async ({sql}) => {
expect(sql).to.not.contain('LIMIT 1');
};

// Construct a query which counts these
await dareInst.get({
table: 'content',
fields: ['id', {count: 'COUNT(DISTINCT userContent.user_id)'}],
limit: 3,
});

});
});

});

0 comments on commit 0f3a7cb

Please sign in to comment.