<commit_before>/**
 *
 * @license
 * Copyright (C) 2016 <NAME>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author <NAME>
 * @file Database.js
 * @providesModule Database
 * @description Provides interations with the application database.
 *
 * @flow
 */
'use strict';

// React Imports
import {
  AsyncStorage,
} from 'react-native';

// Imports
const Promise = require('promise');
const SQLite = require('react-native-sqlite-storage');

// Name of the database
const DB_NAME: string = 'CampusGuideData.db';
// Current version of the database. Update when changes are made
const DB_VERSION: number = 1;
// Identifies the database version, stored in the local storage
const DB_VERSION_KEY: string = 'db_version';
// Display name of the database
const DB_DISPLAY_NAME: string = 'Campus Guide Database';
// TODO: determine sufficient database size
// Size of the database
const DB_SIZE: number = 200000;

// Instance of the database
let db: ?SQLite = null;
// Current version of the database
let dbCurrentVersion: number = 0;
// Indicates if the database is currently being initialized
let dbInitializing: boolean = false;

// List of promises that should resolve or reject when the database opens or fails, respectively
const initPromises: Array < { resolve: () => any, reject: () => any } > = [];

// Set mode for SQLite
SQLite.DEBUG(true); // TODO: disable for release?
SQLite.enablePromise(true);

/**
 * Initialize and update the database.
 *
 * @param {Object} Database the Database module
 */
async function _init(): Promise < void > {

  // Get version of the database
  dbCurrentVersion = 0;
  try {
    const value = await AsyncStorage.getItem(DB_VERSION_KEY);
    dbCurrentVersion = (value === null) ? 0 : parseInt(value);
  } catch (e) {
    console.error('Error getting database version.', e);
  }

  // Test SQLite functionality
  try {
    await SQLite.echoTest();
  } catch (e) {
    console.error('Error - SQLite plugin not functional.', e);
    throw e;
  }

  try {
    db = await SQLite.openDatabase(DB_NAME, DB_VERSION, DB_DISPLAY_NAME, DB_SIZE);
    await _upgradeDatabase(DB_VERSION);
    _openSuccess();
  } catch (e) {
    _openError(e);
    throw e;
  }
}

/**
 * Upgrades the database based on the current version and the new version
 *
 * @param {number} newVersion version of the database to update to
 */
async function _upgradeDatabase(newVersion: number): Promise < void > {
  if (db && dbCurrentVersion < newVersion) {

    /* Ignore numbers used for database versioning */
    /* eslint-disable no-magic-numbers */

    try {
      switch (dbCurrentVersion) {
        case 0:
          await db.transaction(_createDatabase);
          break;
        default:
          // does nothing
      }
    } catch (e) {
      throw e;
    }

    /* eslint-enable no-magic-numbers */

    // Update database version
    await AsyncStorage.setItem(DB_VERSION_KEY, dbCurrentVersion + 1);

    if (dbCurrentVersion !== 0) {
      const upgrade: string = (String:any).format(
        'Successfully upgraded database from {1} to {2}.',
        dbCurrentVersion,
        dbCurrentVersion + 1
      );
      console.log(upgrade);

      dbCurrentVersion++;
      await _upgradeDatabase(db, DB_VERSION);
    }
  }
}

/**
 * Creates the database.
 *
 * @param {any} tx database transaction
 */
function _createDatabase(tx: any): void {
  // Clear the existing database
  tx.executeSql('PRAGMA foreign_keys = ON;');
  tx.executeSql('DROP TABLE IF EXISTS Semesters;');
  tx.executeSql('DROP TABLE IF EXISTS Courses;');
  tx.executeSql('DROP TABLE IF EXISTS Sessions;');
  tx.executeSql('DROP TABLE IF EXISTS Config;');

  // Create the Semesters table
  tx.executeSql(
    'CREATE TABLE IF NOT EXISTS Semesters ('<commit_msg>Use proper typing for saving database version<commit_after>/**
 *
 * @license
 * Copyright (C) 2016 <NAME>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author <NAME>
 * @file Database.js
 * @providesModule Database
 * @description Provides interations with the application database.
 *
 * @flow
 */
'use strict';

// React Imports
import {
  AsyncStorage,
} from 'react-native';

// Imports
const Promise = require('promise');
const SQLite = require('react-native-sqlite-storage');

// Name of the database
const DB_NAME: string = 'CampusGuideData.db';
// Current version of the database. Update when changes are made
const DB_VERSION: number = 1;
// Identifies the database version, stored in the local storage
const DB_VERSION_KEY: string = 'db_version';
// Display name of the database
const DB_DISPLAY_NAME: string = 'Campus Guide Database';
// TODO: determine sufficient database size
// Size of the database
const DB_SIZE: number = 200000;

// Instance of the database
let db: ?SQLite = null;
// Current version of the database
let dbCurrentVersion: number = 0;
// Indicates if the database is currently being initialized
let dbInitializing: boolean = false;

// List of promises that should resolve or reject when the database opens or fails, respectively
const initPromises: Array < { resolve: () => any, reject: () => any } > = [];

// Set mode for SQLite
SQLite.DEBUG(true); // TODO: disable for release?
SQLite.enablePromise(true);

/**
 * Initialize and update the database.
 *
 * @param {Object} Database the Database module
 */
async function _init(): Promise < void > {

  // Get version of the database
  dbCurrentVersion = 0;
  try {
    const value = await AsyncStorage.getItem(DB_VERSION_KEY);
    dbCurrentVersion = (value === null) ? 0 : parseInt(value);
  } catch (e) {
    console.error('Error getting database version.', e);
  }

  // Test SQLite functionality
  try {
    await SQLite.echoTest();
  } catch (e) {
    console.error('Error - SQLite plugin not functional.', e);
    throw e;
  }

  try {
    db = await SQLite.openDatabase(DB_NAME, DB_VERSION, DB_DISPLAY_NAME, DB_SIZE);
    await _upgradeDatabase(DB_VERSION);
    _openSuccess();
  } catch (e) {
    _openError(e);
    throw e;
  }
}

/**
 * Upgrades the database based on the current version and the new version
 *
 * @param {number} newVersion version of the database to update to
 */
async function _upgradeDatabase(newVersion: number): Promise < void > {
  if (db && dbCurrentVersion < newVersion) {

    /* Ignore numbers used for database versioning */
    /* eslint-disable no-magic-numbers */

    try {
      switch (dbCurrentVersion) {
        case 0:
          await db.transaction(_createDatabase);
          break;
        default:
          // does nothing
      }
    } catch (e) {
      throw e;
    }

    /* eslint-enable no-magic-numbers */

    // Update database version
    dbCurrentVersion++;
    await AsyncStorage.setItem(DB_VERSION_KEY, dbCurrentVersion.toString());

    if (dbCurrentVersion !== 0) {
      const upgrade: string = (String:any).format(
        'Successfully upgraded database from {1} to {2}.',
        dbCurrentVersion - 1,
        dbCurrentVersion
      );
      console.log(upgrade);

      await _upgradeDatabase(db, DB_VERSION);
    }
  }
}

/**
 * Creates the database.
 *
 * @param {any} tx database transaction
 */
function _createDatabase(tx: any): void {
  // Clear the existing database
  tx.executeSql('PRAGMA foreign_keys = ON;');
  tx.executeSql('DROP TABLE IF EXISTS Semesters;');
  tx.executeSql('DROP TABLE IF EXISTS Courses;');
  tx.executeSql('DROP TABLE IF EXISTS Sessions;');
  tx.executeSql('DROP TABLE IF EXISTS Config;');

  // Create the Semesters table
  tx.executeSql(
    'CREATE TABLE IF NOT EXISTS Semesters ('
{'fraction_non_alphanumeric': 0.1072473949186278, 'fraction_numerical': 0.005502868516567147, 'mean_word_length': 3.3382427628237683, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4583031', 'n_tokens_mistral': 2484, 'n_tokens_neox': 2406, 'n_words': 885}