"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
  for (var prop in b || (b = {}))
    if (__hasOwnProp.call(b, prop))
      __defNormalProp(a, prop, b[prop]);
  if (__getOwnPropSymbols)
    for (var prop of __getOwnPropSymbols(b)) {
      if (__propIsEnum.call(b, prop))
        __defNormalProp(a, prop, b[prop]);
    }
  return a;
};
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true });
var __export = (target, all) => {
  __markAsModule(target);
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
__export(exports, {
  OracleQueryGenerator: () => OracleQueryGenerator
});
const Utils = require("../../utils");
const DataTypes = require("../../data-types");
const AbstractQueryGenerator = require("../abstract/query-generator");
const _ = require("lodash");
const util = require("util");
const Transaction = require("../../transaction");
const ORACLE_RESERVED_WORDS = ["ACCESS", "ADD", "ALL", "ALTER", "AND", "ANY", "ARRAYLEN", "AS", "ASC", "AUDIT", "BETWEEN", "BY", "CHAR", "CHECK", "CLUSTER", "COLUMN", "COMMENT", "COMPRESS", "CONNECT", "CREATE", "CURRENT", "DATE", "DECIMAL", "DEFAULT", "DELETE", "DESC", "DISTINCT", "DROP", "ELSE", "EXCLUSIVE", "EXISTS", "FILE", "FLOAT", "FOR", "FROM", "GRANT", "GROUP", "HAVING", "IDENTIFIED", "IMMEDIATE", "IN", "INCREMENT", "INDEX", "INITIAL", "INSERT", "INTEGER", "INTERSECT", "INTO", "IS", "LEVEL", "LIKE", "LOCK", "LONG", "MAXEXTENTS", "MINUS", "MODE", "MODIFY", "NOAUDIT", "NOCOMPRESS", "NOT", "NOTFOUND", "NOWAIT", "NULL", "NUMBER", "OF", "OFFLINE", "ON", "ONLINE", "OPTION", "OR", "ORDER", "PCTFREE", "PRIOR", "PRIVILEGES", "PUBLIC", "RAW", "RENAME", "RESOURCE", "REVOKE", "ROW", "ROWID", "ROWLABEL", "ROWNUM", "ROWS", "SELECT", "SESSION", "SET", "SHARE", "SIZE", "SMALLINT", "SQLBUF", "START", "SUCCESSFUL", "SYNONYM", "SYSDATE", "TABLE", "THEN", "TO", "TRIGGER", "UID", "UNION", "UNIQUE", "UPDATE", "USER", "VALIDATE", "VALUES", "VARCHAR", "VARCHAR2", "VIEW", "WHENEVER", "WHERE", "WITH"];
const JSON_FUNCTION_REGEX = /^\s*((?:[a-z]+_){0,2}jsonb?(?:_[a-z]+){0,2})\([^)]*\)/i;
const JSON_OPERATOR_REGEX = /^\s*(->>?|@>|<@|\?[|&]?|\|{2}|#-)/i;
const TOKEN_CAPTURE_REGEX = /^\s*((?:([`"'])(?:(?!\2).|\2{2})*\2)|[\w\d\s]+|[().,;+-])/i;
class OracleQueryGenerator extends AbstractQueryGenerator {
  constructor(options) {
    super(options);
  }
  getCatalogName(value) {
    if (value) {
      if (this.options.quoteIdentifiers === false) {
        const quotedValue = this.quoteIdentifier(value);
        if (quotedValue === value) {
          value = value.toUpperCase();
        }
      }
    }
    return value;
  }
  getSchemaNameAndTableName(table) {
    const tableName = this.getCatalogName(table.tableName || table);
    const schemaName = this.getCatalogName(table.schema);
    return [tableName, schemaName];
  }
  createSchema(schema) {
    const quotedSchema = this.quoteIdentifier(schema);
    return [
      "DECLARE",
      "USER_FOUND BOOLEAN := FALSE;",
      "BEGIN",
      " BEGIN",
      "   EXECUTE IMMEDIATE ",
      this.escape(`CREATE USER ${quotedSchema} IDENTIFIED BY 12345 DEFAULT TABLESPACE USERS`),
      ";",
      "   EXCEPTION WHEN OTHERS THEN",
      "     IF SQLCODE != -1920 THEN",
      "       RAISE;",
      "     ELSE",
      "       USER_FOUND := TRUE;",
      "     END IF;",
      " END;",
      " IF NOT USER_FOUND THEN",
      "    EXECUTE IMMEDIATE ",
      this.escape(`GRANT "CONNECT" TO ${quotedSchema}`),
      ";",
      "    EXECUTE IMMEDIATE ",
      this.escape(`GRANT CREATE TABLE TO ${quotedSchema}`),
      ";",
      "    EXECUTE IMMEDIATE ",
      this.escape(`GRANT CREATE VIEW TO ${quotedSchema}`),
      ";",
      "    EXECUTE IMMEDIATE ",
      this.escape(`GRANT CREATE ANY TRIGGER TO ${quotedSchema}`),
      ";",
      "    EXECUTE IMMEDIATE ",
      this.escape(`GRANT CREATE ANY PROCEDURE TO ${quotedSchema}`),
      ";",
      "    EXECUTE IMMEDIATE ",
      this.escape(`GRANT CREATE SEQUENCE TO ${quotedSchema}`),
      ";",
      "    EXECUTE IMMEDIATE ",
      this.escape(`GRANT CREATE SYNONYM TO ${quotedSchema}`),
      ";",
      "    EXECUTE IMMEDIATE ",
      this.escape(`ALTER USER ${quotedSchema} QUOTA UNLIMITED ON USERS`),
      ";",
      " END IF;",
      "END;"
    ].join(" ");
  }
  showSchemasQuery() {
    return `SELECT USERNAME AS "schema_name" FROM ALL_USERS WHERE COMMON = ('NO') AND USERNAME != user`;
  }
  dropSchema(schema) {
    return [
      "BEGIN",
      "EXECUTE IMMEDIATE ",
      this.escape(`DROP USER ${this.quoteTable(schema)} CASCADE`),
      ";",
      "EXCEPTION WHEN OTHERS THEN",
      "  IF SQLCODE != -1918 THEN",
      "    RAISE;",
      "  END IF;",
      "END;"
    ].join(" ");
  }
  versionQuery() {
    return "SELECT VERSION_FULL FROM PRODUCT_COMPONENT_VERSION WHERE PRODUCT LIKE 'Oracle%'";
  }
  createTableQuery(tableName, attributes, options) {
    const primaryKeys = [], foreignKeys = Object.create(null), attrStr = [], checkStr = [];
    const values = {
      table: this.quoteTable(tableName)
    };
    for (let attr in attributes) {
      if (!Object.prototype.hasOwnProperty.call(attributes, attr))
        continue;
      const dataType = attributes[attr];
      attr = this.quoteIdentifier(attr);
      if (dataType.includes("PRIMARY KEY")) {
        primaryKeys.push(attr);
        if (dataType.includes("REFERENCES")) {
          const match = dataType.match(/^(.+) (REFERENCES.*)$/);
          attrStr.push(`${attr} ${match[1].replace(/PRIMARY KEY/, "")}`);
          foreignKeys[attr] = match[2];
        } else {
          attrStr.push(`${attr} ${dataType.replace(/PRIMARY KEY/, "").trim()}`);
        }
      } else if (dataType.includes("REFERENCES")) {
        const match = dataType.match(/^(.+) (REFERENCES.*)$/);
        attrStr.push(`${attr} ${match[1]}`);
        foreignKeys[attr] = match[2];
      } else {
        attrStr.push(`${attr} ${dataType}`);
      }
    }
    values["attributes"] = attrStr.join(", ");
    const pkString = primaryKeys.map((pk) => this.quoteIdentifier(pk)).join(", ");
    if (pkString.length > 0) {
      values.attributes += `,PRIMARY KEY (${pkString})`;
    }
    for (const fkey in foreignKeys) {
      if (!Object.prototype.hasOwnProperty.call(foreignKeys, fkey))
        continue;
      if (foreignKeys[fkey].indexOf("ON DELETE NO ACTION") > -1) {
        foreignKeys[fkey] = foreignKeys[fkey].replace("ON DELETE NO ACTION", "");
      }
      values.attributes += `,FOREIGN KEY (${this.quoteIdentifier(fkey)}) ${foreignKeys[fkey]}`;
    }
    if (checkStr.length > 0) {
      values.attributes += `, ${checkStr.join(", ")}`;
    }
    if (options && options.indexes && options.indexes.length > 0) {
      const idxToDelete = [];
      options.indexes.forEach((index, idx) => {
        if ("unique" in index && (index.unique === true || index.unique.length > 0 && index.unique !== false)) {
          const fields = index.fields.map((field) => {
            if (typeof field === "string") {
              return field;
            }
            return field.attribute;
          });
          let canContinue = true;
          if (options.uniqueKeys) {
            const keys = Object.keys(options.uniqueKeys);
            for (let fieldIdx = 0; fieldIdx < keys.length; fieldIdx++) {
              const currUnique = options.uniqueKeys[keys[fieldIdx]];
              if (currUnique.fields.length === fields.length) {
                for (let i = 0; i < currUnique.fields.length; i++) {
                  const field = currUnique.fields[i];
                  if (_.includes(fields, field)) {
                    canContinue = false;
                  } else {
                    canContinue = true;
                    break;
                  }
                }
              }
            }
            if (canContinue) {
              const indexName = "name" in index ? index.name : "";
              const constraintToAdd = {
                name: indexName,
                fields
              };
              if (!("uniqueKeys" in options)) {
                options.uniqueKeys = {};
              }
              options.uniqueKeys[indexName] = constraintToAdd;
              idxToDelete.push(idx);
            } else {
              idxToDelete.push(idx);
            }
          }
        }
      });
      idxToDelete.forEach((idx) => {
        options.indexes.splice(idx, 1);
      });
    }
    if (options && !!options.uniqueKeys) {
      _.each(options.uniqueKeys, (columns, indexName) => {
        let canBeUniq = false;
        primaryKeys.forEach((primaryKey) => {
          primaryKey = primaryKey.replace(/"/g, "");
          if (!_.includes(columns.fields, primaryKey)) {
            canBeUniq = true;
          }
        });
        columns.fields.forEach((field) => {
          let currField = "";
          if (!_.isString(field)) {
            currField = field.attribute.replace(/[.,"\s]/g, "");
          } else {
            currField = field.replace(/[.,"\s]/g, "");
          }
          if (currField in attributes) {
            if (attributes[currField].toUpperCase().indexOf("UNIQUE") > -1 && canBeUniq) {
              const attrToReplace = attributes[currField].replace("UNIQUE", "");
              values.attributes = values.attributes.replace(attributes[currField], attrToReplace);
            }
          }
        });
        if (canBeUniq) {
          const index = options.uniqueKeys[columns.name];
          delete options.uniqueKeys[columns.name];
          indexName = indexName.replace(/[.,\s]/g, "");
          columns.name = indexName;
          options.uniqueKeys[indexName] = index;
          if (indexName.length === 0) {
            values.attributes += `,UNIQUE (${columns.fields.map((field) => this.quoteIdentifier(field)).join(", ")})`;
          } else {
            values.attributes += `, CONSTRAINT ${this.quoteIdentifier(indexName)} UNIQUE (${columns.fields.map((field) => this.quoteIdentifier(field)).join(", ")})`;
          }
        }
      });
    }
    const query = Utils.joinSQLFragments([
      "CREATE TABLE",
      values.table,
      `(${values.attributes})`
    ]);
    return Utils.joinSQLFragments([
      "BEGIN",
      "EXECUTE IMMEDIATE",
      `${this.escape(query)};`,
      "EXCEPTION WHEN OTHERS THEN",
      "IF SQLCODE != -955 THEN",
      "RAISE;",
      "END IF;",
      "END;"
    ]);
  }
  tableExistsQuery(table) {
    const [tableName, schemaName] = this.getSchemaNameAndTableName(table);
    return `SELECT TABLE_NAME FROM ALL_TABLES WHERE TABLE_NAME = ${this.escape(tableName)} AND OWNER = ${table.schema ? this.escape(schemaName) : "USER"}`;
  }
  describeTableQuery(tableName, schema) {
    const currTableName = this.getCatalogName(tableName.tableName || tableName);
    schema = this.getCatalogName(schema);
    return [
      "SELECT atc.COLUMN_NAME, atc.DATA_TYPE, atc.DATA_LENGTH, atc.CHAR_LENGTH, atc.DEFAULT_LENGTH, atc.NULLABLE, ucc.constraint_type ",
      "FROM all_tab_columns atc ",
      "LEFT OUTER JOIN ",
      "(SELECT acc.column_name, acc.table_name, ac.constraint_type FROM all_cons_columns acc INNER JOIN all_constraints ac ON acc.constraint_name = ac.constraint_name) ucc ",
      "ON (atc.table_name = ucc.table_name AND atc.COLUMN_NAME = ucc.COLUMN_NAME) ",
      schema ? `WHERE (atc.OWNER = ${this.escape(schema)}) ` : "WHERE atc.OWNER = USER ",
      `AND (atc.TABLE_NAME = ${this.escape(currTableName)})`,
      "ORDER BY atc.COLUMN_NAME, CONSTRAINT_TYPE DESC"
    ].join("");
  }
  renameTableQuery(before, after) {
    return Utils.joinSQLFragments([
      "ALTER TABLE",
      this.quoteTable(before),
      "RENAME TO",
      this.quoteTable(after)
    ]);
  }
  showConstraintsQuery(table) {
    const tableName = this.getCatalogName(table.tableName || table);
    return `SELECT CONSTRAINT_NAME constraint_name FROM user_cons_columns WHERE table_name = ${this.escape(tableName)}`;
  }
  showTablesQuery() {
    return `SELECT owner as table_schema, table_name, 0 as lvl FROM all_tables where OWNER IN(SELECT USERNAME AS "schema_name" FROM ALL_USERS WHERE ORACLE_MAINTAINED = 'N')`;
  }
  dropTableQuery(tableName) {
    return Utils.joinSQLFragments([
      "BEGIN ",
      "EXECUTE IMMEDIATE 'DROP TABLE",
      this.quoteTable(tableName),
      "CASCADE CONSTRAINTS PURGE';",
      "EXCEPTION WHEN OTHERS THEN",
      " IF SQLCODE != -942 THEN",
      "   RAISE;",
      " END IF;",
      "END;"
    ]);
  }
  addIndexQuery(tableName, attributes, options, rawTablename) {
    if (typeof tableName !== "string" && attributes.name) {
      attributes.name = `${tableName.schema}.${attributes.name}`;
    }
    return super.addIndexQuery(tableName, attributes, options, rawTablename);
  }
  addConstraintQuery(tableName, options) {
    options = options || {};
    if (options.onUpdate) {
      delete options.onUpdate;
    }
    if (options.onDelete && options.onDelete.toUpperCase() === "NO ACTION") {
      delete options.onDelete;
    }
    const constraintSnippet = this.getConstraintSnippet(tableName, options);
    tableName = this.quoteTable(tableName);
    return `ALTER TABLE ${tableName} ADD ${constraintSnippet};`;
  }
  addColumnQuery(table, key, dataType) {
    dataType.field = key;
    const attribute = Utils.joinSQLFragments([
      this.quoteIdentifier(key),
      this.attributeToSQL(dataType, {
        attributeName: key,
        context: "addColumn"
      })
    ]);
    return Utils.joinSQLFragments([
      "ALTER TABLE",
      this.quoteTable(table),
      "ADD",
      attribute
    ]);
  }
  removeColumnQuery(tableName, attributeName) {
    return Utils.joinSQLFragments([
      "ALTER TABLE",
      this.quoteTable(tableName),
      "DROP COLUMN",
      this.quoteIdentifier(attributeName),
      ";"
    ]);
  }
  _alterForeignKeyConstraint(definition, table, attributeName) {
    const [tableName, schemaName] = this.getSchemaNameAndTableName(table);
    const attributeNameConstant = this.escape(this.getCatalogName(attributeName));
    const schemaNameConstant = table.schema ? this.escape(this.getCatalogName(schemaName)) : "USER";
    const tableNameConstant = this.escape(this.getCatalogName(tableName));
    const getConsNameQuery = [
      "SELECT constraint_name INTO cons_name",
      "FROM (",
      "  SELECT DISTINCT cc.owner, cc.table_name, cc.constraint_name, cc.column_name AS cons_columns",
      "  FROM all_cons_columns cc, all_constraints c",
      "  WHERE cc.owner = c.owner",
      "  AND cc.table_name = c.table_name",
      "  AND cc.constraint_name = c.constraint_name",
      "  AND c.constraint_type = 'R'",
      "  GROUP BY cc.owner, cc.table_name, cc.constraint_name, cc.column_name",
      ")",
      "WHERE owner =",
      schemaNameConstant,
      "AND table_name =",
      tableNameConstant,
      "AND cons_columns =",
      attributeNameConstant,
      ";"
    ].join(" ");
    const secondQuery = Utils.joinSQLFragments([
      `ALTER TABLE ${this.quoteIdentifier(tableName)}`,
      "ADD FOREIGN KEY",
      `(${this.quoteIdentifier(attributeName)})`,
      definition.replace(/.+?(?=REFERENCES)/, "")
    ]);
    return [
      "BEGIN",
      getConsNameQuery,
      "EXCEPTION",
      "WHEN NO_DATA_FOUND THEN",
      " CONS_NAME := NULL;",
      "END;",
      "IF CONS_NAME IS NOT NULL THEN",
      ` EXECUTE IMMEDIATE 'ALTER TABLE ${this.quoteTable(table)} DROP CONSTRAINT "'||CONS_NAME||'"';`,
      "END IF;",
      `EXECUTE IMMEDIATE ${this.escape(secondQuery)};`
    ].join(" ");
  }
  _modifyQuery(definition, table, attributeName) {
    definition = definition.startsWith("BLOB") ? definition.replace("BLOB ", "") : definition;
    const query = Utils.joinSQLFragments([
      "ALTER TABLE",
      this.quoteTable(table),
      "MODIFY",
      this.quoteIdentifier(attributeName),
      definition
    ]);
    const secondQuery = query.replace("NOT NULL", "").replace("NULL", "");
    return [
      "BEGIN",
      `EXECUTE IMMEDIATE ${this.escape(query)};`,
      "EXCEPTION",
      "WHEN OTHERS THEN",
      " IF SQLCODE = -1442 OR SQLCODE = -1451 THEN",
      `   EXECUTE IMMEDIATE ${this.escape(secondQuery)};`,
      " ELSE",
      "   RAISE;",
      " END IF;",
      "END;"
    ].join(" ");
  }
  changeColumnQuery(table, attributes) {
    const sql = [
      "DECLARE",
      "CONS_NAME VARCHAR2(200);",
      "BEGIN"
    ];
    for (const attributeName in attributes) {
      if (!Object.prototype.hasOwnProperty.call(attributes, attributeName))
        continue;
      const definition = attributes[attributeName];
      if (definition.match(/REFERENCES/)) {
        sql.push(this._alterForeignKeyConstraint(definition, table, attributeName));
      } else {
        sql.push(this._modifyQuery(definition, table, attributeName));
      }
    }
    sql.push("END;");
    return sql.join(" ");
  }
  renameColumnQuery(tableName, attrBefore, attributes) {
    const newName = Object.keys(attributes)[0];
    return `ALTER TABLE ${this.quoteTable(tableName)} RENAME COLUMN ${this.quoteIdentifier(attrBefore)} TO ${this.quoteIdentifier(newName)}`;
  }
  populateInsertQueryReturnIntoBinds(returningModelAttributes, returnTypes, inbindLength, returnAttributes, options) {
    const oracledb = this.sequelize.connectionManager.lib;
    const outBindAttributes = Object.create(null);
    const outbind = [];
    const outbindParam = this.bindParam(outbind, inbindLength);
    returningModelAttributes.forEach((element, index) => {
      if (element.startsWith('"')) {
        element = element.substring(1, element.length - 1);
      }
      outBindAttributes[element] = Object.assign(returnTypes[index]._getBindDef(oracledb), { dir: oracledb.BIND_OUT });
      const returnAttribute = `${this.format(void 0, void 0, { context: "INSERT" }, outbindParam)}`;
      returnAttributes.push(returnAttribute);
    });
    options.outBindAttributes = outBindAttributes;
  }
  upsertQuery(tableName, insertValues, updateValues, where, model, options) {
    const rawAttributes = model.rawAttributes;
    const updateQuery = this.updateQuery(tableName, updateValues, where, options, rawAttributes);
    options.bind = updateQuery.bind;
    const insertQuery = this.insertQuery(tableName, insertValues, rawAttributes, options);
    const sql = [
      "DECLARE ",
      "BEGIN ",
      updateQuery.query ? [
        updateQuery.query,
        "; ",
        " IF ( SQL%ROWCOUNT = 0 ) THEN ",
        insertQuery.query,
        " :isUpdate := 0; ",
        "ELSE ",
        " :isUpdate := 1; ",
        " END IF; "
      ].join("") : [
        insertQuery.query,
        " :isUpdate := 0; ",
        "EXCEPTION WHEN OTHERS THEN",
        " IF SQLCODE != -1 THEN",
        "   RAISE;",
        " END IF;"
      ].join(""),
      "END;"
    ];
    const query = sql.join("");
    const result = { query };
    if (options.bindParam !== false) {
      result.bind = updateQuery.bind || insertQuery.bind;
    }
    return result;
  }
  bulkInsertQuery(tableName, fieldValueHashes, options, fieldMappedAttributes) {
    options = options || {};
    options.executeMany = true;
    fieldMappedAttributes = fieldMappedAttributes || {};
    const tuples = [];
    const allColumns = {};
    const inBindBindDefMap = {};
    const outBindBindDefMap = {};
    const oracledb = this.sequelize.connectionManager.lib;
    for (const fieldValueHash of fieldValueHashes) {
      _.forOwn(fieldValueHash, (value, key) => {
        allColumns[key] = fieldMappedAttributes[key] && fieldMappedAttributes[key].autoIncrement === true && value === null;
      });
    }
    let inBindPosition;
    for (const fieldValueHash of fieldValueHashes) {
      const tuple = [];
      const inbindParam = options.bindParam === void 0 ? this.bindParam(tuple) : options.bindParam;
      const tempBindPositions = Object.keys(allColumns).map((key) => {
        if (allColumns[key] === true) {
          if (fieldValueHash[key] !== null) {
            throw Error("For an auto-increment column either all row must be null or non-null, a mix of null and non-null is not allowed!");
          }
          return "DEFAULT";
        }
        return this.format(fieldValueHash[key], fieldMappedAttributes[key], { context: "INSERT" }, inbindParam);
      });
      if (!inBindPosition) {
        inBindPosition = tempBindPositions;
      }
      tuples.push(tuple);
    }
    const returnColumn = [];
    const returnColumnBindPositions = [];
    const insertColumns = [];
    for (const key of Object.keys(allColumns)) {
      if (fieldMappedAttributes[key]) {
        const bindDef = fieldMappedAttributes[key].type._getBindDef(oracledb);
        if (allColumns[key]) {
          bindDef.dir = oracledb.BIND_OUT;
          outBindBindDefMap[key] = bindDef;
          returnColumn.push(this.quoteIdentifier(key));
          returnColumnBindPositions.push(`:${tuples[0].length + returnColumn.length}`);
        } else {
          bindDef.dir = oracledb.BIND_IN;
          inBindBindDefMap[key] = bindDef;
        }
      }
      insertColumns.push(this.quoteIdentifier(key));
    }
    let query = Utils.joinSQLFragments([
      "INSERT",
      "INTO",
      this.quoteTable(tableName),
      `(${insertColumns.join(",")})`,
      "VALUES",
      `(${inBindPosition})`
    ]);
    if (returnColumn.length > 0) {
      options.outBindAttributes = outBindBindDefMap;
      query = Utils.joinSQLFragments([
        query,
        "RETURNING",
        `${returnColumn.join(",")}`,
        "INTO",
        `${returnColumnBindPositions}`
      ]);
    }
    const result = { query };
    result.bind = tuples;
    options.inbindAttributes = inBindBindDefMap;
    return result;
  }
  truncateTableQuery(tableName) {
    return `TRUNCATE TABLE ${this.quoteTable(tableName)}`;
  }
  deleteQuery(tableName, where, options, model) {
    options = options || {};
    const table = tableName;
    where = this.getWhereConditions(where, null, model, options);
    let queryTmpl;
    if (options.limit) {
      const whereTmpl = where ? ` AND ${where}` : "";
      queryTmpl = `DELETE FROM ${this.quoteTable(table)} WHERE rowid IN (SELECT rowid FROM ${this.quoteTable(table)} WHERE rownum <= ${this.escape(options.limit)}${whereTmpl})`;
    } else {
      const whereTmpl = where ? ` WHERE ${where}` : "";
      queryTmpl = `DELETE FROM ${this.quoteTable(table)}${whereTmpl}`;
    }
    return queryTmpl;
  }
  showIndexesQuery(table) {
    const [tableName, owner] = this.getSchemaNameAndTableName(table);
    const sql = [
      "SELECT i.index_name,i.table_name, i.column_name, u.uniqueness, i.descend, c.constraint_type ",
      "FROM all_ind_columns i ",
      "INNER JOIN all_indexes u ",
      "ON (u.table_name = i.table_name AND u.index_name = i.index_name) ",
      "LEFT OUTER JOIN all_constraints c ",
      "ON (c.table_name = i.table_name AND c.index_name = i.index_name) ",
      `WHERE i.table_name = ${this.escape(tableName)}`,
      " AND u.table_owner = ",
      owner ? this.escape(owner) : "USER",
      " ORDER BY index_name, column_position"
    ];
    return sql.join("");
  }
  removeIndexQuery(tableName, indexNameOrAttributes) {
    let indexName = indexNameOrAttributes;
    if (typeof indexName !== "string") {
      indexName = Utils.underscore(`${tableName}_${indexNameOrAttributes.join("_")}`);
    }
    return `DROP INDEX ${this.quoteIdentifier(indexName)}`;
  }
  attributeToSQL(attribute, options) {
    if (!_.isPlainObject(attribute)) {
      attribute = {
        type: attribute
      };
    }
    attribute.onUpdate = "";
    if (attribute.references) {
      if (attribute.Model && attribute.Model.tableName === attribute.references.model) {
        this.sequelize.log("Oracle does not support self referencial constraints, we will remove it but we recommend restructuring your query");
        attribute.onDelete = "";
      }
    }
    let template;
    template = attribute.type.toSql ? attribute.type.toSql() : "";
    if (attribute.type instanceof DataTypes.JSON) {
      template += ` CHECK (${this.quoteIdentifier(options.attributeName)} IS JSON)`;
      return template;
    }
    if (Utils.defaultValueSchemable(attribute.defaultValue)) {
      template += ` DEFAULT ${this.escape(attribute.defaultValue)}`;
    }
    if (attribute.allowNull === false) {
      template += " NOT NULL";
    }
    if (attribute.type instanceof DataTypes.ENUM) {
      if (attribute.type.values && !attribute.values)
        attribute.values = attribute.type.values;
      template += ` CHECK (${this.quoteIdentifier(options.attributeName)} IN(${_.map(attribute.values, (value) => {
        return this.escape(value);
      }).join(", ")}))`;
      return template;
    }
    if (attribute.type instanceof DataTypes.BOOLEAN) {
      template += ` CHECK (${this.quoteIdentifier(options.attributeName)} IN('1', '0'))`;
      return template;
    }
    if (attribute.autoIncrement) {
      template = " NUMBER(*,0) GENERATED BY DEFAULT ON NULL AS IDENTITY";
    } else if (attribute.type && attribute.type.key === DataTypes.DOUBLE.key) {
      template = attribute.type.toSql();
    } else if (attribute.type) {
      let unsignedTemplate = "";
      if (attribute.type._unsigned) {
        attribute.type._unsigned = false;
        unsignedTemplate += ` check(${this.quoteIdentifier(options.attributeName)} >= 0)`;
      }
      template = attribute.type.toString();
      if (attribute.type && attribute.type !== "TEXT" && attribute.type._binary !== true && Utils.defaultValueSchemable(attribute.defaultValue)) {
        template += ` DEFAULT ${this.escape(attribute.defaultValue)}`;
      }
      if (!attribute.autoIncrement) {
        if (attribute.allowNull === false) {
          template += " NOT NULL";
        } else if (!attribute.primaryKey && !Utils.defaultValueSchemable(attribute.defaultValue)) {
          template += " NULL";
        }
      }
      template += unsignedTemplate;
    } else {
      template = "";
    }
    if (attribute.unique === true && !attribute.primaryKey) {
      template += " UNIQUE";
    }
    if (attribute.primaryKey) {
      template += " PRIMARY KEY";
    }
    if ((!options || !options.withoutForeignKeyConstraints) && attribute.references) {
      template += ` REFERENCES ${this.quoteTable(attribute.references.model)}`;
      if (attribute.references.key) {
        template += ` (${this.quoteIdentifier(attribute.references.key)})`;
      } else {
        template += ` (${this.quoteIdentifier("id")})`;
      }
      if (attribute.onDelete && attribute.onDelete.toUpperCase() !== "NO ACTION") {
        template += ` ON DELETE ${attribute.onDelete.toUpperCase()}`;
      }
    }
    return template;
  }
  attributesToSQL(attributes, options) {
    const result = {};
    for (const key in attributes) {
      const attribute = attributes[key];
      const attributeName = attribute.field || key;
      result[attributeName] = this.attributeToSQL(attribute, __spreadValues({ attributeName }, options));
    }
    return result;
  }
  createTrigger() {
    throwMethodUndefined("createTrigger");
  }
  dropTrigger() {
    throwMethodUndefined("dropTrigger");
  }
  renameTrigger() {
    throwMethodUndefined("renameTrigger");
  }
  createFunction() {
    throwMethodUndefined("createFunction");
  }
  dropFunction() {
    throwMethodUndefined("dropFunction");
  }
  renameFunction() {
    throwMethodUndefined("renameFunction");
  }
  getConstraintsOnColumn(table, column) {
    const [tableName, schemaName] = this.getSchemaNameAndTableName(table);
    column = this.getCatalogName(column);
    const sql = [
      "SELECT CONSTRAINT_NAME FROM user_cons_columns WHERE TABLE_NAME = ",
      this.escape(tableName),
      " and OWNER = ",
      table.schema ? this.escape(schemaName) : "USER",
      " and COLUMN_NAME = ",
      this.escape(column),
      " AND POSITION IS NOT NULL ORDER BY POSITION"
    ].join("");
    return sql;
  }
  getForeignKeysQuery(table) {
    const [tableName, schemaName] = this.getSchemaNameAndTableName(table);
    const sql = [
      'SELECT DISTINCT  a.table_name "tableName", a.constraint_name "constraintName", a.owner "owner",  a.column_name "columnName",',
      ' b.table_name "referencedTableName", b.column_name "referencedColumnName"',
      " FROM all_cons_columns a",
      " JOIN all_constraints c ON a.owner = c.owner AND a.constraint_name = c.constraint_name",
      " JOIN all_cons_columns b ON c.owner = b.owner AND c.r_constraint_name = b.constraint_name",
      " WHERE c.constraint_type  = 'R'",
      " AND a.table_name = ",
      this.escape(tableName),
      " AND a.owner = ",
      table.schema ? this.escape(schemaName) : "USER",
      " ORDER BY a.table_name, a.constraint_name"
    ].join("");
    return sql;
  }
  dropForeignKeyQuery(tableName, foreignKey) {
    return this.dropConstraintQuery(tableName, foreignKey);
  }
  getPrimaryKeyConstraintQuery(table) {
    const [tableName, schemaName] = this.getSchemaNameAndTableName(table);
    const sql = [
      "SELECT cols.column_name, atc.identity_column ",
      "FROM all_constraints cons, all_cons_columns cols ",
      "INNER JOIN all_tab_columns atc ON(atc.table_name = cols.table_name AND atc.COLUMN_NAME = cols.COLUMN_NAME )",
      "WHERE cols.table_name = ",
      this.escape(tableName),
      "AND cols.owner = ",
      table.schema ? this.escape(schemaName) : "USER ",
      "AND cons.constraint_type = 'P' ",
      "AND cons.constraint_name = cols.constraint_name ",
      "AND cons.owner = cols.owner ",
      "ORDER BY cols.table_name, cols.position"
    ].join("");
    return sql;
  }
  dropConstraintQuery(tableName, constraintName) {
    return `ALTER TABLE ${this.quoteTable(tableName)} DROP CONSTRAINT ${constraintName}`;
  }
  setIsolationLevelQuery(value, options) {
    if (options.parent) {
      return;
    }
    switch (value) {
      case Transaction.ISOLATION_LEVELS.READ_UNCOMMITTED:
      case Transaction.ISOLATION_LEVELS.READ_COMMITTED:
        return "SET TRANSACTION ISOLATION LEVEL READ COMMITTED;";
      case Transaction.ISOLATION_LEVELS.REPEATABLE_READ:
        return "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;";
      default:
        throw new Error(`isolation level "${value}" is not supported`);
    }
  }
  getAliasToken() {
    return "";
  }
  startTransactionQuery(transaction) {
    if (transaction.parent) {
      return `SAVEPOINT ${this.quoteIdentifier(transaction.name)}`;
    }
    return "BEGIN TRANSACTION";
  }
  commitTransactionQuery(transaction) {
    if (transaction.parent) {
      return;
    }
    return "COMMIT TRANSACTION";
  }
  rollbackTransactionQuery(transaction) {
    if (transaction.parent) {
      return `ROLLBACK TO SAVEPOINT ${this.quoteIdentifier(transaction.name)}`;
    }
    return "ROLLBACK TRANSACTION";
  }
  handleSequelizeMethod(smth, tableName, factory, options, prepend) {
    let str;
    if (smth instanceof Utils.Json) {
      if (smth.conditions) {
        const conditions = this.parseConditionObject(smth.conditions).map((condition) => `${this.jsonPathExtractionQuery(condition.path[0], _.tail(condition.path))} = '${condition.value}'`);
        return conditions.join(" AND ");
      }
      if (smth.path) {
        if (this._checkValidJsonStatement(smth.path)) {
          str = smth.path;
        } else {
          const paths = _.toPath(smth.path);
          const column = paths.shift();
          str = this.jsonPathExtractionQuery(column, paths);
        }
        if (smth.value) {
          str += util.format(" = %s", this.escape(smth.value));
        }
        return str;
      }
    }
    if (smth instanceof Utils.Cast) {
      if (smth.val instanceof Utils.SequelizeMethod) {
        str = this.handleSequelizeMethod(smth.val, tableName, factory, options, prepend);
        if (smth.type === "boolean") {
          str = `(CASE WHEN ${str}='true' THEN 1 ELSE 0 END)`;
          return `CAST(${str} AS NUMBER)`;
        }
        if (smth.type === "timestamptz" && /json_value\(/.test(str)) {
          str = str.slice(0, -1);
          return `${str} RETURNING TIMESTAMP WITH TIME ZONE)`;
        }
      }
    }
    return super.handleSequelizeMethod(smth, tableName, factory, options, prepend);
  }
  _checkValidJsonStatement(stmt) {
    if (typeof stmt !== "string") {
      return false;
    }
    let currentIndex = 0;
    let openingBrackets = 0;
    let closingBrackets = 0;
    let hasJsonFunction = false;
    let hasInvalidToken = false;
    while (currentIndex < stmt.length) {
      const string = stmt.substr(currentIndex);
      const functionMatches = JSON_FUNCTION_REGEX.exec(string);
      if (functionMatches) {
        currentIndex += functionMatches[0].indexOf("(");
        hasJsonFunction = true;
        continue;
      }
      const operatorMatches = JSON_OPERATOR_REGEX.exec(string);
      if (operatorMatches) {
        currentIndex += operatorMatches[0].length;
        hasJsonFunction = true;
        continue;
      }
      const tokenMatches = TOKEN_CAPTURE_REGEX.exec(string);
      if (tokenMatches) {
        const capturedToken = tokenMatches[1];
        if (capturedToken === "(") {
          openingBrackets++;
        } else if (capturedToken === ")") {
          closingBrackets++;
        } else if (capturedToken === ";") {
          hasInvalidToken = true;
          break;
        }
        currentIndex += tokenMatches[0].length;
        continue;
      }
      break;
    }
    if (hasJsonFunction && (hasInvalidToken || openingBrackets !== closingBrackets)) {
      throw new Error(`Invalid json statement: ${stmt}`);
    }
    return hasJsonFunction;
  }
  jsonPathExtractionQuery(column, path) {
    let paths = _.toPath(path);
    const quotedColumn = this.isIdentifierQuoted(column) ? column : this.quoteIdentifier(column);
    paths = paths.map((subPath) => {
      return /\D/.test(subPath) ? Utils.addTicks(subPath, '"') : subPath;
    });
    const pathStr = this.escape(["$"].concat(paths).join(".").replace(/\.(\d+)(?:(?=\.)|$)/g, (__, digit) => `[${digit}]`));
    return `json_value(${quotedColumn},${pathStr})`;
  }
  addLimitAndOffset(options, model) {
    let fragment = "";
    const offset = options.offset || 0, isSubQuery = options.hasIncludeWhere || options.hasIncludeRequired || options.hasMultiAssociation;
    let orders = {};
    if (options.order) {
      orders = this.getQueryOrders(options, model, isSubQuery);
    }
    if (options.limit || options.offset) {
      if (!orders.mainQueryOrder || !orders.mainQueryOrder.length || isSubQuery && (!orders.subQueryOrder || !orders.subQueryOrder.length)) {
        const tablePkFragment = `${this.quoteTable(options.tableAs || model.name)}.${this.quoteIdentifier(model.primaryKeyField)}`;
        fragment += ` ORDER BY ${tablePkFragment}`;
      }
      if (options.offset || options.limit) {
        fragment += ` OFFSET ${this.escape(offset)} ROWS`;
      }
      if (options.limit) {
        fragment += ` FETCH NEXT ${this.escape(options.limit)} ROWS ONLY`;
      }
    }
    return fragment;
  }
  booleanValue(value) {
    return value ? 1 : 0;
  }
  quoteIdentifier(identifier, force = false) {
    const optForceQuote = force;
    const optQuoteIdentifiers = this.options.quoteIdentifiers !== false;
    const rawIdentifier = Utils.removeTicks(identifier, '"');
    const regExp = /^(([\w][\w\d_]*))$/g;
    if (optForceQuote !== true && optQuoteIdentifiers === false && regExp.test(rawIdentifier) && !ORACLE_RESERVED_WORDS.includes(rawIdentifier.toUpperCase())) {
      return rawIdentifier;
    }
    return Utils.addTicks(rawIdentifier, '"');
  }
  bindParam(bind, posOffset = 0) {
    return (value) => {
      bind.push(value);
      return `:${bind.length + posOffset}`;
    };
  }
  authTestQuery() {
    return "SELECT 1+1 AS result FROM DUAL";
  }
}
function throwMethodUndefined(methodName) {
  throw new Error(`The method "${methodName}" is not defined! Please add it to your sql dialect.`);
}
//# sourceMappingURL=query-generator.js.map
