component { /** * HSQL 1.8 * Принимает Lucee Query, создает в HSQL временную таблицу и льет в нее данные * @name Имя таблицы, которое ты хочешь использовать в SQL * @data Сам запрос (результат queryExecute или cfhttp) * @dsn Имя datasource для HSQL (по умолчанию "inmem") */ public string function register(required string name, required query data, string dsn = "inmem") { var cols = arguments.data.columnList; var meta = getMetaData(arguments.data); //writedump(meta); var colDefs = []; var colTypes = {}; var params = []; // 1. Генерируем "CREATE TEMP TABLE name (col1 TYPE, col2 TYPE...)" for (var i = 1; i <= arrayLen(meta); i++) { var colName = meta[i].name; // Маппинг типов Lucee -> типы H2 var dbType = mapType(meta[i].typeName); arrayAppend(colDefs, "#colName# #dbType#"); colTypes[colName] = meta[i].typeName; } //writedump(colDefs); //var checkSql = "SELECT * FROM INFORMATION_SCHEMA.SYSTEM_TABLES WHERE TABLE_NAME = '#ucase(arguments.name)#';" //var res = queryExecute(checkSql, {}, { datasource: arguments.dsn }); //if (res.recordCount EQ 0) { var createSql = "DROP TABLE IF EXISTS #arguments.name#; CREATE TEMPORARY TABLE #arguments.name#(#arrayToList(colDefs, ', ')#) ON COMMIT PRESERVE ROWS"; queryExecute(createSql, {}, { datasource: arguments.dsn }); //} // 2. Цикл вставки var insertSql = "INSERT INTO #arguments.name# (#cols#) VALUES (#repeatString("?,", listLen(cols)-1)#?)"; for (var row in arguments.data) { var rowData = []; for (var col in cols) { arrayAppend(rowData, formatValue(row[col], colTypes[col])); } queryExecute(insertSql, rowData, { datasource: arguments.dsn }); } return arguments.name; } /** * Удалить временные таблицы (опционально, база сама почистит при закрытии коннекта) */ public void function drop(required string names, string dsn = "inmem") { var list = arguments.names; if (!isArray(list)) list = [list]; for (var name in list) { queryExecute("DROP TABLE IF EXISTS #name#", {}, { datasource: arguments.dsn }); } } // --- Вспомогательная функция маппинга типов --- private string function mapType(required string luceeType) { switch (arguments.luceeType) { case "VARCHAR": case "CHAR": case "LONGVARCHAR": return "LONGVARCHAR"; case "INTEGER": case "BIGINT": case "SMALLINT": case "TINYINT": return "INT"; case "DOUBLE": case "FLOAT": case "REAL": return "DOUBLE"; case "BOOLEAN": case "BIT": return "BIT"; case "DECIMAL": case "NUMERIC": return "DECIMAL(20,6)"; default: return "LONGVARCHAR"; // И тут тоже } } private function formatValue(required any val, required string targetType) { var isTextType = listFindNoCase("VARCHAR,CHAR,LONGVARCHAR", arguments.targetType); // 1. Обработка пустых значений if (isNull(arguments.val) || (isSimpleValue(arguments.val) && len(trim(arguments.val)) == 0)) { // Если это текстовое поле — сохраняем пустую строку как есть if (isTextType) { return ""; } // Для чисел, дат и булев — отдаем настоящий SQL NULL return javaCast("null", ""); } // 2. Форматирование непустых дат if (listFindNoCase("DATE,TIMESTAMP,TIME", arguments.targetType)) { return dateFormat(arguments.val, "yyyy-mm-dd") & " " & timeFormat(arguments.val, "HH:mm:ss"); } // 3. Числа и непустой текст отдаем как есть return arguments.val; } }