| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398 | 
							- /*
 
-  * Copyright (c) 2015-present, Vitaly Tomilov
 
-  *
 
-  * See the LICENSE file at the top-level directory of this distribution
 
-  * for licensing information.
 
-  *
 
-  * Removal or modification of this copyright notice is prohibited.
 
-  */
 
- const {InnerState} = require('./inner-state');
 
- const {QueryFileError} = require('./errors');
 
- const {assert} = require('./assert');
 
- const {ColorConsole} = require('./utils/color');
 
- const npm = {
 
-     fs: require('fs'),
 
-     os: require('os'),
 
-     path: require('path'),
 
-     minify: require('pg-minify'),
 
-     utils: require('./utils'),
 
-     formatting: require('./formatting')
 
- };
 
- const file$query = Symbol('QueryFile.query');
 
- /**
 
-  * @class QueryFile
 
-  * @description
 
-  *
 
-  * Represents an external SQL file. The type is available from the library's root: `pgp.QueryFile`.
 
-  *
 
-  * Reads a file with SQL and prepares it for execution, also parses and minifies it, if required.
 
-  * The SQL can be of any complexity, with both single and multi-line comments.
 
-  *
 
-  * The type can be used in place of the `query` parameter, with any query method directly, plus as `text` in {@link PreparedStatement}
 
-  * and {@link ParameterizedQuery}.
 
-  *
 
-  * It never throws any error, leaving it for query methods to reject with {@link errors.QueryFileError QueryFileError}.
 
-  *
 
-  * **IMPORTANT:** You should only create a single reusable object per file, in order to avoid repeated file reads,
 
-  * as the IO is a very expensive resource. If you do not follow it, you will be seeing the following warning:
 
-  * `Creating a duplicate QueryFile object for the same file`, which signals a bad-use pattern.
 
-  *
 
-  * @param {string} file
 
-  * Path to the SQL file with the query, either absolute or relative to the application's entry point file.
 
-  *
 
-  * If there is any problem reading the file, it will be reported when executing the query.
 
-  *
 
-  * @param {QueryFile.Options} [options]
 
-  * Set of configuration options, as documented by {@link QueryFile.Options}.
 
-  *
 
-  * @returns {QueryFile}
 
-  *
 
-  * @see
 
-  * {@link errors.QueryFileError QueryFileError},
 
-  * {@link QueryFile#toPostgres toPostgres}
 
-  *
 
-  * @example
 
-  * // File sql.js
 
-  *
 
-  * // Proper way to organize an sql provider:
 
-  * //
 
-  * // - have all sql files for Users in ./sql/users
 
-  * // - have all sql files for Products in ./sql/products
 
-  * // - have your sql provider module as ./sql/index.js
 
-  *
 
-  * const {QueryFile} = require('pg-promise');
 
-  * const {join: joinPath} = require('path');
 
-  *
 
-  * // Helper for linking to external query files:
 
-  * function sql(file) {
 
-  *     const fullPath = joinPath(__dirname, file); // generating full path;
 
-  *     return new QueryFile(fullPath, {minify: true});
 
-  * }
 
-  *
 
-  * module.exports = {
 
-  *     // external queries for Users:
 
-  *     users: {
 
-  *         add: sql('users/create.sql'),
 
-  *         search: sql('users/search.sql'),
 
-  *         report: sql('users/report.sql'),
 
-  *     },
 
-  *     // external queries for Products:
 
-  *     products: {
 
-  *         add: sql('products/add.sql'),
 
-  *         quote: sql('products/quote.sql'),
 
-  *         search: sql('products/search.sql'),
 
-  *     }
 
-  * };
 
-  *
 
-  * @example
 
-  * // Testing our SQL provider
 
-  *
 
-  * const db = require('./db'); // our database module;
 
-  * const {users: sql} = require('./sql'); // sql for users;
 
-  *
 
-  * module.exports = {
 
-  *     addUser: (name, age) => db.none(sql.add, [name, age]),
 
-  *     findUser: name => db.any(sql.search, name)
 
-  * };
 
-  *
 
-  */
 
- class QueryFile extends InnerState {
 
-     constructor(file, options) {
 
-         let filePath = file;
 
-         options = assert(options, {
 
-             debug: npm.utils.isDev(),
 
-             minify: (options && options.compress && options.minify === undefined) ? true : undefined,
 
-             compress: undefined,
 
-             params: undefined,
 
-             noWarnings: undefined
 
-         });
 
-         if (npm.utils.isText(filePath) && !npm.path.isAbsolute(filePath)) {
 
-             filePath = npm.path.join(npm.utils.startDir, filePath);
 
-         }
 
-         const {usedPath} = QueryFile.instance;
 
-         // istanbul ignore next:
 
-         if (!options.noWarnings) {
 
-             if (filePath in usedPath) {
 
-                 usedPath[filePath]++;
 
-                 ColorConsole.warn(`WARNING: Creating a duplicate QueryFile object for the same file - \n    ${filePath}\n${npm.utils.getLocalStack(2, 3)}\n`);
 
-             } else {
 
-                 usedPath[filePath] = 0;
 
-             }
 
-         }
 
-         const _inner = {
 
-             file,
 
-             filePath,
 
-             options,
 
-             sql: undefined,
 
-             error: undefined,
 
-             ready: undefined,
 
-             modTime: undefined
 
-         };
 
-         super(_inner);
 
-         this.prepare();
 
-     }
 
-     /**
 
-      * Global instance of the file-path repository.
 
-      *
 
-      * @return {{usedPath: {}}}
 
-      */
 
-     static get instance() {
 
-         const s = Symbol.for('pgPromiseQueryFile');
 
-         let scope = global[s];
 
-         if (!scope) {
 
-             scope = {
 
-                 usedPath: {} // used-path look-up dictionary
 
-             };
 
-             global[s] = scope;
 
-         }
 
-         return scope;
 
-     }
 
-     /**
 
-      * @name QueryFile#Symbol(QueryFile.$query)
 
-      * @type {string}
 
-      * @default undefined
 
-      * @readonly
 
-      * @private
 
-      * @summary Prepared query string.
 
-      * @description
 
-      * When property {@link QueryFile#error error} is set, the query is `undefined`.
 
-      *
 
-      * **IMPORTANT:** This property is for internal use by the library only, never use this
 
-      * property directly from your code.
 
-      */
 
-     get [file$query]() {
 
-         return this._inner.sql;
 
-     }
 
-     /**
 
-      * @name QueryFile#error
 
-      * @type {errors.QueryFileError}
 
-      * @default undefined
 
-      * @readonly
 
-      * @description
 
-      * When in an error state, it is set to a {@link errors.QueryFileError QueryFileError} object. Otherwise, it is `undefined`.
 
-      */
 
-     get error() {
 
-         return this._inner.error;
 
-     }
 
-     /**
 
-      * @name QueryFile#file
 
-      * @type {string}
 
-      * @readonly
 
-      * @description
 
-      * File name that was passed into the constructor.
 
-      *
 
-      * This property is primarily for internal use by the library.
 
-      */
 
-     get file() {
 
-         return this._inner.file;
 
-     }
 
-     /**
 
-      * @name QueryFile#options
 
-      * @type {QueryFile.Options}
 
-      * @readonly
 
-      * @description
 
-      * Set of options, as configured during the object's construction.
 
-      *
 
-      * This property is primarily for internal use by the library.
 
-      */
 
-     get options() {
 
-         return this._inner.options;
 
-     }
 
-     /**
 
-      * @summary Prepares the query for execution.
 
-      * @description
 
-      * If the query hasn't been prepared yet, it will read the file and process the content according
 
-      * to the parameters passed into the constructor.
 
-      *
 
-      * This method is primarily for internal use by the library.
 
-      *
 
-      * @param {boolean} [throwErrors=false]
 
-      * Throw any error encountered.
 
-      */
 
-     prepare(throwErrors) {
 
-         const i = this._inner, options = i.options;
 
-         let lastMod;
 
-         if (options.debug && i.ready) {
 
-             try {
 
-                 lastMod = npm.fs.statSync(i.filePath).mtime.getTime();
 
-                 // istanbul ignore if;
 
-                 if (lastMod === i.modTime) {
 
-                     return;
 
-                 }
 
-                 i.ready = false;
 
-             } catch (e) {
 
-                 i.sql = undefined;
 
-                 i.ready = false;
 
-                 i.error = e;
 
-                 if (throwErrors) {
 
-                     throw i.error;
 
-                 }
 
-                 return;
 
-             }
 
-         }
 
-         if (i.ready) {
 
-             return;
 
-         }
 
-         try {
 
-             i.sql = npm.fs.readFileSync(i.filePath, 'utf8');
 
-             i.modTime = lastMod || npm.fs.statSync(i.filePath).mtime.getTime();
 
-             if (options.minify && options.minify !== 'after') {
 
-                 i.sql = npm.minify(i.sql, {compress: options.compress});
 
-             }
 
-             if (options.params !== undefined) {
 
-                 i.sql = npm.formatting.as.format(i.sql, options.params, {partial: true});
 
-             }
 
-             if (options.minify && options.minify === 'after') {
 
-                 i.sql = npm.minify(i.sql, {compress: options.compress});
 
-             }
 
-             i.ready = true;
 
-             i.error = undefined;
 
-         } catch (e) {
 
-             i.sql = undefined;
 
-             i.error = new QueryFileError(e, this);
 
-             if (throwErrors) {
 
-                 throw i.error;
 
-             }
 
-         }
 
-     }
 
- }
 
- // Hiding the query as a symbol within the type,
 
- // to make it even more difficult to misuse it:
 
- QueryFile.$query = file$query;
 
- /**
 
-  * @method QueryFile#toPostgres
 
-  * @description
 
-  * $[Custom Type Formatting], based on $[Symbolic CTF], i.e. the actual method is available only via {@link external:Symbol Symbol}:
 
-  *
 
-  * ```js
 
-  * const ctf = pgp.as.ctf; // Custom Type Formatting symbols namespace
 
-  * const query = qf[ctf.toPostgres](); // qf = an object of type QueryFile
 
-  * ```
 
-  *
 
-  * This is a raw formatting type (`rawType = true`), i.e. when used as a query-formatting parameter, type `QueryFile` injects SQL as raw text.
 
-  *
 
-  * If you need to support type `QueryFile` outside of query methods, this is the only safe way to get the most current SQL.
 
-  * And you would want to use this method dynamically, as it reloads the SQL automatically, if option `debug` is set.
 
-  * See {@link QueryFile.Options Options}.
 
-  *
 
-  * @param {QueryFile} [self]
 
-  * Optional self-reference, for ES6 arrow functions.
 
-  *
 
-  * @returns {string}
 
-  * SQL string from the file, according to the {@link QueryFile.Options options} specified.
 
-  *
 
-  */
 
- QueryFile.prototype[npm.formatting.as.ctf.toPostgres] = function (self) {
 
-     self = this instanceof QueryFile && this || self;
 
-     self.prepare(true);
 
-     return self[QueryFile.$query];
 
- };
 
- QueryFile.prototype[npm.formatting.as.ctf.rawType] = true; // use as pre-formatted
 
- /**
 
-  * @method QueryFile#toString
 
-  * @description
 
-  * Creates a well-formatted multi-line string that represents the object's current state.
 
-  *
 
-  * It is called automatically when writing the object into the console.
 
-  *
 
-  * @param {number} [level=0]
 
-  * Nested output level, to provide visual offset.
 
-  *
 
-  * @returns {string}
 
-  */
 
- QueryFile.prototype.toString = function (level) {
 
-     level = level > 0 ? parseInt(level) : 0;
 
-     const gap = npm.utils.messageGap(level + 1);
 
-     const lines = [
 
-         'QueryFile {'
 
-     ];
 
-     this.prepare();
 
-     lines.push(gap + 'file: "' + this.file + '"');
 
-     lines.push(gap + 'options: ' + npm.utils.toJson(this.options));
 
-     if (this.error) {
 
-         lines.push(gap + 'error: ' + this.error.toString(level + 1));
 
-     } else {
 
-         lines.push(gap + 'query: "' + this[QueryFile.$query] + '"');
 
-     }
 
-     lines.push(npm.utils.messageGap(level) + '}');
 
-     return lines.join(npm.os.EOL);
 
- };
 
- npm.utils.addInspection(QueryFile, function () {
 
-     return this.toString();
 
- });
 
- module.exports = {QueryFile};
 
- /**
 
-  * @typedef QueryFile.Options
 
-  * @description
 
-  * A set of configuration options as passed into the {@link QueryFile} constructor.
 
-  *
 
-  * @property {boolean} debug
 
-  * When in debug mode, the query file is checked for its last modification time on every query request,
 
-  * so if it changes, the file is read afresh.
 
-  *
 
-  * The default for this property is `true` when `NODE_ENV` = `development`,
 
-  * or `false` otherwise.
 
-  *
 
-  * @property {boolean|string} minify=false
 
-  * Parses and minifies the SQL using $[pg-minify]:
 
-  * - `false` - do not use $[pg-minify]
 
-  * - `true` - use $[pg-minify] to parse and minify SQL
 
-  * - `'after'` - use $[pg-minify] after applying static formatting parameters
 
-  *   (option `params`), as opposed to before it (default)
 
-  *
 
-  * If option `compress` is set, then the default for `minify` is `true`.
 
-  *
 
-  * Failure to parse SQL will result in $[SQLParsingError].
 
-  *
 
-  * @property {boolean} compress=false
 
-  * Sets option `compress` as supported by $[pg-minify], to uglify the SQL:
 
-  * - `false` - no compression to be applied, keep minimum spaces for easier read
 
-  * - `true` - remove all unnecessary spaces from SQL
 
-  *
 
-  * This option has no meaning, if `minify` is explicitly set to `false`. However, if `minify` is not
 
-  * specified and `compress` is specified as `true`, then `minify` defaults to `true`.
 
-  *
 
-  * @property {array|object|value} params
 
-  *
 
-  * Static formatting parameters to be applied to the SQL, using the same method {@link formatting.format as.format},
 
-  * but with option `partial` = `true`.
 
-  *
 
-  * Most of the time query formatting is fully dynamic, and applied just before executing the query.
 
-  * In some cases though you may need to pre-format SQL with static values. Examples of it can be a
 
-  * schema name, or a configurable table name.
 
-  *
 
-  * This option makes two-step SQL formatting easy: you can pre-format the SQL initially, and then
 
-  * apply the second-step dynamic formatting when executing the query.
 
-  *
 
-  * @property {boolean} noWarnings=false
 
-  * Suppresses all warnings produced by the class. It is not recommended for general use, only in specific tests
 
-  * that may require it.
 
-  *
 
-  */
 
 
  |