text: "use strict";
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
      return new (P || (P = Promise))(function (resolve, reject) {
          function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
          function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
          function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
          step((generator = generator.apply(thisArg, _arguments || [])).next());
      });
  };
  var __generator = (this && this.__generator) || function (thisArg, body) {
      var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
      return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
      function verb(n) { return function (v) { return step([n, v]); }; }
      function step(op) {
          if (f) throw new TypeError("Generator is already executing.");
          while (_) try {
              if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
              if (y = 0, t) op = [0, t.value];
              switch (op[0]) {
                  case 0: case 1: t = op; break;
                  case 4: _.label++; return { value: op[1], done: false };
                  case 5: _.label++; y = op[1]; op = [0]; continue;
                  case 7: op = _.ops.pop(); _.trys.pop(); continue;
                  default:
                      if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                      if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                      if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                      if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                      if (t[2]) _.ops.pop();
                      _.trys.pop(); continue;
              }
              op = body.call(thisArg, _);
          } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
          if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
      }
  };
  Object.defineProperty(exports, "__esModule", { value: true });
  var TransactionAlreadyStartedError_1 = require("../../error/TransactionAlreadyStartedError");
  var TransactionNotStartedError_1 = require("../../error/TransactionNotStartedError");
  var ColumnSchema_1 = require("../../schema-builder/schema/ColumnSchema");
  var ColumnMetadata_1 = require("../../metadata/ColumnMetadata");
  var TableSchema_1 = require("../../schema-builder/schema/TableSchema");
  var QueryRunnerAlreadyReleasedError_1 = require("../../error/QueryRunnerAlreadyReleasedError");
  var OrmUtils_1 = require("../../util/OrmUtils");
  var QueryFailedError_1 = require("../../error/QueryFailedError");
  /**
   * Runs queries on a single websql database connection.
   */
  var WebsqlQueryRunner = (function () {
      // -------------------------------------------------------------------------
      // Constructor
      // -------------------------------------------------------------------------
      function WebsqlQueryRunner(driver) {
          /**
           * Indicates if connection for this query runner is released.
           * Once its released, query runner cannot run queries anymore.
           */
          this.isReleased = false;
          /**
           * Indicates if transaction is in progress.
           */
          this.isTransactionActive = false;
          /**
           * Stores temporarily user data.
           * Useful for sharing data with subscribers.
           */
          this.data = {};
          /**
           * Indicates if special query runner mode in which sql queries won't be executed is enabled.
           */
          this.sqlMemoryMode = false;
          /**
           * Sql-s stored if "sql in memory" mode is enabled.
           */
          this.sqlsInMemory = [];
          this.driver = driver;
          this.connection = driver.connection;
      }
      // -------------------------------------------------------------------------
      // Public Methods
      // -------------------------------------------------------------------------
      /**
       * Creates/uses database connection from the connection pool to perform further operations.
       * Returns obtained database connection.
       */
      WebsqlQueryRunner.prototype.connect = function () {
          var _this = this;
          if (this.databaseConnection)
              return Promise.resolve(this.databaseConnection);
          if (this.databaseConnectionPromise)
              return this.databaseConnectionPromise;
          var options = Object.assign({}, {
              database: this.driver.options.database,
          }, this.driver.options.extra || {});
          this.databaseConnectionPromise = new Promise(function (ok, fail) {
              _this.databaseConnection = openDatabase(options.database, options.version, options.description, options.size);
              ok(_this.databaseConnection);
          });
          return this.databaseConnectionPromise;
      };
      /**
       * Releases used database connection.
       * You cannot use query runner methods once its released.
       */
      WebsqlQueryRunner.prototype.release = function () {
          this.isReleased = true;
          // todo: implement closing
          return Promise.resolve();
      };
      /**
       * Starts transaction.
       */
      WebsqlQueryRunner.prototype.startTransaction = function () {
          return __awaiter(this, void 0, void 0, function () {
              return __generator(this, function (_a) {
                  if (this.isReleased)
                      throw new QueryRunnerAlreadyReleasedError_1.QueryRunnerAlreadyReleasedError();
                  if (this.isTransactionActive)
                      throw new TransactionAlreadyStartedError_1.TransactionAlreadyStartedError();
                  this.isTransactionActive = true;
                  return [2 /*return*/];
              });
          });
      };
      /**
       * Commits transaction.
       * Error will be thrown if transaction was not started.
       */
      WebsqlQueryRunner.prototype.commitTransaction = function () {
          return __awaiter(this, void 0, void 0, function () {
              return __generator(this, function (_a) {
                  if (this.isReleased)
                      throw new QueryRunnerAlreadyReleasedError_1.QueryRunnerAlreadyReleasedError();
                  if (!this.isTransactionActive)
                      throw new TransactionNotStartedError_1.TransactionNotStartedError();
                  // await this.query("COMMIT");
                  this.isTransactionActive = false;
                  return [2 /*return*/];
              });
          });
      };
      /**
       * Rollbacks transaction.
       * Error will be thrown if transaction was not started.
       */
      WebsqlQueryRunner.prototype.rollbackTransaction = function () {
          return __awaiter(this, void 0, void 0, function () {
              return __generator(this, function (_a) {
                  if (this.isReleased)
                      throw new QueryRunnerAlreadyReleasedError_1.QueryRunnerAlreadyReleasedError();
                  if (!this.isTransactionActive)
                      throw new TransactionNotStartedError_1.TransactionNotStartedError();
                  // await this.query("ROLLBACK");
                  this.isTransactionActive = false;
                  return [2 /*return*/];
              });
          });
      };
      /**
       * Executes a given SQL query.
       */
      WebsqlQueryRunner.prototype.query = function (query, parameters) {
          var _this = this;
          if (this.isReleased)
              throw new QueryRunnerAlreadyReleasedError_1.QueryRunnerAlreadyReleasedError();
          return new Promise(function (ok, fail) { return __awaiter(_this, void 0, void 0, function () {
              var _this = this;
              var db, queryStartTime;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0: return [4 /*yield*/, this.connect()];
                      case 1:
                          db = _a.sent();
                          // todo(dima): check if transaction is not active
                          this.driver.connection.logger.logQuery(query, parameters, this);
                          queryStartTime = +new Date();
                          db.transaction(function (tx) {
                              tx.executeSql(query, parameters, function (tx, result) {
                                  // log slow queries if maxQueryExecution time is set
                                  var maxQueryExecutionTime = _this.driver.connection.options.maxQueryExecutionTime;
                                  var queryEndTime = +new Date();
                                  var queryExecutionTime = queryEndTime - queryStartTime;
                                  if (maxQueryExecutionTime && queryExecutionTime > maxQueryExecutionTime)
                                      _this.driver.connection.logger.logQuerySlow(queryExecutionTime, query, parameters, _this);
                                  var rows = Object
                                      .keys(result.rows)
                                      .filter(function (key) { return key !== "length"; })
                                      .map(function (key) { return result.rows[key]; });
                                  ok(rows);
                              }, function (tx, err) {
                                  _this.driver.connection.logger.logQueryError(err, query, parameters, _this);
                                  return fail(new QueryFailedError_1.QueryFailedError(query, parameters, err));
                              });
                          });
                          return [2 /*return*/];
                  }
              });
          }); });
      };
      /**
       * Returns raw data stream.
       */
      WebsqlQueryRunner.prototype.stream = function (query, parameters, onEnd, onError) {
          throw new Error("Stream is not supported by websqlite driver.");
      };
      /**
       * Insert a new row with given values into the given table.
       * Returns value of the generated column if given and generate column exist in the table.
       */
      WebsqlQueryRunner.prototype.insert = function (tableName, keyValues) {
          return __awaiter(this, void 0, void 0, function () {
              var _this = this;
              var keys, columns, values, generatedColumns, sql, parameters;
              return __generator(this, function (_a) {
                  keys = Object.keys(keyValues);
                  columns = keys.map(function (key) { return "\"" + key + "\""; }).join(", ");
                  values = keys.map(function (key, index) { return "$" + (index + 1); }).join(",");
                  generatedColumns = this.connection.hasMetadata(tableName) ? this.connection.getMetadata(tableName).generatedColumns : [];
                  sql = columns.length > 0 ? ("INSERT INTO \"" + tableName + "\"(" + columns + ") VALUES (" + values + ")") : "INSERT INTO \"" + tableName + "\" DEFAULT VALUES";
                  parameters = keys.map(function (key) { return keyValues[key]; });
                  return [2 /*return*/, new Promise(function (ok, fail) { return __awaiter(_this, void 0, void 0, function () {
                          var _this = this;
                          var db;
                          return __generator(this, function (_a) {
                              switch (_a.label) {
                                  case 0:
                                      this.driver.connection.logger.logQuery(sql, parameters, this);
                                      return [4 /*yield*/, this.connect()];
                                  case 1:
                                      db = _a.sent();
                                      // todo: check if transaction is not active
                                      db.transaction(function (tx) {
                                          tx.executeSql(sql, parameters, function (tx, result) {
                                              var generatedMap = generatedColumns.reduce(function (map, generatedColumn) {
                                                  var value = generatedColumn.isPrimary && generatedColumn.generationStrategy === "increment" && result["insertId"] ? result["insertId"] : keyValues[generatedColumn.databaseName];
                                                  if (!value)
                                                      return map;
                                                  return OrmUtils_1.OrmUtils.mergeDeep(map, generatedColumn.createValueMap(value));
                                              }, {});
                                              ok({
                                                  result: undefined,
                                                  generatedMap: Object.keys(generatedMap).length > 0 ? generatedMap : undefined
                                              });
                                          }, function (tx, err) {
                                              _this.driver.connection.logger.logQueryError(err, sql, parameters, _this);
                                              return fail(err);
                                          });
                                      });
                                      return [2 /*return*/];
                              }
                          });
                      }); })];
              });
          });
      };
      /**
       * Updates rows that match given conditions in the given table.
       */
      WebsqlQueryRunner.prototype.update = function (tableName, valuesMap, conditions) {
          return __awaiter(this, void 0, void 0, function () {
              var updateValues, conditionString, query, updateParams, conditionParams, allParameters;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          updateValues = this.parametrize(valuesMap).join(", ");
                          conditionString = this.parametrize(conditions, Object.keys(valuesMap).length).join(" AND ");
                          query = "UPDATE \"" + tableName + "\" SET " + updateValues + " " + (conditionString ? (" WHERE " + conditionString) : "");
                          updateParams = Object.keys(valuesMap).map(function (key) { return valuesMap[key]; });
                          conditionParams = Object.keys(conditions).map(function (key) { return conditions[key]; });
                          allParameters = updateParams.concat(conditionParams);
                          return [4 /*yield*/, this.query(query, allParameters)];
                      case 1:
                          _a.sent();
                          return [2 /*return*/];
                  }
              });
          });
      };
      /**
       * Deletes from the given table by a given conditions.
       */
      WebsqlQueryRunner.prototype.delete = function (tableName, conditions, maybeParameters) {
          return __awaiter(this, void 0, void 0, function () {
              var conditionString, parameters, sql;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          conditionString = typeof conditions === "string" ? conditions : this.parametrize(conditions).join(" AND ");
                          parameters = conditions instanceof Object ? Object.keys(conditions).map(function (key) { return conditions[key]; }) : maybeParameters;
                          sql = "DELETE FROM \"" + tableName + "\" WHERE " + conditionString;
                          return [4 /*yield*/, this.query(sql, parameters)];
                      case 1:
                          _a.sent();
                          return [2 /*return*/];
                  }
              });
          });
      };
      /**
       * Inserts rows into closure table.
       */
      WebsqlQueryRunner.prototype.insertIntoClosureTable = function (tableName, newEntityId, parentId, hasLevel) {
          return __awaiter(this, void 0, void 0, function () {
              var sql, results;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          sql = "";
                          if (hasLevel) {
                              sql = "INSERT INTO \"" + tableName + "\"(\"ancestor\", \"descendant\", \"level\") " +
                                  ("SELECT \"ancestor\", " + newEntityId + ", \"level\" + 1 FROM \"" + tableName + "\" WHERE \"descendant\" = " + parentId + " ") +
                                  ("UNION ALL SELECT " + newEntityId + ", " + newEntityId + ", 1");
                          }
                          else {
                              sql = "INSERT INTO \"" + tableName + "\"(\"ancestor\", \"descendant\") " +
                                  ("SELECT \"ancestor\", " + newEntityId + " FROM \"" + tableName + "\" WHERE \"descendant\" = " + parentId + " ") +
                                  ("UNION ALL SELECT " + newEntityId + ", " + newEntityId);
                          }
                          return [4 /*yield*/, this.query(sql)];
                      case 1:
                          _a.sent();
                          return [4 /*yield*/, this.query("SELECT MAX(\"level\") as \"level\" FROM " + tableName + " WHERE \"descendant\" = " + parentId)];
                      case 2:
                          results = _a.sent();
                          return [2 /*return*/, results && results[0] && results[0]["level"] ? parseInt(results[0]["level"]) + 1 : 1];
                  }
              });
          });
      };
      /**
       * Loads given table's data from the database.
       */
      WebsqlQueryRunner.prototype.loadTableSchema = function (tableName) {
          return __awaiter(this, void 0, void 0, function () {
              var tableSchemas;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0: return [4 /*yield*/, this.loadTableSchemas([tableName])];
                      case 1:
                          tableSchemas = _a.sent();
                          return [2 /*return*/, tableSchemas.length > 0 ? tableSchemas[0] : undefined];
                  }
              });
          });
      };
      /**
       * Loads all tables (with given names) from the database and creates a TableSchema from them.
       */
      WebsqlQueryRunner.prototype.loadTableSchemas = function (tableNames) {
          return __awaiter(this, void 0, void 0, function () {
              var _this = this;
              var tableNamesString, dbTables;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          // if no tables given then no need to proceed
                          if (!tableNames || !tableNames.length)
                              return [2 /*return*/, []];
                          tableNamesString = tableNames.map(function (tableName) { return "'" + tableName + "'"; }).join(", ");
                          return [4 /*yield*/, this.query("SELECT * FROM sqlite_master WHERE type = 'table' AND name IN (" + tableNamesString + ")")];
                      case 1:
                          dbTables = _a.sent();
                          // if tables were not found in the db, no need to proceed
                          if (!dbTables || !dbTables.length)
                              return [2 /*return*/, []];
                          // create table schemas for loaded tables
                          return [2 /*return*/, Promise.all(dbTables.map(function (dbTable) { return __awaiter(_this, void 0, void 0, function () {
                                  var tableSchema;
                                  return __generator(this, function (_a) {
                                      tableSchema = new TableSchema_1.TableSchema(dbTable["name"]);
                                      // load columns and indices
                                      /*const [dbColumns, dbIndices, dbForeignKeys]: ObjectLiteral[][] = await Promise.all([
                                          this.query(`PRAGMA table_info("${dbTable["name"]}")`),
                                          this.query(`PRAGMA index_list("${dbTable["name"]}")`),
                                          this.query(`PRAGMA foreign_key_list("${dbTable["name"]}")`),
                                      ]);
                          
                                      // find column name with auto increment
                                      let autoIncrementColumnName: string|undefined = undefined;
                                      const tableSql: string = dbTable["sql"];
                                      if (tableSql.indexOf("AUTOINCREMENT") !== -1) {
                                          autoIncrementColumnName = tableSql.substr(0, tableSql.indexOf("AUTOINCREMENT"));
                                          const comma = autoIncrementColumnName.lastIndexOf(",");
                                          const bracket = autoIncrementColumnName.lastIndexOf("(");
                                          if (comma !== -1) {
                                              autoIncrementColumnName = autoIncrementColumnName.substr(comma);
                                              autoIncrementColumnName = autoIncrementColumnName.substr(0, autoIncrementColumnName.lastIndexOf("\""));
                                              autoIncrementColumnName = autoIncrementColumnName.substr(autoIncrementColumnName.indexOf("\"") + 1);
                          
                                          } else if (bracket !== -1) {
                                              autoIncrementColumnName = autoIncrementColumnName.substr(bracket);
                                              autoIncrementColumnName = autoIncrementColumnName.substr(0, autoIncrementColumnName.lastIndexOf("\""));
                                              autoIncrementColumnName = autoIncrementColumnName.substr(autoIncrementColumnName.indexOf("\"") + 1);
                                          }
                                      }
                          
                                      // create column schemas from the loaded columns
                                      tableSchema.columns = dbColumns.map(dbColumn => {
                                          const columnSchema = new ColumnSchema();
                                          columnSchema.name = dbColumn["name"];
                                          columnSchema.type = dbColumn["type"].toLowerCase();
                                          columnSchema.default = dbColumn["dflt_value"] !== null && dbColumn["dflt_value"] !== undefined ? dbColumn["dflt_value"] : undefined;
                                          columnSchema.isNullable = dbColumn["notnull"] === 0;
                                          columnSchema.isPrimary = dbColumn["pk"] === 1;
                                          columnSchema.comment = ""; // todo later
                                          columnSchema.isGenerated = autoIncrementColumnName === dbColumn["name"];
                                          const columnForeignKeys = dbForeignKeys
                                              .filter(foreignKey => foreignKey["from"] === dbColumn["name"])
                                              .map(foreignKey => {
                                                  const keyName = namingStrategy.foreignKeyName(dbTable["name"], [foreignKey["from"]], foreignKey["table"], [foreignKey["to"]]);
                                                  return new ForeignKeySchema(keyName, [foreignKey["from"]], [foreignKey["to"]], foreignKey["table"], foreignKey["on_delete"]); // todo: how sqlite return from and to when they are arrays? (multiple column foreign keys)
                                              });
                                          tableSchema.addForeignKeys(columnForeignKeys);
                                          return columnSchema;
                                      });
                          
                                      // create primary key schema
                                      await Promise.all(dbIndices
                                          .filter(index => index["origin"] === "pk")
                                          .map(async index => {
                                              const indexInfos: ObjectLiteral[] = await this.query(`PRAGMA index_info("${index["name"]}")`);
                                              const indexColumns = indexInfos.map(indexInfo => indexInfo["name"]);
                                              indexColumns.forEach(indexColumn => {
                                                  tableSchema.primaryKeys.push(new PrimaryKeySchema(index["name"], indexColumn));
                                              });
                                          }));
                          
                                      // create index schemas from the loaded indices
                                      const indicesPromises = dbIndices
                                          .filter(dbIndex => {
                                              return  dbIndex["origin"] !== "pk" &&
                                                  (!tableSchema.foreignKeys.find(foreignKey => foreignKey.name === dbIndex["name"])) &&
                                                  (!tableSchema.primaryKeys.find(primaryKey => primaryKey.name === dbIndex["name"]));
                                          })
                                          .map(dbIndex => dbIndex["name"])
                                          .filter((value, index, self) => self.indexOf(value) === index) // unqiue
                                          .map(async dbIndexName => {
                                              const dbIndex = dbIndices.find(dbIndex => dbIndex["name"] === dbIndexName);
                                              const indexInfos: ObjectLiteral[] = await this.query(`PRAGMA index_info("${dbIndex!["name"]}")`);
                                              const indexColumns = indexInfos.map(indexInfo => indexInfo["name"]);
                          
                                              // check if db index is generated by sqlite itself and has special use case
                                              if (dbIndex!["name"].substr(0, "sqlite_autoindex".length) === "sqlite_autoindex") {
                                                  if (dbIndex!["unique"] === 1) { // this means we have a special index generated for a column
                                                      // so we find and update the column
                                                      indexColumns.forEach(columnName => {
                                                          const column = tableSchema.columns.find(column => column.name === columnName);
                                                          if (column)
                                                              column.isUnique = true;
                                                      });
                                                  }
                          
                                                  return Promise.resolve(undefined);
                          
                                              } else {
                                                  return new IndexSchema(dbTable["name"], dbIndex!["name"], indexColumns, dbIndex!["unique"] === "1");
                                              }
                                          });
                          
                                      const indices = await Promise.all(indicesPromises);
                                      tableSchema.indices = indices.filter(index => !!index) as IndexSchema[];*/
                                      return [2 /*return*/, tableSchema];
                                  });
                              }); }))];
                  }
              });
          });
      };
      /**
       * Checks if table with the given name exist in the database.
       */
      WebsqlQueryRunner.prototype.hasTable = function (tableName) {
          return __awaiter(this, void 0, void 0, function () {
              var sql, result;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          sql = "SELECT * FROM sqlite_master WHERE type = 'table' AND name = " + tableName + "'";
                          return [4 /*yield*/, this.query(sql)];
                      case 1:
                          result = _a.sent();
                          return [2 /*return*/, result.length ? true : false];
                  }
              });
          });
      };
      /**
       * Creates a schema if it's not created.
       */
      WebsqlQueryRunner.prototype.createSchema = function () {
          return Promise.resolve();
      };
      /**
       * Creates a new table from the given table metadata and column metadatas.
       */
      WebsqlQueryRunner.prototype.createTable = function (table) {
          return __awaiter(this, void 0, void 0, function () {
              var _this = this;
              var columnDefinitions, sql, primaryKeyColumns;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          columnDefinitions = table.columns.map(function (column) { return _this.buildCreateColumnSql(column); }).join(", ");
                          sql = "CREATE TABLE \"" + table.name + "\" (" + columnDefinitions;
                          primaryKeyColumns = table.columns.filter(function (column) { return column.isPrimary && !column.isGenerated; });
                          if (primaryKeyColumns.length > 0)
                              sql += ", PRIMARY KEY(" + primaryKeyColumns.map(function (column) { return "" + column.name; }).join(", ") + ")"; // for some reason column escaping here generates a wrong schema
                          sql += ")";
                          return [4 /*yield*/, this.query(sql)];
                      case 1:
                          _a.sent();
                          return [2 /*return*/];
                  }
              });
          });
      };
      /**
       * Drops the table.
       */
      WebsqlQueryRunner.prototype.dropTable = function (tableName) {
          return __awaiter(this, void 0, void 0, function () {
              var sql;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          sql = "DROP TABLE \"" + tableName + "\"";
                          return [4 /*yield*/, this.query(sql)];
                      case 1:
                          _a.sent();
                          return [2 /*return*/];
                  }
              });
          });
      };
      /**
       * Checks if column with the given name exist in the given table.
       */
      WebsqlQueryRunner.prototype.hasColumn = function (tableName, columnName) {
          return __awaiter(this, void 0, void 0, function () {
              var sql, columns;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          sql = "PRAGMA table_info(\"" + tableName + "\")";
                          return [4 /*yield*/, this.query(sql)];
                      case 1:
                          columns = _a.sent();
                          return [2 /*return*/, !!columns.find(function (column) { return column["name"] === columnName; })];
                  }
              });
          });
      };
      /**
       * Creates a new column from the column schema in the table.
       */
      WebsqlQueryRunner.prototype.addColumn = function (tableSchemaOrName, column) {
          return __awaiter(this, void 0, void 0, function () {
              var tableSchema, newTableSchema;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0: return [4 /*yield*/, this.getTableSchema(tableSchemaOrName)];
                      case 1:
                          tableSchema = _a.sent();
                          newTableSchema = tableSchema.clone();
                          newTableSchema.addColumns([column]);
                          return [4 /*yield*/, this.recreateTable(newTableSchema, tableSchema)];
                      case 2:
                          _a.sent();
                          return [2 /*return*/];
                  }
              });
          });
      };
      /**
       * Creates a new columns from the column schema in the table.
       */
      WebsqlQueryRunner.prototype.addColumns = function (tableSchemaOrName, columns) {
          return __awaiter(this, void 0, void 0, function () {
              var tableSchema, newTableSchema;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0: return [4 /*yield*/, this.getTableSchema(tableSchemaOrName)];
                      case 1:
                          tableSchema = _a.sent();
                          newTableSchema = tableSchema.clone();
                          newTableSchema.addColumns(columns);
                          return [4 /*yield*/, this.recreateTable(newTableSchema, tableSchema)];
                      case 2:
                          _a.sent();
                          return [2 /*return*/];
                  }
              });
          });
      };
      /**
       * Renames column in the given table.
       */
      WebsqlQueryRunner.prototype.renameColumn = function (tableSchemaOrName, oldColumnSchemaOrName, newColumnSchemaOrName) {
          return __awaiter(this, void 0, void 0, function () {
              var tableSchema, oldColumn, newColumn;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          tableSchema = undefined;
                          if (!(tableSchemaOrName instanceof TableSchema_1.TableSchema)) return [3 /*break*/, 1];
                          tableSchema = tableSchemaOrName;
                          return [3 /*break*/, 3];
                      case 1: return [4 /*yield*/, this.loadTableSchema(tableSchemaOrName)];
                      case 2:
                          tableSchema = _a.sent();
                          _a.label = 3;
                      case 3:
                          if (!tableSchema)
                              throw new Error("Table " + tableSchemaOrName + " was not found.");
                          oldColumn = undefined;
                          if (oldColumnSchemaOrName instanceof ColumnSchema_1.ColumnSchema) {
                              oldColumn = oldColumnSchemaOrName;
                          }
                          else {
                              oldColumn = tableSchema.columns.find(function (column) { return column.name === oldColumnSchemaOrName; });
                          }
                          if (!oldColumn)
                              throw new Error("Column \"" + oldColumnSchemaOrName + "\" was not found in the \"" + tableSchemaOrName + "\" table.");
                          newColumn = undefined;
                          if (newColumnSchemaOrName instanceof ColumnSchema_1.ColumnSchema) {
                              newColumn = newColumnSchemaOrName;
                          }
                          else {
                              newColumn = oldColumn.clone();
                              newColumn.name = newColumnSchemaOrName;
                          }
                          return [2 /*return*/, this.changeColumn(tableSchema, oldColumn, newColumn)];
                  }
              });
          });
      };
      /**
       * Changes a column in the table.
       */
      WebsqlQueryRunner.prototype.changeColumn = function (tableSchemaOrName, oldColumnSchemaOrName, newColumn) {
          return __awaiter(this, void 0, void 0, function () {
              var tableSchema, oldColumn;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          tableSchema = undefined;
                          if (!(tableSchemaOrName instanceof TableSchema_1.TableSchema)) return [3 /*break*/, 1];
                          tableSchema = tableSchemaOrName;
                          return [3 /*break*/, 3];
                      case 1: return [4 /*yield*/, this.loadTableSchema(tableSchemaOrName)];
                      case 2:
                          tableSchema = _a.sent();
                          _a.label = 3;
                      case 3:
                          if (!tableSchema)
                              throw new Error("Table " + tableSchemaOrName + " was not found.");
                          oldColumn = undefined;
                          if (oldColumnSchemaOrName instanceof ColumnSchema_1.ColumnSchema) {
                              oldColumn = oldColumnSchemaOrName;
                          }
                          else {
                              oldColumn = tableSchema.columns.find(function (column) { return column.name === oldColumnSchemaOrName; });
                          }
                          if (!oldColumn)
                              throw new Error("Column \"" + oldColumnSchemaOrName + "\" was not found in the \"" + tableSchemaOrName + "\" table.");
                          // todo: fix it. it should not depend on tableSchema
                          return [2 /*return*/, this.recreateTable(tableSchema)];
                  }
              });
          });
      };
      /**
       * Changes a column in the table.
       * Changed column looses all its keys in the db.
       */
      WebsqlQueryRunner.prototype.changeColumns = function (tableSchema, changedColumns) {
          return __awaiter(this, void 0, void 0, function () {
              return __generator(this, function (_a) {
                  // todo: fix it. it should not depend on tableSchema
                  return [2 /*return*/, this.recreateTable(tableSchema)];
              });
          });
      };
      /**
       * Drops column in the table.
       */
      WebsqlQueryRunner.prototype.dropColumn = function (table, column) {
          return __awaiter(this, void 0, void 0, function () {
              return __generator(this, function (_a) {
                  return [2 /*return*/, this.dropColumns(table, [column])];
              });
          });
      };
      /**
       * Drops the columns in the table.
       */
      WebsqlQueryRunner.prototype.dropColumns = function (table, columns) {
          return __awaiter(this, void 0, void 0, function () {
              var updatingTableSchema;
              return __generator(this, function (_a) {
                  updatingTableSchema = table.clone();
                  updatingTableSchema.removeColumns(columns);
                  return [2 /*return*/, this.recreateTable(updatingTableSchema)];
              });
          });
      };
      /**
       * Updates table's primary keys.
       */
      WebsqlQueryRunner.prototype.updatePrimaryKeys = function (dbTable) {
          return __awaiter(this, void 0, void 0, function () {
              return __generator(this, function (_a) {
                  return [2 /*return*/, this.recreateTable(dbTable)];
              });
          });
      };
      /**
       * Creates a new foreign key.
       */
      WebsqlQueryRunner.prototype.createForeignKey = function (tableSchemaOrName, foreignKey) {
          return __awaiter(this, void 0, void 0, function () {
              return __generator(this, function (_a) {
                  return [2 /*return*/, this.createForeignKeys(tableSchemaOrName, [foreignKey])];
              });
          });
      };
      /**
       * Creates a new foreign keys.
       */
      WebsqlQueryRunner.prototype.createForeignKeys = function (tableSchemaOrName, foreignKeys) {
          return __awaiter(this, void 0, void 0, function () {
              var tableSchema, changedTableSchema;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0: return [4 /*yield*/, this.getTableSchema(tableSchemaOrName)];
                      case 1:
                          tableSchema = _a.sent();
                          changedTableSchema = tableSchema.clone();
                          changedTableSchema.addForeignKeys(foreignKeys);
                          return [2 /*return*/, this.recreateTable(changedTableSchema)];
                  }
              });
          });
      };
      /**
       * Drops a foreign key from the table.
       */
      WebsqlQueryRunner.prototype.dropForeignKey = function (tableSchemaOrName, foreignKey) {
          return __awaiter(this, void 0, void 0, function () {
              return __generator(this, function (_a) {
                  return [2 /*return*/, this.dropForeignKeys(tableSchemaOrName, [foreignKey])];
              });
          });
      };
      /**
       * Drops a foreign keys from the table.
       */
      WebsqlQueryRunner.prototype.dropForeignKeys = function (tableSchemaOrName, foreignKeys) {
          return __awaiter(this, void 0, void 0, function () {
              var tableSchema, changedTableSchema;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0: return [4 /*yield*/, this.getTableSchema(tableSchemaOrName)];
                      case 1:
                          tableSchema = _a.sent();
                          changedTableSchema = tableSchema.clone();
                          changedTableSchema.removeForeignKeys(foreignKeys);
                          return [2 /*return*/, this.recreateTable(changedTableSchema)];
                  }
              });
          });
      };
      /**
       * Creates a new index.
       */
      WebsqlQueryRunner.prototype.createIndex = function (tableName, index) {
          return __awaiter(this, void 0, void 0, function () {
              var columnNames, sql;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          columnNames = index.columnNames.map(function (columnName) { return "\"" + columnName + "\""; }).join(",");
                          sql = "CREATE " + (index.isUnique ? "UNIQUE " : "") + "INDEX \"" + index.name + "\" ON \"" + tableName + "\"(" + columnNames + ")";
                          return [4 /*yield*/, this.query(sql)];
                      case 1:
                          _a.sent();
                          return [2 /*return*/];
                  }
              });
          });
      };
      /**
       * Drops an index from the table.
       */
      WebsqlQueryRunner.prototype.dropIndex = function (tableName, indexName) {
          return __awaiter(this, void 0, void 0, function () {
              var sql;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          sql = "DROP INDEX \"" + indexName + "\"";
                          return [4 /*yield*/, this.query(sql)];
                      case 1:
                          _a.sent();
                          return [2 /*return*/];
                  }
              });
          });
      };
      /**
       * Truncates table.
       */
      WebsqlQueryRunner.prototype.truncate = function (tableName) {
          return __awaiter(this, void 0, void 0, function () {
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0: return [4 /*yield*/, this.query("DELETE FROM \"" + tableName + "\"")];
                      case 1:
                          _a.sent();
                          return [2 /*return*/];
                  }
              });
          });
      };
      /**
       * Removes all tables from the currently connected database.
       */
      WebsqlQueryRunner.prototype.clearDatabase = function () {
          return __awaiter(this, void 0, void 0, function () {
              var _this = this;
              var selectDropsQuery, dropQueries, error_1, rollbackError_1;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0: 
                      // await this.query(`PRAGMA foreign_keys = OFF;`);
                      return [4 /*yield*/, this.startTransaction()];
                      case 1:
                          // await this.query(`PRAGMA foreign_keys = OFF;`);
                          _a.sent();
                          _a.label = 2;
                      case 2:
                          _a.trys.push([2, 6, , 11]);
                          selectDropsQuery = "select 'drop table \"' || name || '\";' as query from sqlite_master where type = 'table' and name != 'sqlite_sequence'";
                          return [4 /*yield*/, this.query(selectDropsQuery)];
                      case 3:
                          dropQueries = _a.sent();
                          return [4 /*yield*/, Promise.all(dropQueries.map(function (q) { return _this.query(q["query"]); }))];
                      case 4:
                          _a.sent();
                          return [4 /*yield*/, this.commitTransaction()];
                      case 5:
                          _a.sent();
                          return [3 /*break*/, 11];
                      case 6:
                          error_1 = _a.sent();
                          _a.label = 7;
                      case 7:
                          _a.trys.push([7, 9, , 10]);
                          return [4 /*yield*/, this.rollbackTransaction()];
                      case 8:
                          _a.sent();
                          return [3 /*break*/, 10];
                      case 9:
                          rollbackError_1 = _a.sent();
                          return [3 /*break*/, 10];
                      case 10: throw error_1;
                      case 11: return [2 /*return*/];
                  }
              });
          });
      };
      /**
       * Enables special query runner mode in which sql queries won't be executed,
       * instead they will be memorized into a special variable inside query runner.
       * You can get memorized sql using getMemorySql() method.
       */
      WebsqlQueryRunner.prototype.enableSqlMemory = function () {
          this.sqlMemoryMode = true;
      };
      /**
       * Disables special query runner mode in which sql queries won't be executed
       * started by calling enableSqlMemory() method.
       *
       * Previously memorized sql will be flushed.
       */
      WebsqlQueryRunner.prototype.disableSqlMemory = function () {
          this.sqlsInMemory = [];
          this.sqlMemoryMode = false;
      };
      /**
       * Gets sql stored in the memory. Parameters in the sql are already replaced.
       */
      WebsqlQueryRunner.prototype.getMemorySql = function () {
          return this.sqlsInMemory;
      };
      // -------------------------------------------------------------------------
      // Protected Methods
      // -------------------------------------------------------------------------
      /**
       * Parametrizes given object of values. Used to create column=value queries.
       */
      WebsqlQueryRunner.prototype.parametrize = function (objectLiteral, startIndex) {
          if (startIndex === void 0) { startIndex = 0; }
          return Object.keys(objectLiteral).map(function (key, index) { return "\"" + key + "\"" + "=$" + (startIndex + index + 1); });
      };
      /**
       * Builds a query for create column.
       */
      WebsqlQueryRunner.prototype.buildCreateColumnSql = function (column) {
          var c = "\"" + column.name + "\"";
          if (column instanceof ColumnMetadata_1.ColumnMetadata) {
              c += " " + this.driver.normalizeType(column);
          }
          else {
              c += " " + this.connection.driver.createFullType(column);
          }
          if (column.isNullable !== true)
              c += " NOT NULL";
          if (column.isUnique === true)
              c += " UNIQUE";
          if (column.isGenerated === true)
              c += " PRIMARY KEY AUTOINCREMENT";
          if (column.default !== undefined && column.default !== null)
              c += " DEFAULT " + column.default;
          return c;
      };
      WebsqlQueryRunner.prototype.recreateTable = function (tableSchema, oldTableSchema) {
          return __awaiter(this, void 0, void 0, function () {
              var _this = this;
              var columnDefinitions, columnNames, sql1, primaryKeyColumns, oldColumnNames, sql2, sql3, sql4, indexPromises;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          columnDefinitions = tableSchema.columns.map(function (dbColumn) { return _this.buildCreateColumnSql(dbColumn); }).join(", ");
                          columnNames = tableSchema.columns.map(function (column) { return "\"" + column.name + "\""; }).join(", ");
                          sql1 = "CREATE TABLE \"temporary_" + tableSchema.name + "\" (" + columnDefinitions;
                          // if (options && options.createForeignKeys) {
                          tableSchema.foreignKeys.forEach(function (foreignKey) {
                              var columnNames = foreignKey.columnNames.map(function (name) { return "\"" + name + "\""; }).join(", ");
                              var referencedColumnNames = foreignKey.referencedColumnNames.map(function (name) { return "\"" + name + "\""; }).join(", ");
                              sql1 += ", FOREIGN KEY(" + columnNames + ") REFERENCES \"" + foreignKey.referencedTableName + "\"(" + referencedColumnNames + ")";
                              if (foreignKey.onDelete)
                                  sql1 += " ON DELETE " + foreignKey.onDelete;
                          });
                          primaryKeyColumns = tableSchema.columns.filter(function (column) { return column.isPrimary && !column.isGenerated; });
                          if (primaryKeyColumns.length > 0)
                              sql1 += ", PRIMARY KEY(" + primaryKeyColumns.map(function (column) { return "" + column.name; }).join(", ") + ")"; // for some reason column escaping here generate a wrong schema
                          sql1 += ")";
                          // todo: need also create uniques and indices?
                          // recreate a table with a temporary name
                          return [4 /*yield*/, this.query(sql1)];
                      case 1:
                          // todo: need also create uniques and indices?
                          // recreate a table with a temporary name
                          _a.sent();
                          oldColumnNames = oldTableSchema ? oldTableSchema.columns.map(function (column) { return "\"" + column.name + "\""; }).join(", ") : columnNames;
                          sql2 = "INSERT INTO \"temporary_" + tableSchema.name + "\"(" + oldColumnNames + ") SELECT " + oldColumnNames + " FROM \"" + tableSchema.name + "\"";
                          return [4 /*yield*/, this.query(sql2)];
                      case 2:
                          _a.sent();
                          sql3 = "DROP TABLE \"" + tableSchema.name + "\"";
                          return [4 /*yield*/, this.query(sql3)];
                      case 3:
                          _a.sent();
                          sql4 = "ALTER TABLE \"temporary_" + tableSchema.name + "\" RENAME TO \"" + tableSchema.name + "\"";
                          return [4 /*yield*/, this.query(sql4)];
                      case 4:
                          _a.sent();
                          indexPromises = tableSchema.indices.map(function (index) { return _this.createIndex(tableSchema.name, index); });
                          // const uniquePromises = tableSchema.uniqueKeys.map(key => this.createIndex(key));
                          return [4 /*yield*/, Promise.all(indexPromises /*.concat(uniquePromises)*/)];
                      case 5:
                          // const uniquePromises = tableSchema.uniqueKeys.map(key => this.createIndex(key));
                          _a.sent();
                          return [2 /*return*/];
                  }
              });
          });
      };
      /**
       * If given value is a table name then it loads its table schema representation from the database.
       */
      WebsqlQueryRunner.prototype.getTableSchema = function (tableSchemaOrName) {
          return __awaiter(this, void 0, void 0, function () {
              var tableSchema;
              return __generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          if (!(tableSchemaOrName instanceof TableSchema_1.TableSchema)) return [3 /*break*/, 1];
                          return [2 /*return*/, tableSchemaOrName];
                      case 1: return [4 /*yield*/, this.loadTableSchema(tableSchemaOrName)];
                      case 2:
                          tableSchema = _a.sent();
                          if (!tableSchema)
                              throw new Error("Table named " + tableSchemaOrName + " was not found in the database.");
                          return [2 /*return*/, tableSchema];
                  }
              });
          });
      };
      return WebsqlQueryRunner;
  }());
  exports.WebsqlQueryRunner = WebsqlQueryRunner;
  
  //# sourceMappingURL=WebsqlQueryRunner.js.map
  