dobo-clickhouse 2.2.1
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/.jsdoc.conf.json +45 -0
- package/LICENSE +21 -0
- package/README.md +49 -0
- package/extend/dobo/driver/clickhouse.js +59 -0
- package/index.js +30 -0
- package/lib/dialect/client.js +239 -0
- package/lib/dialect/query/compiler.js +76 -0
- package/lib/dialect/schema/columncompiler.js +112 -0
- package/lib/dialect/schema/compiler.js +47 -0
- package/lib/dialect/schema/tablecompiler.js +248 -0
- package/lib/dialect/transaction.js +30 -0
- package/package.json +43 -0
- package/wiki/CHANGES.md +6 -0
package/.jsdoc.conf.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"plugins": ["plugins/markdown", "jsdoc-plugin-intersection"],
|
|
3
|
+
"opts": {
|
|
4
|
+
"encoding": "utf8",
|
|
5
|
+
"recurse": true,
|
|
6
|
+
"verbose": true,
|
|
7
|
+
"destination": "./docs",
|
|
8
|
+
"template": "node_modules/clean-jsdoc-theme",
|
|
9
|
+
"readme": "./docs/static/home.md",
|
|
10
|
+
"theme_opts": {
|
|
11
|
+
"default_theme": "light",
|
|
12
|
+
"display-module-header": true,
|
|
13
|
+
"title": "DoboRedis API",
|
|
14
|
+
"homepageTitle": "DoboRedis API",
|
|
15
|
+
"sections": ["Classes", "Events", "Modules", "Global"],
|
|
16
|
+
"menu": [{
|
|
17
|
+
"title": "NPM",
|
|
18
|
+
"link": "https://www.npmjs.com/package/dobo-couchdb"
|
|
19
|
+
}, {
|
|
20
|
+
"title": "Github",
|
|
21
|
+
"link": "https://github.com/ardhi/dobo-couchdb"
|
|
22
|
+
}, {
|
|
23
|
+
"title": "Dobo",
|
|
24
|
+
"link": "https://dobo.bajo.app/"
|
|
25
|
+
}, {
|
|
26
|
+
"title": "Bajo",
|
|
27
|
+
"link": "https://bajo.app/"
|
|
28
|
+
}]
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"source": {
|
|
32
|
+
"include": ["."],
|
|
33
|
+
"includePattern": ".+\\.js(doc|x)?$",
|
|
34
|
+
"exclude": ["node_modules", "docs", "test"]
|
|
35
|
+
},
|
|
36
|
+
"markdown": {
|
|
37
|
+
"hardwrap": false,
|
|
38
|
+
"idInHeadings": true
|
|
39
|
+
},
|
|
40
|
+
"sourceType": "module",
|
|
41
|
+
"templates": {
|
|
42
|
+
"cleverLinks": false,
|
|
43
|
+
"monospaceLinks": false
|
|
44
|
+
}
|
|
45
|
+
}
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023-2025 Ardhi Lukianto
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# dobo-clickhouse
|
|
2
|
+
|
|
3
|
+
 
|
|
4
|
+
|
|
5
|
+
> <br />**Attention**: I do NOT accept any pull request at the moment, thanks! ([Why?](wiki/CONTRIBUTING.md))<br /><br />
|
|
6
|
+
|
|
7
|
+
Clickhouse driver for [Dobo](https://github.com/ardhi/dobo)
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
First, go to your ```{appDir}``` and run the following command in your terminal:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
$ npm install dobo dobo-clickhouse
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
And enable the plugin by adding ```dobo```, ```dobo-knex``` and ```dobo-clickhouse``` to the plugin list in either the ```$dataDir/config/.plugins``` file or the ```bajo.plugins``` array within your ```package.json``` file
|
|
18
|
+
|
|
19
|
+
## Documentations
|
|
20
|
+
|
|
21
|
+
- [Config Object](wiki/CONFIG.md)
|
|
22
|
+
- [API](https://ardhi.github.io/dobo-clickhouse)
|
|
23
|
+
- [Contributing](wiki/CONTRIBUTING.md)
|
|
24
|
+
|
|
25
|
+
## Hire Me
|
|
26
|
+
|
|
27
|
+
If you have a Bajo Framework-based project and need a professional service or assistance, please <a href="https://github.com/ardhi#professional-service">click here</a>. I'd be happy to work on it at a competitive price and with fast turnaround!
|
|
28
|
+
|
|
29
|
+
## Support Me
|
|
30
|
+
|
|
31
|
+
Please support me using the channels below. Your donation will motivate me to work faster and more diligently on future development.
|
|
32
|
+
|
|
33
|
+
<a href="https://github.com/sponsors/ardhi">
|
|
34
|
+
<img src="https://img.shields.io/badge/Github-slategrey?style=flat&logo=github" height="50">
|
|
35
|
+
</a>
|
|
36
|
+
<a href="https://www.patreon.com/bajoframework">
|
|
37
|
+
<img src="https://img.shields.io/badge/Patreon-f2c3b2?style=flat&logo=patreon" height="50">
|
|
38
|
+
</a>
|
|
39
|
+
<a href="https://www.paypal.com/ncp/payment/EWLERL7SCUU64">
|
|
40
|
+
<img src="https://img.shields.io/badge/Paypal-blue?style=flat&logo=paypal" height="50">
|
|
41
|
+
</a>
|
|
42
|
+
|
|
43
|
+
<p>
|
|
44
|
+
<div><img alt="bc1qwtv78cwp9ef8hnqaw84fxg5856l0pggqe32g6f" src="docs/static/bitcoin.jpeg" width="150" height="150" /><br>Bitcoin</div>
|
|
45
|
+
</p>
|
|
46
|
+
|
|
47
|
+
## License
|
|
48
|
+
|
|
49
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import clientFactory from '../../../lib/dialect/client.js'
|
|
2
|
+
|
|
3
|
+
async function clickhouseDriverFactory () {
|
|
4
|
+
const { DoboKnexDriver } = this.app.baseClass
|
|
5
|
+
|
|
6
|
+
class DoboClickhouseDriver extends DoboKnexDriver {
|
|
7
|
+
constructor (plugin, name, options) {
|
|
8
|
+
super(plugin, name, options)
|
|
9
|
+
this.idField = {
|
|
10
|
+
name: 'id',
|
|
11
|
+
type: 'string',
|
|
12
|
+
required: true,
|
|
13
|
+
autoInc: true,
|
|
14
|
+
index: 'primary'
|
|
15
|
+
}
|
|
16
|
+
this.idGenerator = 'uuidv7'
|
|
17
|
+
this.defaultEngine = 'MergeTree'
|
|
18
|
+
this.support.returning = false
|
|
19
|
+
this.support.uniqueIndex = false
|
|
20
|
+
this.support.nullableField = false
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async sanitizeConnection (item) {
|
|
24
|
+
await super.sanitizeConnection(item)
|
|
25
|
+
item.port = item.port ?? 8123
|
|
26
|
+
item.user = item.user ?? 'default'
|
|
27
|
+
item.host = item.host ?? '127.0.0.1'
|
|
28
|
+
item.database = item.database ?? 'default'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async connect (connection, noRebuild) {
|
|
32
|
+
const { importPkg } = this.app.bajo
|
|
33
|
+
const knex = await importPkg('doboKnex:knex')
|
|
34
|
+
const client = await clientFactory.call(this.plugin)
|
|
35
|
+
const { user, password, host, port, database } = connection.options
|
|
36
|
+
connection.client = knex({
|
|
37
|
+
client,
|
|
38
|
+
connection: () => {
|
|
39
|
+
return `clickhouse://${user}:${password}@${host}:${port}/${database}`
|
|
40
|
+
},
|
|
41
|
+
...this.options
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async updateRecord (model, id, body = {}, options = {}) {
|
|
46
|
+
const oldData = options._data
|
|
47
|
+
const client = model.connection.client
|
|
48
|
+
const result = await client(model.collName).where('id', id).update(body, this._getReturningFields(model, options))
|
|
49
|
+
if (options.noResult) return
|
|
50
|
+
if (this.support.returning) return { data: result[0], oldData }
|
|
51
|
+
const resp = await this.getRecord(model, id)
|
|
52
|
+
return { data: resp.data, oldData }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return DoboClickhouseDriver
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export default clickhouseDriverFactory
|
package/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin factory
|
|
3
|
+
*
|
|
4
|
+
* @param {string} pkgName - NPM package name
|
|
5
|
+
* @returns {class}
|
|
6
|
+
*/
|
|
7
|
+
async function factory (pkgName) {
|
|
8
|
+
const me = this
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* DoboClickhouse class
|
|
12
|
+
*
|
|
13
|
+
* @class
|
|
14
|
+
*/
|
|
15
|
+
class DoboClickhouse extends this.app.baseClass.Base {
|
|
16
|
+
constructor () {
|
|
17
|
+
super(pkgName, me.app)
|
|
18
|
+
this.config = {
|
|
19
|
+
connections: []
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
exit = () => {
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return DoboClickhouse
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default factory
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
// From: https://github.com/DnAp/KnexClickhouseDialect/blob/master/src/client.js
|
|
2
|
+
import ClickHouse from 'clickhouse'
|
|
3
|
+
import _qc from './query/compiler.js'
|
|
4
|
+
import _sc from './schema/compiler.js'
|
|
5
|
+
import _tc from './schema/tablecompiler.js'
|
|
6
|
+
import _cc from './schema/columncompiler.js'
|
|
7
|
+
import TransactionClickHouse from './transaction.js'
|
|
8
|
+
import sqlString from 'sqlstring'
|
|
9
|
+
|
|
10
|
+
function ltrimSlashes (v) {
|
|
11
|
+
return v.replace(/^\//g, '')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function clientFactory () {
|
|
15
|
+
const { importModule } = this.app.bajo
|
|
16
|
+
const { isString, isFunction, assign, map } = this.app.lib._
|
|
17
|
+
const makeEscape = (await importModule('doboKnex:node_modules/knex/lib/util/string.js')).makeEscape
|
|
18
|
+
const TableBuilder = await importModule('doboKnex:node_modules/knex/lib/schema/tablebuilder.js')
|
|
19
|
+
const Client = await importModule('doboKnex:node_modules/knex/lib/client.js')
|
|
20
|
+
const QueryCompiler = await _qc.call(this)
|
|
21
|
+
const SchemaCompilerClickHouse = await _sc.call(this)
|
|
22
|
+
const TableCompilerClickHouse = await _tc.call(this)
|
|
23
|
+
const ColumnCompilerClickHouse = await _cc.call(this)
|
|
24
|
+
|
|
25
|
+
class ClickhouseClient extends Client {
|
|
26
|
+
dialect = 'clickhouse'
|
|
27
|
+
driverName = 'clickhouse'
|
|
28
|
+
_escapeBinding = makeEscape()
|
|
29
|
+
canCancelQuery = true
|
|
30
|
+
_migrationLockTableName = '`knex_migrations_lock`'
|
|
31
|
+
|
|
32
|
+
constructor (config = {}) {
|
|
33
|
+
super(config)
|
|
34
|
+
this.initializeDriver()
|
|
35
|
+
this.initializePool()
|
|
36
|
+
if (config.migrations && config.migrations.tableName) {
|
|
37
|
+
const migrationCfg = config.migrations
|
|
38
|
+
if (migrationCfg.schemaName) {
|
|
39
|
+
this._migrationLockTableName = '`' + migrationCfg.schemaName + '`.`' + migrationCfg.tableName + '_lock`'
|
|
40
|
+
} else {
|
|
41
|
+
this._migrationLockTableName = '`' + migrationCfg.tableName + '_lock`'
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
_driver () {
|
|
47
|
+
return ClickHouse
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
tableBuilder (type, tableName, tableNameLike, fn) {
|
|
51
|
+
const builder = new TableBuilder(this, type, tableName, tableNameLike, fn)
|
|
52
|
+
builder.engine = function (val) {
|
|
53
|
+
this._single.engine = val
|
|
54
|
+
}
|
|
55
|
+
return builder
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
queryCompiler (...args) {
|
|
59
|
+
return new QueryCompiler(this, ...args)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
schemaCompiler (builder) {
|
|
63
|
+
return new SchemaCompilerClickHouse(this, builder)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
tableCompiler (tableBuilder) {
|
|
67
|
+
return new TableCompilerClickHouse(this, tableBuilder)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
columnCompiler () {
|
|
71
|
+
return new ColumnCompilerClickHouse(this, ...arguments)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async transaction () {
|
|
75
|
+
return new TransactionClickHouse(this, ...arguments)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
wrapIdentifierImpl (value) {
|
|
79
|
+
return value !== '*' ? `\`${value.replace(/`/g, '``')}\`` : '*'
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Get a raw connection, called by the `pool` whenever a new
|
|
83
|
+
// connection needs to be added to the pool.
|
|
84
|
+
async acquireRawConnection () {
|
|
85
|
+
const config = await this.getConfiguration()
|
|
86
|
+
return new (this.driver.ClickHouse)(config)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async getConfiguration () {
|
|
90
|
+
let config = this.config.connection
|
|
91
|
+
if (isFunction(config)) {
|
|
92
|
+
config = await config()
|
|
93
|
+
}
|
|
94
|
+
if (isString(config)) {
|
|
95
|
+
const url = new URL(config)
|
|
96
|
+
config = {
|
|
97
|
+
url: url.hostname,
|
|
98
|
+
port: url.port ? url.port : 8123,
|
|
99
|
+
user: url.username,
|
|
100
|
+
password: url.password,
|
|
101
|
+
database: ltrimSlashes(url.pathname),
|
|
102
|
+
debug: false,
|
|
103
|
+
basicAuth: null,
|
|
104
|
+
isUseGzip: false,
|
|
105
|
+
usePost: true,
|
|
106
|
+
config: {
|
|
107
|
+
session_timeout: 60,
|
|
108
|
+
output_format_json_quote_64bit_integers: 0,
|
|
109
|
+
enable_http_compression: 0
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return config
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Used to explicitly close a connection, called internally by the pool
|
|
118
|
+
// when a connection times out or the pool is shutdown.
|
|
119
|
+
destroyRawConnection (connection) {
|
|
120
|
+
connection.destroy()
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// eslint-disable-next-line no-unused-vars
|
|
124
|
+
validateConnection (connection) {
|
|
125
|
+
return true
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Grab a connection, run the query via the MySQL streaming interface,
|
|
129
|
+
// and pass that through to the stream we've sent back to the client.
|
|
130
|
+
_stream (connection, obj, stream, options) {
|
|
131
|
+
options = options || {}
|
|
132
|
+
const queryOptions = assign({ sql: obj.sql }, obj.options)
|
|
133
|
+
return new Promise((resolve, reject) => {
|
|
134
|
+
stream.on('error', reject)
|
|
135
|
+
stream.on('end', resolve)
|
|
136
|
+
const queryStream = connection
|
|
137
|
+
.query(queryOptions, obj.bindings)
|
|
138
|
+
.stream(options)
|
|
139
|
+
|
|
140
|
+
queryStream.on('error', (err) => {
|
|
141
|
+
reject(err)
|
|
142
|
+
stream.emit('error', err)
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
queryStream.pipe(stream)
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Runs the query on the specified connection, providing the bindingsand any other necessary prep work.
|
|
151
|
+
*/
|
|
152
|
+
async _query (connection, obj) {
|
|
153
|
+
if (!obj || typeof obj === 'string') obj = { sql: obj }
|
|
154
|
+
// dirty hack for knex-migrator
|
|
155
|
+
if (['insert', 'update'].includes(obj.method) && obj.sql.indexOf(this._migrationLockTableName) > -1) {
|
|
156
|
+
obj.response = [[], []]
|
|
157
|
+
return obj
|
|
158
|
+
}
|
|
159
|
+
return new Promise((resolve, reject) => {
|
|
160
|
+
if (!obj.sql) {
|
|
161
|
+
resolve()
|
|
162
|
+
return
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const queryOptions = assign({ sql: obj.sql }, obj.options)
|
|
166
|
+
const query = this._applyBindings(queryOptions.sql, obj.bindings)
|
|
167
|
+
|
|
168
|
+
connection.query(query, (err, rows, fields) => {
|
|
169
|
+
if (err) return reject(err)
|
|
170
|
+
obj.response = [rows, fields]
|
|
171
|
+
resolve(obj)
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
_applyBindings (sql, bindings) {
|
|
177
|
+
for (let i = 0; i < bindings.length; i++) {
|
|
178
|
+
if (bindings[i] instanceof Date) {
|
|
179
|
+
bindings[i] = bindings[i].toISOString()
|
|
180
|
+
.replace(/T/, ' ')
|
|
181
|
+
.replace(/\..+/, '')
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return sqlString.format(sql, bindings)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Process the response as returned from the query.
|
|
188
|
+
processResponse (obj, runner) {
|
|
189
|
+
if (obj == null) return
|
|
190
|
+
const { response } = obj
|
|
191
|
+
const { method } = obj
|
|
192
|
+
const rows = response[0]
|
|
193
|
+
const fields = response[1]
|
|
194
|
+
if (obj.output) return obj.output.call(runner, rows, fields)
|
|
195
|
+
switch (method) {
|
|
196
|
+
case 'select':
|
|
197
|
+
case 'pluck':
|
|
198
|
+
case 'first': {
|
|
199
|
+
if (method === 'pluck') return map(rows, obj.pluck)
|
|
200
|
+
return method === 'first' ? rows[0] : rows
|
|
201
|
+
}
|
|
202
|
+
case 'insert':
|
|
203
|
+
return [rows.insertId]
|
|
204
|
+
case 'del':
|
|
205
|
+
case 'update':
|
|
206
|
+
case 'counter':
|
|
207
|
+
return rows.affectedRows
|
|
208
|
+
default:
|
|
209
|
+
return response
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
cancelQuery (connectionToKill) {
|
|
214
|
+
const acquiringConn = this.acquireConnection()
|
|
215
|
+
|
|
216
|
+
// Error out if we can't acquire connection in time.
|
|
217
|
+
// Purposely not putting timeout on `KILL QUERY` execution because erroring
|
|
218
|
+
// early there would release the `connectionToKill` back to the pool with
|
|
219
|
+
// a `KILL QUERY` command yet to finish.
|
|
220
|
+
return acquiringConn
|
|
221
|
+
.timeout(100)
|
|
222
|
+
.then((conn) => this.query(conn, {
|
|
223
|
+
method: 'raw',
|
|
224
|
+
sql: 'KILL QUERY ?',
|
|
225
|
+
bindings: [connectionToKill.threadId],
|
|
226
|
+
options: {}
|
|
227
|
+
}))
|
|
228
|
+
.finally(() => {
|
|
229
|
+
// NOT returning this promise because we want to release the connection
|
|
230
|
+
// in a non-blocking fashion
|
|
231
|
+
acquiringConn.then((conn) => this.releaseConnection(conn))
|
|
232
|
+
})
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return ClickhouseClient
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export default clientFactory
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// From: https://github.com/DnAp/KnexClickhouseDialect/blob/master/src/query/compiler.js
|
|
2
|
+
|
|
3
|
+
async function factory () {
|
|
4
|
+
const { importModule } = this.app.bajo
|
|
5
|
+
const { identity } = this.app.lib._
|
|
6
|
+
const QueryCompiler = await importModule('doboKnex:node_modules/knex/lib/query/querycompiler.js')
|
|
7
|
+
|
|
8
|
+
class QueryCompilerClickhouse extends QueryCompiler {
|
|
9
|
+
_emptyInsertValue = '() values ()'
|
|
10
|
+
|
|
11
|
+
constructor (client, builder, bindings) {
|
|
12
|
+
super(client, builder, bindings)
|
|
13
|
+
const { returning } = this.single
|
|
14
|
+
|
|
15
|
+
if (returning) {
|
|
16
|
+
this.client.logger.warn(
|
|
17
|
+
'.returning() is not supported by Clickhouse and will not have any effect.'
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Update method, including joins, wheres, order & limits.
|
|
23
|
+
/*
|
|
24
|
+
update () {
|
|
25
|
+
const { tableName } = this
|
|
26
|
+
const updateData = this._prepUpdate(this.single.update)
|
|
27
|
+
const wheres = this.where()
|
|
28
|
+
return `ALTER TABLE ${tableName} UPDATE ` +
|
|
29
|
+
updateData.join(', ') +
|
|
30
|
+
(wheres ? ` ${wheres}` : '') + ' SETTINGS mutations_sync = 1'
|
|
31
|
+
}
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
// Compiles a `columnInfo` query.
|
|
35
|
+
columnInfo () {
|
|
36
|
+
const column = this.single.columnInfo
|
|
37
|
+
|
|
38
|
+
// The user may have specified a custom wrapIdentifier function in the config. We
|
|
39
|
+
// need to run the identifiers through that function, but not format them as
|
|
40
|
+
// identifiers otherwise.
|
|
41
|
+
const table = this.client.customWrapIdentifier(this.single.table, identity)
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
sql:
|
|
45
|
+
'select * from information_schema.columns where table_name = ? and table_schema = ?',
|
|
46
|
+
bindings: [table, this.client.database()],
|
|
47
|
+
output (resp) {
|
|
48
|
+
const out = resp.reduce(function r (columns, val) {
|
|
49
|
+
columns[val.COLUMN_NAME] = {
|
|
50
|
+
defaultValue: val.COLUMN_DEFAULT,
|
|
51
|
+
type: val.DATA_TYPE,
|
|
52
|
+
maxLength: val.CHARACTER_MAXIMUM_LENGTH,
|
|
53
|
+
nullable: val.IS_NULLABLE === 'YES'
|
|
54
|
+
}
|
|
55
|
+
return columns
|
|
56
|
+
}, {})
|
|
57
|
+
return (column && out[column]) || out
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
limit () {
|
|
63
|
+
// Workaround for offset only.
|
|
64
|
+
// see: http://stackoverflow.com/questions/255517/mysql-offset-infinite-rows
|
|
65
|
+
if (this.single.offset && !this.single.limit && this.single.limit !== 0) return 'limit 18446744073709551615'
|
|
66
|
+
|
|
67
|
+
return super.limit()
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Set the QueryBuilder & QueryCompiler on the client object,
|
|
72
|
+
// in case anyone wants to modify things to suit their own purposes.
|
|
73
|
+
return QueryCompilerClickhouse
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export default factory
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// From: https://github.com/DnAp/KnexClickhouseDialect/blob/master/src/schema/columncompiler.js
|
|
2
|
+
|
|
3
|
+
async function factory () {
|
|
4
|
+
const { importModule } = this.app.bajo
|
|
5
|
+
|
|
6
|
+
const ColumnCompiler = await importModule('doboKnex:node_modules/knex/lib/schema/columncompiler.js')
|
|
7
|
+
const { toNumber } = await importModule('doboKnex:node_modules/knex/lib/util/helpers.js')
|
|
8
|
+
const Raw = await importModule('doboKnex:node_modules/knex/lib/raw.js')
|
|
9
|
+
|
|
10
|
+
class ColumnCompilerClickHouse extends ColumnCompiler {
|
|
11
|
+
modifiers = ['defaultTo']
|
|
12
|
+
|
|
13
|
+
increments = 'UUID default generateUUIDv7()'
|
|
14
|
+
|
|
15
|
+
bigincrements = 'UUID default generateUUIDv7()'
|
|
16
|
+
|
|
17
|
+
smallint = 'Int8'
|
|
18
|
+
|
|
19
|
+
mediumint = 'Int16'
|
|
20
|
+
|
|
21
|
+
integer = 'Int32'
|
|
22
|
+
|
|
23
|
+
bigint = 'Int64'
|
|
24
|
+
|
|
25
|
+
text = 'String'
|
|
26
|
+
|
|
27
|
+
varchar = 'String'
|
|
28
|
+
|
|
29
|
+
datetime = 'datetime'
|
|
30
|
+
|
|
31
|
+
timestamp = 'datetime'
|
|
32
|
+
|
|
33
|
+
time = 'time'
|
|
34
|
+
|
|
35
|
+
/*
|
|
36
|
+
double (precision, scale) {
|
|
37
|
+
return `Decimal32(${toNumber(precision, 8)}, ${toNumber(scale, 2)})`
|
|
38
|
+
}
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
float = 'Float32'
|
|
42
|
+
|
|
43
|
+
double = 'Float64'
|
|
44
|
+
|
|
45
|
+
enu (allowed) {
|
|
46
|
+
// todo
|
|
47
|
+
// let enumData = [];
|
|
48
|
+
// allowed.forEach((v, k) => {
|
|
49
|
+
// enumData += '';
|
|
50
|
+
// });
|
|
51
|
+
return `enum('${allowed.join('\', \'')}')`
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
bit (length) {
|
|
55
|
+
return length ? `bit(${toNumber(length)})` : 'bit'
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
binary (length) {
|
|
59
|
+
return length ? `varbinary(${toNumber(length)})` : 'blob'
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
json () {
|
|
63
|
+
return 'json'
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
jsonb () {
|
|
67
|
+
return 'json'
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Modifiers
|
|
71
|
+
// ------
|
|
72
|
+
|
|
73
|
+
defaultTo (value) {
|
|
74
|
+
if (value === null || value === undefined) {
|
|
75
|
+
return ''
|
|
76
|
+
}
|
|
77
|
+
if (value instanceof Raw) {
|
|
78
|
+
value = value.toQuery()
|
|
79
|
+
} else if (this.type === 'bool' || this.type === 'UInt8') {
|
|
80
|
+
if (value === 'false') value = 0
|
|
81
|
+
value = value ? 1 : 0
|
|
82
|
+
} else {
|
|
83
|
+
value = this.client._escapeBinding(value.toString())
|
|
84
|
+
}
|
|
85
|
+
return 'default ' + value
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
unsigned () {
|
|
89
|
+
return ''
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
comment () {
|
|
93
|
+
return ''
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
first () {
|
|
97
|
+
return ''
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
after (column) {
|
|
101
|
+
return `after ${this.formatter.wrap(column)}`
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
collate (collation) {
|
|
105
|
+
return collation && `collate '${collation}'`
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return ColumnCompilerClickHouse
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export default factory
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// From: https://github.com/DnAp/KnexClickhouseDialect/blob/master/src/schema/compiler.js
|
|
2
|
+
|
|
3
|
+
async function factory () {
|
|
4
|
+
const { importModule } = this.app.bajo
|
|
5
|
+
const SchemaCompiler = await importModule('doboKnex:node_modules/knex/lib/schema/compiler.js')
|
|
6
|
+
|
|
7
|
+
class SchemaCompilerClickHouse extends SchemaCompiler {
|
|
8
|
+
// Rename a table on the schema.
|
|
9
|
+
renameTable (tableName, to) {
|
|
10
|
+
this.pushQuery(
|
|
11
|
+
`rename table ${this.formatter.wrap(tableName)} to ${this.formatter.wrap(to)}`
|
|
12
|
+
)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Check whether a table exists on the query.
|
|
16
|
+
hasTable (tableName) {
|
|
17
|
+
const sql = 'select name from system.tables where name = ? and database = currentDatabase()'
|
|
18
|
+
const bindings = [tableName]
|
|
19
|
+
|
|
20
|
+
this.pushQuery({
|
|
21
|
+
sql,
|
|
22
|
+
bindings,
|
|
23
|
+
output: function output (resp) {
|
|
24
|
+
return resp.length > 0
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Check whether a column exists on the schema.
|
|
30
|
+
hasColumn (tableName, column) {
|
|
31
|
+
const sql = 'SELECT name FROM system.columns where table = ? and name = ? and database = currentDatabase()'
|
|
32
|
+
const bindings = [tableName, column]
|
|
33
|
+
|
|
34
|
+
this.pushQuery({
|
|
35
|
+
sql,
|
|
36
|
+
bindings,
|
|
37
|
+
output: function output (resp) {
|
|
38
|
+
return resp.length > 0
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return SchemaCompilerClickHouse
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default factory
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
// From: https://github.com/DnAp/KnexClickhouseDialect/blob/master/src/schema/tablecompiler.js
|
|
2
|
+
|
|
3
|
+
async function factory () {
|
|
4
|
+
const { importModule } = this.app.bajo
|
|
5
|
+
const { isString, isPlainObject } = this.app.lib._
|
|
6
|
+
const TableCompiler = await importModule('doboKnex:node_modules/knex/lib/schema/tablecompiler.js')
|
|
7
|
+
// Table Compiler
|
|
8
|
+
// ------
|
|
9
|
+
|
|
10
|
+
class TableCompilerClickHouse extends TableCompiler {
|
|
11
|
+
addColumnsPrefix = 'add '
|
|
12
|
+
alterColumnsPrefix = 'modify '
|
|
13
|
+
dropColumnPrefix = 'drop '
|
|
14
|
+
|
|
15
|
+
createQuery (columns, ifNot) {
|
|
16
|
+
const createStatement = ifNot ? 'create table if not exists ' : 'create table '
|
|
17
|
+
let sql = createStatement + this.tableName() + ' (' + columns.sql.join(', ') + ')'
|
|
18
|
+
|
|
19
|
+
const engine = this.single.engine || 'TinyLog'
|
|
20
|
+
|
|
21
|
+
if (engine) sql += ` engine = ${engine}`
|
|
22
|
+
|
|
23
|
+
// hack: find primary key statement and put it here
|
|
24
|
+
const primary = this.tableBuilder._statements.find(stmt => stmt.grouping === 'alterTable' && stmt.method === 'primary')
|
|
25
|
+
if (primary && !['TinyLog'].includes(engine)) {
|
|
26
|
+
sql += ` primary key (${this.formatter.columnize(primary.args[0])})`
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (this.single.comment) {
|
|
30
|
+
const comment = this.single.comment || ''
|
|
31
|
+
if (comment.length > 60) this.client.logger.warn('The max length for a table comment is 60 characters')
|
|
32
|
+
sql += ` comment = '${comment}'`
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
sql += ' SETTINGS enable_block_number_column = 1, enable_block_offset_column = 1'
|
|
36
|
+
this.pushQuery(sql)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Compiles the comment on the table.
|
|
40
|
+
comment (comment) {
|
|
41
|
+
this.pushQuery(`alter table ${this.tableName()} comment = '${comment}'`)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
changeType () {
|
|
45
|
+
// alter table + table + ' modify ' + wrapped + '// type';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Renames a column on the table.
|
|
49
|
+
renameColumn (from, to) {
|
|
50
|
+
const compiler = this
|
|
51
|
+
const table = this.tableName()
|
|
52
|
+
const wrapped = this.formatter.wrap(from) + ' ' + this.formatter.wrap(to)
|
|
53
|
+
|
|
54
|
+
this.pushQuery({
|
|
55
|
+
sql:
|
|
56
|
+
`show fields from ${table} where field = ` +
|
|
57
|
+
this.formatter.parameter(from),
|
|
58
|
+
output (resp) {
|
|
59
|
+
const column = resp[0]
|
|
60
|
+
const runner = this
|
|
61
|
+
return compiler.getFKRefs(runner)
|
|
62
|
+
.then(([refs]) => new Promise((resolve, reject) => {
|
|
63
|
+
try {
|
|
64
|
+
if (!refs.length) {
|
|
65
|
+
resolve()
|
|
66
|
+
}
|
|
67
|
+
resolve(compiler.dropFKRefs(runner, refs))
|
|
68
|
+
} catch (e) {
|
|
69
|
+
reject(e)
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
.then(function f () {
|
|
73
|
+
let sql = `alter table ${table} change ${wrapped} ${column.Type}`
|
|
74
|
+
|
|
75
|
+
if (String(column.Null)
|
|
76
|
+
.toUpperCase() !== 'YES') {
|
|
77
|
+
sql += ' NOT NULL'
|
|
78
|
+
} else {
|
|
79
|
+
// This doesn't matter for most cases except Timestamp, where this is important
|
|
80
|
+
sql += ' NULL'
|
|
81
|
+
}
|
|
82
|
+
if (column.Default) {
|
|
83
|
+
sql += ` DEFAULT '${column.Default}'`
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return runner.query({
|
|
87
|
+
sql
|
|
88
|
+
})
|
|
89
|
+
})
|
|
90
|
+
.then(function f () {
|
|
91
|
+
if (!refs.length) {
|
|
92
|
+
return undefined
|
|
93
|
+
}
|
|
94
|
+
return compiler.createFKRefs(
|
|
95
|
+
runner,
|
|
96
|
+
refs.map(function m (ref) {
|
|
97
|
+
if (ref.REFERENCED_COLUMN_NAME === from) {
|
|
98
|
+
ref.REFERENCED_COLUMN_NAME = to
|
|
99
|
+
}
|
|
100
|
+
if (ref.COLUMN_NAME === from) {
|
|
101
|
+
ref.COLUMN_NAME = to
|
|
102
|
+
}
|
|
103
|
+
return ref
|
|
104
|
+
})
|
|
105
|
+
)
|
|
106
|
+
}))
|
|
107
|
+
}
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
getFKRefs (runner) {
|
|
112
|
+
// todo wtf?
|
|
113
|
+
const formatter = this.client.formatter(this.tableBuilder)
|
|
114
|
+
const sql = `SELECT KCU.CONSTRAINT_NAME, KCU.TABLE_NAME, KCU.COLUMN_NAME,
|
|
115
|
+
KCU.REFERENCED_TABLE_NAME, KCU.REFERENCED_COLUMN_NAME,
|
|
116
|
+
RC.UPDATE_RULE,
|
|
117
|
+
RC.DELETE_RULE FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS RC
|
|
118
|
+
USING(CONSTRAINT_NAME)WHERE KCU.REFERENCED_TABLE_NAME = ${formatter.parameter(this.tableNameRaw)} AND KCU.CONSTRAINT_SCHEMA = ${formatter.parameter(this.client.database())} AND RC.CONSTRAINT_SCHEMA = ${formatter.parameter(this.client.database())}`
|
|
119
|
+
|
|
120
|
+
return runner.query({
|
|
121
|
+
sql,
|
|
122
|
+
bindings: formatter.bindings
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
dropFKRefs (runner, refs) {
|
|
127
|
+
const formatter = this.client.formatter(this.tableBuilder)
|
|
128
|
+
|
|
129
|
+
return Promise.all(
|
|
130
|
+
refs.map(function f (ref) {
|
|
131
|
+
const constraintName = formatter.wrap(ref.CONSTRAINT_NAME)
|
|
132
|
+
const tableName = formatter.wrap(ref.TABLE_NAME)
|
|
133
|
+
return runner.query({
|
|
134
|
+
sql: `alter table ${tableName} drop foreign key ${constraintName}`
|
|
135
|
+
})
|
|
136
|
+
})
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
createFKRefs (runner, refs) {
|
|
141
|
+
const formatter = this.client.formatter(this.tableBuilder)
|
|
142
|
+
|
|
143
|
+
return Promise.all(
|
|
144
|
+
refs.map(function f (ref) {
|
|
145
|
+
const tableName = formatter.wrap(ref.TABLE_NAME)
|
|
146
|
+
const keyName = formatter.wrap(ref.CONSTRAINT_NAME)
|
|
147
|
+
const column = formatter.columnize(ref.COLUMN_NAME)
|
|
148
|
+
const references = formatter.columnize(ref.REFERENCED_COLUMN_NAME)
|
|
149
|
+
const inTable = formatter.wrap(ref.REFERENCED_TABLE_NAME)
|
|
150
|
+
const onUpdate = ` ON UPDATE ${ref.UPDATE_RULE}`
|
|
151
|
+
const onDelete = ` ON DELETE ${ref.DELETE_RULE}`
|
|
152
|
+
|
|
153
|
+
return runner.query({
|
|
154
|
+
sql:
|
|
155
|
+
`alter table ${tableName} add constraint ${keyName} ` +
|
|
156
|
+
'foreign key (' +
|
|
157
|
+
column +
|
|
158
|
+
') references ' +
|
|
159
|
+
inTable +
|
|
160
|
+
' (' +
|
|
161
|
+
references +
|
|
162
|
+
')' +
|
|
163
|
+
onUpdate +
|
|
164
|
+
onDelete
|
|
165
|
+
})
|
|
166
|
+
})
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
index (columns, indexName, options) {
|
|
171
|
+
let indexType
|
|
172
|
+
|
|
173
|
+
if (isString(options)) {
|
|
174
|
+
indexType = options
|
|
175
|
+
} else if (isPlainObject(options)) {
|
|
176
|
+
({ indexType } = options)
|
|
177
|
+
}
|
|
178
|
+
if (!indexType) indexType = 'bloom_filter'
|
|
179
|
+
|
|
180
|
+
indexName = indexName
|
|
181
|
+
? this.formatter.wrap(indexName)
|
|
182
|
+
: this._indexCommand('index', this.tableNameRaw, columns)
|
|
183
|
+
this.pushQuery(
|
|
184
|
+
`alter table ${this.tableName()} add index ${indexName}(${this.formatter.columnize(columns)}) type ${indexType}`
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
primary (columns, constraintName) {
|
|
189
|
+
// these won't work since primary key needs to be defined inside create table
|
|
190
|
+
/*
|
|
191
|
+
constraintName = constraintName
|
|
192
|
+
? this.formatter.wrap(constraintName)
|
|
193
|
+
: this.formatter.wrap(`${this.tableNameRaw}_pkey`)
|
|
194
|
+
this.pushQuery(
|
|
195
|
+
`alter table ${this.tableName()} add primary key ${constraintName}(${this.formatter.columnize(
|
|
196
|
+
columns
|
|
197
|
+
)})`
|
|
198
|
+
)
|
|
199
|
+
*/
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
unique (columns, indexName) {
|
|
203
|
+
indexName = indexName
|
|
204
|
+
? this.formatter.wrap(indexName)
|
|
205
|
+
: this._indexCommand('unique', this.tableNameRaw, columns)
|
|
206
|
+
this.pushQuery(
|
|
207
|
+
`alter table ${this.tableName()} add unique ${indexName}(${this.formatter.columnize(
|
|
208
|
+
columns
|
|
209
|
+
)})`
|
|
210
|
+
)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Compile a drop index command.
|
|
214
|
+
dropIndex (columns, indexName) {
|
|
215
|
+
indexName = indexName
|
|
216
|
+
? this.formatter.wrap(indexName)
|
|
217
|
+
: this._indexCommand('index', this.tableNameRaw, columns)
|
|
218
|
+
this.pushQuery(`alter table ${this.tableName()} drop index ${indexName}`)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Compile a drop foreign key command.
|
|
222
|
+
dropForeign (columns, indexName) {
|
|
223
|
+
indexName = indexName
|
|
224
|
+
? this.formatter.wrap(indexName)
|
|
225
|
+
: this._indexCommand('foreign', this.tableNameRaw, columns)
|
|
226
|
+
this.pushQuery(
|
|
227
|
+
`alter table ${this.tableName()} drop foreign key ${indexName}`
|
|
228
|
+
)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Compile a drop primary key command.
|
|
232
|
+
dropPrimary () {
|
|
233
|
+
// this.pushQuery(`alter table ${this.tableName()} drop primary key`)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Compile a drop unique key command.
|
|
237
|
+
dropUnique (column, indexName) {
|
|
238
|
+
indexName = indexName
|
|
239
|
+
? this.formatter.wrap(indexName)
|
|
240
|
+
: this._indexCommand('unique', this.tableNameRaw, column)
|
|
241
|
+
this.pushQuery(`alter table ${this.tableName()} drop index ${indexName}`)
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return TableCompilerClickHouse
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export default factory
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// From: https://github.com/DnAp/KnexClickhouseDialect/blob/master/src/transaction.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @implements {Knex.Transaction}
|
|
5
|
+
*/
|
|
6
|
+
class TransactionClickHouse {
|
|
7
|
+
executionPromise = Promise.resolve(undefined)
|
|
8
|
+
|
|
9
|
+
commit (value) {
|
|
10
|
+
return undefined
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
isCompleted () {
|
|
14
|
+
return true
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
query (conn, sql, status, value) {
|
|
18
|
+
return undefined
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
rollback () {
|
|
22
|
+
return undefined
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
savepoint (transactionScope) {
|
|
26
|
+
return undefined
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default TransactionClickHouse
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dobo-clickhouse",
|
|
3
|
+
"version": "2.2.1",
|
|
4
|
+
"description": "Clickhouse driver for Dobo",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build-doc": "jsdoc -c .jsdoc.conf.json",
|
|
8
|
+
"test": "mocha"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"bajo": {
|
|
12
|
+
"type": "plugin",
|
|
13
|
+
"alias": "dbclickhouse",
|
|
14
|
+
"dependencies": ["dobo", "dobo-knex"]
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/ardhi/dobo-clickhouse.git"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"clickhouse",
|
|
22
|
+
"dobo",
|
|
23
|
+
"db",
|
|
24
|
+
"driver",
|
|
25
|
+
"bajo",
|
|
26
|
+
"framework",
|
|
27
|
+
"modular"
|
|
28
|
+
],
|
|
29
|
+
"author": "Ardhi Lukianto <ardhi@lukianto.com>",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/ardhi/dobo-clickhouse/issues"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://github.com/ardhi/dobo-clickhouse#readme",
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"clean-jsdoc-theme": "^4.3.0",
|
|
37
|
+
"jsdoc-plugin-intersection": "^1.0.4"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"clickhouse": "^2.6.0",
|
|
41
|
+
"sqlstring": "^2.3.3"
|
|
42
|
+
}
|
|
43
|
+
}
|