Skip to content
This repository was archived by the owner on May 25, 2025. It is now read-only.

Commit cf84910

Browse files
fix: make most of db-lib tests pass, fix some bugs
1 parent eccf3e2 commit cf84910

File tree

8 files changed

+980
-950
lines changed

8 files changed

+980
-950
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
},
1313
"devDependencies": {
1414
"@naturalcycles/dev-lib": "^12.7.5",
15-
"@types/node": "^16.10.2",
15+
"@types/node": "^17.0.5",
1616
"dotenv": "^10.0.0",
1717
"jest": "^27.2.4"
1818
},

readme.md

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,33 @@
33
> MySQL client implementing CommonDB interface
44
55
[![npm](https://img.shields.io/npm/v/@naturalcycles/mysql-lib/latest.svg)](https://www.npmjs.com/package/@naturalcycles/mysql-lib)
6-
[![](https://circleci.com/gh/NaturalCycles/mysql-lib.svg?style=shield&circle-token=123)](https://circleci.com/gh/NaturalCycles/mysql-lib)
76
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
87

9-
# Packaging
8+
# Quick start
109

11-
- `engines.node >= 10.13`: Latest Node.js LTS
12-
- `main: dist/index.js`: commonjs, es2018
13-
- `types: dist/index.d.ts`: typescript types
14-
- `/src` folder with source `*.ts` files included
10+
todo
11+
12+
# Develop
13+
14+
Install MySQL server+client on Mac:
15+
16+
```shell
17+
18+
brew install mysql
19+
20+
# start
21+
22+
mysql.server start
23+
24+
# secure
25+
26+
mysql_secure_installation
27+
28+
# connect
29+
30+
mysql -uroot
31+
32+
# Default options are read from the following files in the given order:
33+
# /etc/my.cnf /etc/mysql/my.cnf /opt/homebrew/etc/my.cnf ~/.my.cnf
34+
35+
```

src/mysql.db.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
RunQueryResult,
1111
} from '@naturalcycles/db-lib'
1212
import {
13-
_filterNullishValues,
13+
_filterUndefinedValues,
1414
_mapKeys,
1515
_mapValues,
1616
_Memo,
@@ -136,7 +136,7 @@ export class MysqlDB extends BaseCommonDB implements CommonDB {
136136
async close(): Promise<void> {
137137
const pool = this.pool()
138138
// eslint-disable-next-line @typescript-eslint/await-thenable
139-
await promisify(pool.end.bind(pool))
139+
await promisify(pool.end.bind(pool))()
140140
this.cfg.logger.log('closed')
141141
}
142142

@@ -191,14 +191,24 @@ export class MysqlDB extends BaseCommonDB implements CommonDB {
191191
_opt: MysqlDBOptions = {},
192192
): Promise<RunQueryResult<OUT>> {
193193
const sql = dbQueryToSQLSelect(q)
194-
const rows = await this.runSQL<ROW[]>({ sql })
194+
const rows = (await this.runSQL<ROW[]>({ sql })).map(
195+
row => _mapKeys(_filterUndefinedValues(row, true), k => mapNameFromMySQL(k)) as any,
196+
)
197+
198+
// edge case where 0 fields are selected
199+
if (q._selectedFieldNames?.length === 0) {
200+
return {
201+
rows: rows.map(_ => ({} as any)),
202+
}
203+
}
204+
195205
return {
196-
rows: rows.map(r => _mapKeys(_filterNullishValues(r, true), k => mapNameFromMySQL(k)) as any),
206+
rows,
197207
}
198208
}
199209

200210
async runSQL<RESULT>(q: QueryOptions): Promise<RESULT> {
201-
if (this.cfg.logSQL) this.cfg.logger.log(q.sql, q.values)
211+
if (this.cfg.logSQL) this.cfg.logger.log(...[q.sql, q.values].filter(Boolean))
202212

203213
return await new Promise<RESULT>((resolve, reject) => {
204214
this.pool().query(q, (err, res) => {
@@ -232,7 +242,7 @@ export class MysqlDB extends BaseCommonDB implements CommonDB {
232242
new Transform({
233243
objectMode: true,
234244
transform(row, _encoding, cb) {
235-
cb(null, _filterNullishValues(row, true))
245+
cb(null, _filterUndefinedValues(row, true))
236246
},
237247
}),
238248
)
@@ -248,13 +258,13 @@ export class MysqlDB extends BaseCommonDB implements CommonDB {
248258

249259
// Stringify object values
250260
const rows = _rows.map(row =>
251-
_mapValues(row, v => {
261+
_mapValues(row, (_k, v) => {
252262
return v && typeof v === 'object' && !Buffer.isBuffer(v) ? JSON.stringify(v) : v
253263
}),
254264
)
255265

256266
// inserts are split into multiple sentenses to respect the max_packet_size (1Mb usually)
257-
const sqls = insertSQL(table, rows, 'INSERT', this.cfg.logger)
267+
const sqls = insertSQL(table, rows, 'REPLACE', this.cfg.logger)
258268

259269
for await (const sql of sqls) {
260270
await this.runSQL({ sql })

src/query.util.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,16 @@ export function dbQueryToSQLDelete(q: DBQuery<any>): string {
4141
*/
4242
export function insertSQL(
4343
table: string,
44-
records: Record<any, any>[],
44+
rows: Record<any, any>[],
4545
verb: 'INSERT' | 'REPLACE' = 'INSERT',
4646
logger: CommonLogger = console,
4747
): string[] {
4848
// INSERT INTO table_name (column1, column2, column3, ...)
4949
// VALUES (value1, value2, value3, ...);
5050

5151
// eslint-disable-next-line unicorn/no-array-reduce
52-
const fieldSet = records.reduce((set: Set<string>, rec) => {
53-
Object.keys(rec).forEach(field => set.add(field))
52+
const fieldSet = rows.reduce((set: Set<string>, row) => {
53+
Object.keys(row).forEach(field => set.add(field))
5454
return set
5555
}, new Set<string>())
5656
const fields = [...fieldSet]
@@ -63,7 +63,7 @@ export function insertSQL(
6363
`VALUES\n`,
6464
].join(' ')
6565

66-
const valueRows = records.map(rec => {
66+
const valueRows = rows.map(rec => {
6767
return `(` + fields.map(k => mysql.escape(rec[k])).join(',') + `)`
6868
})
6969

src/schema/mysql.schema.util.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ export function mysqlTableStatsToJsonSchemaField<T = any>(
106106
s.properties[name] = { instanceof: 'Buffer' }
107107
} else if (t.startsWith('tinyint') || t.includes('(1)')) {
108108
s.properties[name] = { type: 'boolean' } as JsonSchemaBoolean
109-
} else if (t.startsWith('int(')) {
109+
} else if (t === 'int' || t.startsWith('int(')) {
110110
s.properties[name] = { type: 'integer' } as JsonSchemaNumber
111111
} else if (t.startsWith('float')) {
112112
s.properties[name] = { type: 'number' } as JsonSchemaNumber

src/test/mysql.manual.test.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { DBQuery } from '@naturalcycles/db-lib'
22
import {
3+
createTestItemDBM,
34
createTestItemsDBM,
45
getTestItemSchema,
56
runCommonDaoTest,
67
runCommonDBTest,
78
TEST_TABLE,
9+
TestItemDBM,
810
} from '@naturalcycles/db-lib/dist/testing'
911
import { deflateString, inflateToString, requireEnvKeys } from '@naturalcycles/nodejs-lib'
1012
import { MysqlDB } from '../mysql.db'
@@ -51,6 +53,24 @@ test('getTableSchema', async () => {
5153
expect(getTestItemSchema()).toMatchObject(schema)
5254
})
5355

56+
test('saveBatch overwrite', async () => {
57+
const items = createTestItemsDBM(1)
58+
// should not throw
59+
await db.saveBatch(TEST_TABLE, items)
60+
await db.saveBatch(TEST_TABLE, items)
61+
})
62+
63+
test('null values', async () => {
64+
const item3 = {
65+
...createTestItemDBM(3),
66+
k2: null,
67+
}
68+
await db.saveBatch(TEST_TABLE, [item3])
69+
const item3Loaded = (await db.getByIds<TestItemDBM>(TEST_TABLE, [item3.id]))[0]!
70+
console.log(item3Loaded)
71+
// expect(item3Loaded.k2).toBe(null)
72+
})
73+
5474
test('emojis', async () => {
5575
const items = createTestItemsDBM(5).map(r => ({ ...r, k1: `😣` }))
5676
// should not throw
@@ -97,7 +117,7 @@ test('stringify objects', async () => {
97117
await db.createTable(TEST_TABLE, getTestItemSchema(), { dropIfExists: true })
98118
await db.saveBatch(TEST_TABLE, [item])
99119
const { rows } = await db.runQuery(new DBQuery(TEST_TABLE))
100-
// console.log(records)
120+
// console.log(rows)
101121
expect(rows).toEqual([
102122
{
103123
...item,

src/test/setupJest.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { jestLog, jestLogger } from '@naturalcycles/dev-lib/dist/testing'
2+
3+
// Patch console functions so jest doesn't log it so verbose
4+
console.log = console.warn = jestLog
5+
console.error = jestLogger.error.bind(jestLogger)

0 commit comments

Comments
 (0)