UNPKG

12.9 kBJavaScriptView Raw
1"use strict";
2/*
3 * @adonisjs/lucid
4 *
5 * (c) Harminder Virk <virk@adonisjs.com>
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10Object.defineProperty(exports, "__esModule", { value: true });
11exports.Database = void 0;
12/// <reference path="../../adonis-typings/index.ts" />
13const macroable_1 = require("macroable");
14const utils_1 = require("@poppinss/utils");
15const QueryClient_1 = require("../QueryClient");
16const Raw_1 = require("./StaticBuilder/Raw");
17const prettyPrint_1 = require("../Helpers/prettyPrint");
18const QueryBuilder_1 = require("../Orm/QueryBuilder");
19const Manager_1 = require("../Connection/Manager");
20const Insert_1 = require("./QueryBuilder/Insert");
21const Reference_1 = require("./StaticBuilder/Reference");
22const SimplePaginator_1 = require("./Paginator/SimplePaginator");
23const Database_1 = require("./QueryBuilder/Database");
24/**
25 * Database class exposes the API to manage multiple connections and obtain an instance
26 * of query/transaction clients.
27 */
28class Database extends macroable_1.Macroable {
29 constructor(config, logger, profiler, emitter) {
30 super();
31 Object.defineProperty(this, "config", {
32 enumerable: true,
33 configurable: true,
34 writable: true,
35 value: config
36 });
37 Object.defineProperty(this, "logger", {
38 enumerable: true,
39 configurable: true,
40 writable: true,
41 value: logger
42 });
43 Object.defineProperty(this, "profiler", {
44 enumerable: true,
45 configurable: true,
46 writable: true,
47 value: profiler
48 });
49 Object.defineProperty(this, "emitter", {
50 enumerable: true,
51 configurable: true,
52 writable: true,
53 value: emitter
54 });
55 /**
56 * Reference to self constructor. TypeScript sucks with "this.constructor"
57 * https://github.com/microsoft/TypeScript/issues/4586
58 */
59 Object.defineProperty(this, "Database", {
60 enumerable: true,
61 configurable: true,
62 writable: true,
63 value: Database
64 });
65 /**
66 * Reference to connections manager
67 */
68 Object.defineProperty(this, "manager", {
69 enumerable: true,
70 configurable: true,
71 writable: true,
72 value: void 0
73 });
74 /**
75 * Primary connection name
76 */
77 Object.defineProperty(this, "primaryConnectionName", {
78 enumerable: true,
79 configurable: true,
80 writable: true,
81 value: this.config.connection
82 });
83 /**
84 * Reference to query builders. We expose them, so that they can be
85 * extended from outside using macros.
86 */
87 Object.defineProperty(this, "DatabaseQueryBuilder", {
88 enumerable: true,
89 configurable: true,
90 writable: true,
91 value: Database_1.DatabaseQueryBuilder
92 });
93 Object.defineProperty(this, "InsertQueryBuilder", {
94 enumerable: true,
95 configurable: true,
96 writable: true,
97 value: Insert_1.InsertQueryBuilder
98 });
99 Object.defineProperty(this, "ModelQueryBuilder", {
100 enumerable: true,
101 configurable: true,
102 writable: true,
103 value: QueryBuilder_1.ModelQueryBuilder
104 });
105 Object.defineProperty(this, "SimplePaginator", {
106 enumerable: true,
107 configurable: true,
108 writable: true,
109 value: SimplePaginator_1.SimplePaginator
110 });
111 /**
112 * A store of global transactions
113 */
114 Object.defineProperty(this, "connectionGlobalTransactions", {
115 enumerable: true,
116 configurable: true,
117 writable: true,
118 value: new Map()
119 });
120 Object.defineProperty(this, "hasHealthChecksEnabled", {
121 enumerable: true,
122 configurable: true,
123 writable: true,
124 value: false
125 });
126 Object.defineProperty(this, "prettyPrint", {
127 enumerable: true,
128 configurable: true,
129 writable: true,
130 value: prettyPrint_1.prettyPrint
131 });
132 this.validateConfig();
133 this.manager = new Manager_1.ConnectionManager(this.logger, this.emitter);
134 this.registerConnections();
135 this.findIfHealthChecksAreEnabled();
136 }
137 /**
138 * Validate config at runtime
139 */
140 validateConfig() {
141 const validator = new utils_1.ManagerConfigValidator(this.config, 'database', 'config/database');
142 validator.validateDefault('connection');
143 validator.validateList('connections', 'connection');
144 }
145 /**
146 * Compute whether health check is enabled or not after registering the connections.
147 * There are chances that all pre-registered connections are not using health
148 * checks but a dynamic connection is using it. We don't support that use case
149 * for now, since it complicates things a lot and forces us to register the
150 * health checker on demand.
151 */
152 findIfHealthChecksAreEnabled() {
153 for (let [, conn] of this.manager.connections) {
154 if (conn.config.healthCheck) {
155 this.hasHealthChecksEnabled = true;
156 break;
157 }
158 }
159 }
160 /**
161 * Registering all connections with the manager, so that we can fetch
162 * and connect with them whenver required.
163 */
164 registerConnections() {
165 Object.keys(this.config.connections).forEach((name) => {
166 this.manager.add(name, this.config.connections[name]);
167 });
168 }
169 /**
170 * Returns the connection node from the connection manager
171 */
172 getRawConnection(name) {
173 return this.manager.get(name);
174 }
175 /**
176 * Returns the query client for a given connection
177 */
178 connection(connection = this.primaryConnectionName, options) {
179 options = options || {};
180 /**
181 * Use default profiler, when no profiler is defined when obtaining
182 * the query client for a given connection.
183 */
184 if (!options.profiler) {
185 options.profiler = this.profiler;
186 }
187 /**
188 * Connect is noop when already connected
189 */
190 this.manager.connect(connection);
191 /**
192 * Disallow modes other than `read` or `write`
193 */
194 if (options.mode && !['read', 'write'].includes(options.mode)) {
195 throw new utils_1.Exception(`Invalid mode ${options.mode}. Must be read or write`);
196 }
197 /**
198 * Return the global transaction when it already exists.
199 */
200 if (this.connectionGlobalTransactions.has(connection)) {
201 this.logger.trace({ connection }, 'using pre-existing global transaction connection');
202 const globalTransactionClient = this.connectionGlobalTransactions.get(connection);
203 return globalTransactionClient;
204 }
205 /**
206 * Fetching connection for the given name
207 */
208 const rawConnection = this.getRawConnection(connection).connection;
209 /**
210 * Generating query client for a given connection and setting appropriate
211 * mode on it
212 */
213 this.logger.trace({ connection }, 'creating query client in %s mode', [options.mode || 'dual']);
214 const queryClient = options.mode
215 ? new QueryClient_1.QueryClient(options.mode, rawConnection, this.emitter)
216 : new QueryClient_1.QueryClient('dual', rawConnection, this.emitter);
217 /**
218 * Passing profiler to the query client for profiling queries
219 */
220 queryClient.profiler = options.profiler;
221 return queryClient;
222 }
223 /**
224 * Returns the knex query builder
225 */
226 knexQuery() {
227 return this.connection(this.primaryConnectionName).knexQuery();
228 }
229 /**
230 * Returns the knex raw query builder
231 */
232 knexRawQuery(sql, bindings) {
233 return this.connection(this.primaryConnectionName).knexRawQuery(sql, bindings);
234 }
235 /**
236 * Returns query builder. Optionally one can define the mode as well
237 */
238 query(options) {
239 return this.connection(this.primaryConnectionName, options).query();
240 }
241 /**
242 * Returns insert query builder. Always has to be dual or write mode and
243 * hence it doesn't matter, since in both `dual` and `write` mode,
244 * the `write` connection is always used.
245 */
246 insertQuery(options) {
247 return this.connection(this.primaryConnectionName, options).insertQuery();
248 }
249 /**
250 * Returns a query builder instance for a given model.
251 */
252 modelQuery(model, options) {
253 return this.connection(this.primaryConnectionName, options).modelQuery(model);
254 }
255 /**
256 * Returns an instance of raw query builder. Optionally one can
257 * defined the `read/write` mode in which to execute the
258 * query
259 */
260 rawQuery(sql, bindings, options) {
261 return this.connection(this.primaryConnectionName, options).rawQuery(sql, bindings);
262 }
263 /**
264 * Returns an instance of raw builder. This raw builder queries
265 * cannot be executed. Use `rawQuery`, if you want to execute
266 * queries raw queries.
267 */
268 raw(sql, bindings) {
269 return new Raw_1.RawBuilder(sql, bindings);
270 }
271 /**
272 * Returns reference builder.
273 */
274 ref(reference) {
275 return new Reference_1.ReferenceBuilder(reference, this.connection().getReadClient().client);
276 }
277 /**
278 * Returns instance of a query builder and selects the table
279 */
280 from(table) {
281 return this.connection().from(table);
282 }
283 /**
284 * Returns insert query builder and selects the table
285 */
286 table(table) {
287 return this.connection().table(table);
288 }
289 /**
290 * Returns a transaction instance on the default
291 * connection
292 */
293 transaction(callback, options) {
294 const client = this.connection();
295 return typeof callback === 'function'
296 ? client.transaction(callback, options)
297 : client.transaction(callback);
298 }
299 /**
300 * Invokes `manager.report`
301 */
302 report() {
303 return this.manager.report();
304 }
305 /**
306 * Begin a new global transaction
307 */
308 async beginGlobalTransaction(connectionName, options) {
309 connectionName = connectionName || this.primaryConnectionName;
310 /**
311 * Return global transaction as it is
312 */
313 const globalTrx = this.connectionGlobalTransactions.get(connectionName);
314 if (globalTrx) {
315 return globalTrx;
316 }
317 /**
318 * Create a new transaction and store a reference to it
319 */
320 const trx = await this.connection(connectionName, options).transaction();
321 this.connectionGlobalTransactions.set(trx.connectionName, trx);
322 /**
323 * Listen for events to drop the reference when transaction
324 * is over
325 */
326 trx.on('commit', ($trx) => {
327 this.connectionGlobalTransactions.delete($trx.connectionName);
328 });
329 trx.on('rollback', ($trx) => {
330 this.connectionGlobalTransactions.delete($trx.connectionName);
331 });
332 return trx;
333 }
334 /**
335 * Commit an existing global transaction
336 */
337 async commitGlobalTransaction(connectionName) {
338 connectionName = connectionName || this.primaryConnectionName;
339 const trx = this.connectionGlobalTransactions.get(connectionName);
340 if (!trx) {
341 throw new utils_1.Exception([
342 'Cannot commit a non-existing global transaction.',
343 ' Make sure you are not calling "commitGlobalTransaction" twice',
344 ].join(''));
345 }
346 await trx.commit();
347 }
348 /**
349 * Rollback an existing global transaction
350 */
351 async rollbackGlobalTransaction(connectionName) {
352 connectionName = connectionName || this.primaryConnectionName;
353 const trx = this.connectionGlobalTransactions.get(connectionName);
354 if (!trx) {
355 throw new utils_1.Exception([
356 'Cannot rollback a non-existing global transaction.',
357 ' Make sure you are not calling "commitGlobalTransaction" twice',
358 ].join(''));
359 }
360 await trx.rollback();
361 }
362}
363exports.Database = Database;
364/**
365 * Required by macroable
366 */
367Object.defineProperty(Database, "macros", {
368 enumerable: true,
369 configurable: true,
370 writable: true,
371 value: {}
372});
373Object.defineProperty(Database, "getters", {
374 enumerable: true,
375 configurable: true,
376 writable: true,
377 value: {}
378});