@superhero/db 0.5.2 → 1.0.1-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/adapter/mysql/index.js +7 -5
- package/adapter/mysql/transaction.js +3 -10
- package/adapter/mysql2/index.js +48 -5
- package/adapter/mysql2/transaction.js +3 -10
- package/index.js +54 -28
- package/package.json +5 -2
package/adapter/mysql/index.js
CHANGED
|
@@ -20,6 +20,11 @@ class AdapterMySql
|
|
|
20
20
|
return new Promise(resolve)
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
getFormattedQuery(query, ...ctx)
|
|
24
|
+
{
|
|
25
|
+
return this.pool.format(query, ...ctx)
|
|
26
|
+
}
|
|
27
|
+
|
|
23
28
|
getConnection()
|
|
24
29
|
{
|
|
25
30
|
const resolve = (accept, reject, i = 0) =>
|
|
@@ -33,13 +38,10 @@ class AdapterMySql
|
|
|
33
38
|
return new Promise(resolve)
|
|
34
39
|
}
|
|
35
40
|
|
|
36
|
-
async createTransaction()
|
|
41
|
+
async createTransaction(connection = await this.getConnection())
|
|
37
42
|
{
|
|
38
|
-
const connection = await this.getConnection()
|
|
39
43
|
await connection.query('START TRANSACTION')
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return transaction
|
|
44
|
+
return new AdapterMySqlTransaction(connection)
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
close()
|
|
@@ -12,14 +12,7 @@ class AdapterMySqlTransaction
|
|
|
12
12
|
throw new NestedTransactionsNotAllowed('Nested transactions are not allowed')
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
query(...
|
|
16
|
-
{
|
|
17
|
-
return this._query(...args)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// We are forced to have a different query function to be able to use it from inside
|
|
21
|
-
// the class without the Proxy making interferences
|
|
22
|
-
async _query(query, ...ctx)
|
|
15
|
+
query(query, ...ctx)
|
|
23
16
|
{
|
|
24
17
|
return new Promise((accept, reject) =>
|
|
25
18
|
this.connection.query(query, ...ctx, (error, response) =>
|
|
@@ -30,13 +23,13 @@ class AdapterMySqlTransaction
|
|
|
30
23
|
|
|
31
24
|
async commit()
|
|
32
25
|
{
|
|
33
|
-
await this.
|
|
26
|
+
await this.query('COMMIT')
|
|
34
27
|
this.connection.release()
|
|
35
28
|
}
|
|
36
29
|
|
|
37
30
|
async rollback()
|
|
38
31
|
{
|
|
39
|
-
await this.
|
|
32
|
+
await this.query('ROLLBACK')
|
|
40
33
|
this.connection.release()
|
|
41
34
|
}
|
|
42
35
|
}
|
package/adapter/mysql2/index.js
CHANGED
|
@@ -20,6 +20,11 @@ class AdapterMySql2
|
|
|
20
20
|
return new Promise(resolve)
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
getFormattedQuery(query, ...ctx)
|
|
24
|
+
{
|
|
25
|
+
return this.pool.format(query, ...ctx)
|
|
26
|
+
}
|
|
27
|
+
|
|
23
28
|
getConnection()
|
|
24
29
|
{
|
|
25
30
|
const resolve = (accept, reject, i = 0) =>
|
|
@@ -33,13 +38,51 @@ class AdapterMySql2
|
|
|
33
38
|
return new Promise(resolve)
|
|
34
39
|
}
|
|
35
40
|
|
|
36
|
-
async
|
|
41
|
+
async lock(
|
|
42
|
+
reference,
|
|
43
|
+
operation,
|
|
44
|
+
timeout = 10,
|
|
45
|
+
connection = await this.getConnection())
|
|
37
46
|
{
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
47
|
+
try
|
|
48
|
+
{
|
|
49
|
+
const
|
|
50
|
+
result = await connection.query('SELECT GET_LOCK(?, ?) as status', [reference, timeout]),
|
|
51
|
+
status = result[0].status
|
|
41
52
|
|
|
42
|
-
|
|
53
|
+
if(1 === status)
|
|
54
|
+
{
|
|
55
|
+
await operation(connection)
|
|
56
|
+
}
|
|
57
|
+
else if(0 === status)
|
|
58
|
+
{
|
|
59
|
+
const error = new Error(`advisory lock '${reference}' is busy by a different process`)
|
|
60
|
+
error.code = 'DB_LOCK_TIMEOUT'
|
|
61
|
+
error.reference = reference
|
|
62
|
+
error.timeout = timeout
|
|
63
|
+
throw error
|
|
64
|
+
}
|
|
65
|
+
else
|
|
66
|
+
{
|
|
67
|
+
const error = new Error(`something is wrong with the query for the advisory lock '${reference}'`)
|
|
68
|
+
error.code = 'E_EVENTFLOW_DB_ADVISORY_LOCK_NULL_RESULT'
|
|
69
|
+
error.reference = reference
|
|
70
|
+
error.timeout = timeout
|
|
71
|
+
throw error
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
finally
|
|
75
|
+
{
|
|
76
|
+
await connection.query('DO RELEASE_LOCK(?)', [reference])
|
|
77
|
+
.then(() => connection.release())
|
|
78
|
+
.catch(() => connection.end())
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async createTransaction(connection = await this.getConnection())
|
|
83
|
+
{
|
|
84
|
+
await connection.query('START TRANSACTION')
|
|
85
|
+
return new AdapterMySql2Transaction(connection)
|
|
43
86
|
}
|
|
44
87
|
|
|
45
88
|
close()
|
|
@@ -12,14 +12,7 @@ class AdapterMySql2Transaction
|
|
|
12
12
|
throw new NestedTransactionsNotAllowed('Nested transactions are not allowed')
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
query(...
|
|
16
|
-
{
|
|
17
|
-
return this._query(...args)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// We are forced to have a different query function to be able to use it from inside
|
|
21
|
-
// the class without the Proxy making interferences
|
|
22
|
-
async _query(query, ...ctx)
|
|
15
|
+
query(query, ...ctx)
|
|
23
16
|
{
|
|
24
17
|
return new Promise((accept, reject) =>
|
|
25
18
|
this.connection.query(query, ...ctx, (error, response) =>
|
|
@@ -30,13 +23,13 @@ class AdapterMySql2Transaction
|
|
|
30
23
|
|
|
31
24
|
async commit()
|
|
32
25
|
{
|
|
33
|
-
await this.
|
|
26
|
+
await this.query('COMMIT')
|
|
34
27
|
this.connection.release()
|
|
35
28
|
}
|
|
36
29
|
|
|
37
30
|
async rollback()
|
|
38
31
|
{
|
|
39
|
-
await this.
|
|
32
|
+
await this.query('ROLLBACK')
|
|
40
33
|
this.connection.release()
|
|
41
34
|
}
|
|
42
35
|
}
|
package/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const
|
|
2
2
|
util = require('util'),
|
|
3
3
|
fs = require('fs'),
|
|
4
|
+
os = require('os'),
|
|
4
5
|
promisify = util.promisify,
|
|
5
6
|
readFile = promisify(fs.readFile)
|
|
6
7
|
|
|
@@ -9,7 +10,7 @@ class Db
|
|
|
9
10
|
constructor(adaptor, queryPath, fileSuffix = '')
|
|
10
11
|
{
|
|
11
12
|
this.adaptor = adaptor
|
|
12
|
-
this.queryPath = require('path').normalize(queryPath) +
|
|
13
|
+
this.queryPath = require('path').normalize(queryPath) + os.EOL
|
|
13
14
|
this.fileSuffix = fileSuffix
|
|
14
15
|
this.queries = {}
|
|
15
16
|
}
|
|
@@ -21,56 +22,81 @@ class Db
|
|
|
21
22
|
|
|
22
23
|
async query(file, ...ctx)
|
|
23
24
|
{
|
|
24
|
-
let query = await this.getQuery(file)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
noEscapeReplace > -1;
|
|
28
|
-
noEscapeReplace = query.indexOf('?%s'))
|
|
25
|
+
let query = await this.getQuery(file, ctx)
|
|
26
|
+
|
|
27
|
+
try
|
|
29
28
|
{
|
|
30
|
-
|
|
29
|
+
return await this.adaptor.query(query, ...ctx)
|
|
30
|
+
}
|
|
31
|
+
catch(reason)
|
|
32
|
+
{
|
|
33
|
+
this._throwOnQueryError(reason, file, query, ctx)
|
|
31
34
|
}
|
|
32
|
-
|
|
33
|
-
return await this.adaptor.query(query, ...ctx)
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
async
|
|
37
|
+
async lock(...args)
|
|
37
38
|
{
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
if('lock' in this.adaptor)
|
|
40
|
+
{
|
|
41
|
+
await this.adaptor.lock(...args)
|
|
42
|
+
}
|
|
43
|
+
else
|
|
44
|
+
{
|
|
45
|
+
throw new Error('The database adapter provided does not support locking')
|
|
46
|
+
}
|
|
44
47
|
}
|
|
45
48
|
|
|
46
|
-
async createTransaction()
|
|
49
|
+
async createTransaction(connection)
|
|
47
50
|
{
|
|
48
|
-
const transaction = await this.adaptor.createTransaction()
|
|
51
|
+
const transaction = await this.adaptor.createTransaction(connection)
|
|
49
52
|
|
|
50
53
|
return new Proxy(transaction,
|
|
51
54
|
{
|
|
52
|
-
get: (target,
|
|
55
|
+
get: (target, member) =>
|
|
53
56
|
{
|
|
54
|
-
return
|
|
55
|
-
? target
|
|
57
|
+
return 'query' !== member
|
|
58
|
+
? Reflect.get(target, member, target)
|
|
56
59
|
: async (file, ...ctx) =>
|
|
57
60
|
{
|
|
58
|
-
const
|
|
59
|
-
query = await this.getQuery(file),
|
|
60
|
-
response = await transaction.query(query, ...ctx)
|
|
61
|
+
const query = await this.getQuery(file, ctx)
|
|
61
62
|
|
|
62
|
-
|
|
63
|
+
try
|
|
64
|
+
{
|
|
65
|
+
const method = Reflect.get(target, member, target)
|
|
66
|
+
return await Reflect.apply(method, target, [query, ...ctx])
|
|
67
|
+
}
|
|
68
|
+
catch(reason)
|
|
69
|
+
{
|
|
70
|
+
this._throwOnQueryError(reason, file, query, ctx)
|
|
71
|
+
}
|
|
63
72
|
}
|
|
64
73
|
}
|
|
65
74
|
})
|
|
66
75
|
}
|
|
67
76
|
|
|
68
|
-
async getQuery(file)
|
|
77
|
+
async getQuery(file, ctx)
|
|
69
78
|
{
|
|
70
|
-
const
|
|
79
|
+
const buffer = file in this.queries
|
|
71
80
|
? this.queries[file]
|
|
72
81
|
: this.queries[file] = await readFile(this.queryPath + file + this.fileSuffix)
|
|
73
|
-
|
|
82
|
+
|
|
83
|
+
let query = buffer.toString()
|
|
84
|
+
|
|
85
|
+
while(ctx.length && query.indexOf('?%s') > -1)
|
|
86
|
+
{
|
|
87
|
+
query = query.replace('?%s', ctx.shift())
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return query
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
_throwOnQueryError(reason, file, query, ctx)
|
|
94
|
+
{
|
|
95
|
+
const error = new Error(`DB query '${file}' failed`)
|
|
96
|
+
error.code = 'DB_QUERY_ERROR'
|
|
97
|
+
error.query = this.adaptor.getFormattedQuery?.(query, ...ctx) ?? query
|
|
98
|
+
error.cause = reason
|
|
99
|
+
throw error
|
|
74
100
|
}
|
|
75
101
|
}
|
|
76
102
|
|
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@superhero/db",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.1-rc.0",
|
|
4
4
|
"description": "Db interaction, designed to separate queries from the source code",
|
|
5
|
-
"repository":
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+ssh://git@github.com/superhero/js.db.git"
|
|
8
|
+
},
|
|
6
9
|
"main": "index.js",
|
|
7
10
|
"license": "MIT",
|
|
8
11
|
"author": {
|