Home Reference Source

src/SQLite3/SQLite3.js

/**
 * The script is part of toolbox-wsh.
 * 
 * AUTHOR:
 *  natade (http://twitter.com/natadea)
 * 
 * LICENSE:
 *  The MIT license https://opensource.org/licenses/MIT
 */

/// <reference path="../../include/SenkoWSH.d.ts" />

import SQLite3Schema from "./SQLite3Schema.js";
import SQLite3IF from "./SQLite3IF.js";

/**
 * SQL のタイムアウト設定
 */
const SQL_TIME_OUT = ".timeout 1000\n";

/**
 * WSH で SQLite3 を使用するためのライブラリ
 * @requires SenkoWSH
 */
export default class SQLite3 {
	
	/**
	 * 利用するデータベースをセット
	 * @param {SFile} db_file DBファイル
	 * @returns {Object<string, SQLite3IF>} テーブル操作用データ
	 */
	static use(db_file) {
		/**
		 * @type {Object<string, SQLite3IF>}
		 */
		const output = {};

		// テーブル名のリストを作成
		const table_name = SQLite3.execSQL(db_file, ".tables", "-readonly");
		if(!table_name) {
			console.log("Error : tables " + db_file);
			return null;
		}
		const table_name_list = table_name.trim().split(/\s+/);

		// テーブル内の列データを作成する
		/**
		 * 列名
		 * @type {string[]}
		 */
		const table_info_sql = [];
		for(let i = 0; i < table_name_list.length; i++) {
			const key = table_name_list[i];
			table_info_sql.push("pragma table_info(" + key + ");");
		}

		// テーブル内の列データを全て取得する
		const table_info_data = SQLite3.execSQL(db_file, table_info_sql.join(""), "-readonly -json");
		if(!table_info_data) {
			console.log("Error : table_info " + db_file);
			return null;
		}
		// []で括られた1テーブルごとのJSON情報から、1テーブルずつ抜き出して、データを格納する

		/**
		 * 列情報
		 * @type {SQLite3Schema[]}
		 */
		const type_obj_list = [];

		/**
		 * @param {string} table_info_text
		 * @returns {string}
		 * @private
		 */
		const create_table_info = function(table_info_text) {
			type_obj_list.push(SQLite3Schema.create(table_info_text));
			return "";
		}
		table_info_data.replace(/(\[[^\]]+\])/g, create_table_info);

		// IFを作成していく
		for(let i = 0; i < table_name_list.length; i++) {
			const key = table_name_list[i];
			const data = new SQLite3IF(db_file, key, type_obj_list[i]);
			output[key] = data;
		}
		return output;
	}

	/**
	 * データベースをバキュームする
	 * @param {SFile} db_file DBファイル
	 * @returns {boolean}
	 */
	static vaccum(db_file) {
		return SQLite3.execSQL(db_file, "vacuum;") !== null;
	}

	/**
	 * SQL文を実行する
	 * @param {SFile} db_file DBファイル
	 * @param {string} sql SQL文
	 * @param {string} [option] 実行時のオプション
	 * @returns {string | null} SQL実行結果
	 */
	 static execSQL(db_file, sql, option) {
		if(!SQLite3.sqlite3) {
			console.log("Error : execSQL " + db_file + "," + sql);
			return null;
		}
		const option_ = option !== undefined ? option : "";
		const sql_file = SFile.createTempFile();
		const sqt_text = SQL_TIME_OUT + sql;
		sql_file.writeString(sqt_text, "utf-8", "\r\n", false);
		const script = "\"" + SQLite3.sqlite3.getAbsolutePath() + "\" \"" + db_file.getAbsolutePath() + "\" " + option_ + " < \"" + sql_file.getAbsolutePath() + "\"";
		const output = System.execBatchScript(script, "utf-8");
		sql_file.remove();
		if(output.exit_code) {
			console.log(output.error);
			return null;
		}
		return output.out;
	 }

	/**
	 * ツールをインストールする
	 * - `./lib/sqlite3.exe` を利用する
	 * - ファイルが存在しない場合は自動でダウンロードする
	 * - 本ライブラリを使用する場合は、`setSQLite3` でツールを設定するか、`install` でダウンロードする必要がある
	 * 
	 * @returns {boolean}
	 */
	static install() {
		if(SQLite3.sqlite3) {
			return true;
		}
		const exe_path = "./lib/sqlite3.exe";
		const exe_file = new SFile(exe_path);
		if(exe_file.isFile()) {
			// ファイルがある場合はそれを設定する
			return SQLite3.setSQLite3(exe_file);
		}
		else {
			// ファイルがない場合は、ダウンロードしてくれる
			new SFile("./lib").mkdirs();
			const download_url = "https://www.sqlite.org/download.html";
			const download_text = new SFile(download_url).readString();
			if(download_text) {
				const match_data = download_text.match(/'[^']+\/sqlite-tools-win32[^']+'/);
				if(match_data) {
					const match_text = match_data[0].toString();
					const zip_url = "https://www.sqlite.org/" + match_text.substr(1, match_text.length - 2);
					const zip_binary = new SFile(zip_url).readBinary();
					if(zip_binary.length !== 0) {
						const temp_file_1 = SFile.createTempFile();
						temp_file_1.mkdirs();
						const zip_file = new SFile(temp_file_1 + "\\" + new SFile(zip_url).getName());
						zip_file.writeBinary(zip_binary);
						const temp_file_2 = SFile.createTempFile();
						SFile.extract(zip_file, temp_file_2);
						/**
						 * @type {SFile}
						 */
						const sqlite3_file = SFile.findFile(temp_file_2, /\\sqlite3.exe$/i);
						if(sqlite3_file) {
							sqlite3_file.move("./lib");
						}
						temp_file_1.remove();
						temp_file_2.remove();
						return SQLite3.setSQLite3(sqlite3_file);
					}
				}
			}
		}
		return false;
	}

	/**
	 * ツールを設定する
	 * - `*.db` を操作するための `sqlite-tools-win32` に含まれる `sqlite3.exe` を設定する
	 * @param {SFile|string} sqlite_tool_path
	 * @returns {boolean}
	 */
	static setSQLite3(sqlite_tool_path) {
		const exe_file = new SFile(sqlite_tool_path);
		if(!exe_file.isFile() || !/sqlite3.exe$/i.test(exe_file.getAbsolutePath()) ) {
			return false;
		}
		SQLite3.sqlite3 = exe_file;
		return true;
	}
	
}

/**
 *  `*.db` を操作するための `sqlite-tools-win32` に含まれる `sqlite3.exe`
 * @type {SFile}
 */
SQLite3.sqlite3 = null;