Home Reference Source

src/SQLite3/SQLite3Type.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" />

/**
 * データベースのレコードの列情報
 * @typedef {Object} SQLite3TypeData
 * @property {number} cid 列番号
 * @property {string} name 列名
 * @property {string} type 型名
 * @property {number} size 型のサイズ
 * @property {string|null} dflt_value 未設定は`null`, 設定されている場合は文字列
 * @property {boolean} is_not_null `null` を許してよいか
 */

/**
 * データベース内のテーブルの列情報
 * @requires SenkoWSH
 */
export default class SQLite3Type {

	/**
	 * データベース内のテーブルの列情報を初期設定する
	 * - `create` を使用して作成すること
	 * 
	 * @param {SQLite3TypeData} info_data
	 * @private
	 */
	constructor(info_data) {

		/**
		 * 列情報
		 */
		this.info = info_data;

		// SQLite には扱える型名が複数あるため正規化した型名を作成
		{
			let type = "undefined";
			// https://www.sqlite.org/datatype3.html
			// TEXT, VARCHAR
			if(/char|clob|text|decimal/i.test(this.info.type)) {
				type = "string";
			}
			// NUMERIC
			else if(/numeric/i.test(this.info.type)) {
				type = "numeric";
			}
			// INTEGER
			else if(/int/i.test(this.info.type)) {
				type = "int";
			}
			// REAL
			else if(/double|float|real/i.test(this.info.type)) {
				type = "real";
			}
			// BLOB
			else if(/blob/i.test(this.info.type)) {
				type = "blob";
			}
			// BOOLEAN(NUMERIC)
			else if(/boolean/i.test(this.info.type)) {
				type = "boolean";
			}
			// DATETIME(NUMERIC)
			else if(/date|datetime/i.test(this.info.type)) {
				type = "datetime";
			}
			// NONE(NUMERIC)
			else if(/none/i.test(this.info.type)) {
				type = "none";
			}

			/**
			 * 正規化した型名
			 */
			this.normalized_type = type;
		}
	}

	/**
	 * `SQLite3Type` を作成する
	 * `-json` で `pragma table_info(x);` で取得した1レコードデータを引数に取る
	 * 
	 * @param {import("./SQLite3Schema").SQLite3TableInfo} table_info_record 
	 */
	static create(table_info_record) {
		const cid = table_info_record.cid;
		const name = table_info_record.name;
		// type_data が未入力の場合はNONE として扱う
		const type_data = table_info_record.type.match(/[^(]+/);
		const type = type_data ? type_data[0] : "NONE";
		// 未定義なら null 存在する場合は '0' のように ' で囲まれている。
		let default_value = table_info_record.dflt_value;
		if(default_value !== null) {
			// 除去する
			if(/^'.*'$/.test(default_value)) {
				default_value = default_value.match(/^'(.*)'$/)[1];
			} 
		}
		const dflt_value = default_value;
		const is_not_null = table_info_record.notnull !== 0;
		const size_data = table_info_record.type.match(/\(([0-9]+)\)/);
		const size = size_data ? Number.parseInt(size_data[1], 10) : -1;
		return new SQLite3Type({
			cid : cid,
			name : name,
			type : type,
			size : size,
			dflt_value : dflt_value,
			is_not_null : is_not_null
		});
	}

	/**
	 * 型情報を取得する
	 * 
	 * @returns {SQLite3TypeData}
	 */
	getType() {
		return {
			cid : this.info.cid,
			name : this.info.name,
			type : this.info.type,
			size : this.info.size,
			dflt_value : this.info.dflt_value,
			is_not_null : this.info.is_not_null
		};
	}

	/**
	 * JavaScript用のデータをSQL文で使用できる文字列へ変換
	 * - `SQL` の型情報を元に `SQL` 内への記載用データへ変換
	 * - 文字列データはシングルクォーテーションを付けた文字列を返す
	 * - 数値データなどはシングルクォーテーション無しの文字列型を返す
	 * 
	 * @param {any} x
	 * @returns {string}
	 */
	toSQLDataFromJSData(x) {
		const td = this.info;
		const js_type = System.typeOf(x);
		if(this.normalized_type === "string") {
			/**
			 * @type {string}
			 */
			const str = x.toString();
			if(td.size === -1) {
				return "'" + str + "'";
			}
			else {
				return "'" + str.slice(0, td.size) + "'";
			}
		}
		else if((this.normalized_type === "numeric") || (this.normalized_type === "none")) {
			if(js_type !== "number") {
				return "'" + x + "'";
			}
			return x.toString();
		}
		else if(this.normalized_type === "int") {
			let num = 0;
			if(js_type !== "number") {
				num = Number.parseFloat(x);
			}
			else {
				num = x;
			}
			if(!isFinite(num)) {
				num = 0;
			}
			return Math.trunc(num).toString();
		}
		else if(this.normalized_type === "real") {
			if(js_type !== "number") {
				return Number.parseFloat(x).toString();
			}
			return x.toString();
		}
		else if(this.normalized_type === "blob") {
			return "null";
		}
		else if(this.normalized_type === "boolean") {
			return !!x ? "1" : "0";
		}
		else if(this.normalized_type === "datetime") {
			const date = new Date(x);
			const date_text = Format.textf("%04d-%02d-%02d %02d:%02d:%02d",
				date.getUTCFullYear(),
				date.getUTCMonth() + 1,
				date.getUTCDate(),
				date.getUTCHours(),
				date.getUTCMinutes(),
				date.getUTCSeconds()
			);
			return "'" + date_text + "'";
		}
		console.log("Error : toSQLDataFromJSData " + x);
		return "null";
	}

	/**
	 * SQLで取得したデータをJavaScript用のデータへ変換
	 * - 「`-json` で取得し `eval` で変換したデータ」から `SQL` の型情報を元に `JavaScript` の型へ変換
	 * 
	 * @param {any} x
	 * @returns {any}
	 */
	toJSDataFromSQLData(x) {
		const js_type = System.typeOf(x);
		if(js_type === "null") {
			return null;
		}
		else if(this.normalized_type === "string") {
			if(js_type === "string") {
				return x;
			}
			else if(js_type === "object") {
				return null;
			}
			else {
				return x.toString();
			}
		}
		else if((this.normalized_type === "numeric") || (this.normalized_type === "none")) {
			if(js_type === "object") {
				return null;
			}
			const number = Number.parseFloat(x);
			if(Number.isFinite(number)) {
				return number;
			}
			if(x.toString() === number.toString()) {
				return number;
			}
			return x.toString();
		}
		else if(this.normalized_type === "int") {
			if(js_type === "object") {
				return null;
			}
			return Number.parseInt(x, 10);
		}
		else if(this.normalized_type === "real") {
			if(js_type === "object") {
				return null;
			}
			return Number.parseFloat(x);
		}
		else if(this.normalized_type === "blob") {
			return {};
		}
		else if(this.normalized_type === "boolean") {
			const number = Number.parseFloat(x);
			return number !== 0;
		}
		else if(this.normalized_type === "datetime") {
			/**
			 * @type {string}
			 */
			const date = x.toString();
			return new Date(date.replace(/\-/g, "/"));
		}
		return null;
	}

}