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;
}
}