blob: 0b505530c3ab638079cdb8a62a634eafe4909627 [file]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/**
* Schema Manager Module
*
* Handles metadata registration before performance testing to avoid
* metadata creation overhead during benchmarks. Supports both tree
* and table models.
*/
const { TSDataType } = require('./config');
/**
* Map TSDataType code to IoTDB type string
* @param {number} dataType - TSDataType code
* @returns {string} IoTDB data type string
*/
function getDataTypeString(dataType) {
const typeMap = {
[TSDataType.BOOLEAN]: 'BOOLEAN',
[TSDataType.INT32]: 'INT32',
[TSDataType.INT64]: 'INT64',
[TSDataType.FLOAT]: 'FLOAT',
[TSDataType.DOUBLE]: 'DOUBLE',
[TSDataType.TEXT]: 'TEXT',
[TSDataType.TIMESTAMP]: 'TIMESTAMP',
[TSDataType.DATE]: 'DATE',
[TSDataType.BLOB]: 'BLOB',
[TSDataType.STRING]: 'STRING',
};
return typeMap[dataType] || 'FLOAT';
}
/**
* Create tree model schema
* @param {Object} session - IoTDB session or pool
* @param {Object} testData - Test data structure
* @param {Object} config - Configuration object
*/
async function createTreeModelSchema(session, testData, config) {
console.log('\n=== Creating Tree Model Schema (Using Device Template) ===');
const startTime = Date.now();
try {
// Delete existing storage group if exists
console.log(`Deleting existing storage group: ${config.STORAGE_GROUP_PREFIX}...`);
try {
await session.executeNonQueryStatement(
`DROP DATABASE ${config.STORAGE_GROUP_PREFIX}`
);
console.log(' ✓ Existing storage group deleted');
} catch (error) {
if (error.message && (error.message.includes('does not exist') || error.message.includes('not exist'))) {
console.log(' ℹ Storage group does not exist, will create new one');
} else {
console.log(` Delete storage group: ${error.message}`);
}
}
// Create storage group
console.log(`Creating storage group: ${config.STORAGE_GROUP_PREFIX}...`);
await session.executeNonQueryStatement(
`CREATE DATABASE ${config.STORAGE_GROUP_PREFIX}`
);
console.log(' ✓ Storage group created');
// Use device template for fast schema creation
const templateName = 'benchmark_template';
// Step 1: Drop existing template if exists
console.log(`Dropping existing device template: ${templateName}...`);
try {
await session.executeNonQueryStatement(
`DROP DEVICE TEMPLATE ${templateName}`
);
console.log(' ✓ Existing template dropped');
} catch (error) {
console.log(' ℹ Template does not exist, will create new one');
}
// Step 2: Create device template with measurements from first device
// All devices have same structure in benchmark
if (testData.devices && testData.devices.length > 0) {
const device = testData.devices[0];
// Build template creation SQL
const measurements = [];
for (let i = 0; i < device.measurements.length; i++) {
const measurement = device.measurements[i];
const dataType = device.dataTypes[i];
const typeString = getDataTypeString(dataType);
measurements.push(`${measurement} ${typeString}`);
}
const createTemplateSQL = `CREATE DEVICE TEMPLATE ${templateName} (${measurements.join(', ')})`;
console.log(`Creating device template with ${device.measurements.length} measurements...`);
console.log(`SQL: ${createTemplateSQL}`);
await session.executeNonQueryStatement(createTemplateSQL);
console.log(' ✓ Device template created');
// Step 3: Set template to storage group
console.log(`Setting template to storage group: ${config.STORAGE_GROUP_PREFIX}...`);
await session.executeNonQueryStatement(
`SET DEVICE TEMPLATE ${templateName} TO ${config.STORAGE_GROUP_PREFIX}`
);
console.log(' ✓ Template set to storage group');
// Step 4: Template will be auto-activated when data is inserted
// No need to manually activate for each device
console.log(`Template will be auto-activated for ${testData.devices.length} devices on first insert`);
const duration = Date.now() - startTime;
console.log(`\n Schema creation completed in ${(duration / 1000).toFixed(2)}s`);
console.log(` Using device template for ${testData.devices.length} devices`);
console.log(` Total measurements per device: ${device.measurements.length}`);
} else {
throw new Error('No devices in test data');
}
} catch (error) {
console.error('✗ Schema creation failed:', error);
throw error;
}
}
/**
* Create table model schema
* @param {Object} session - IoTDB session or pool
* @param {Object} testData - Test data structure
* @param {Object} config - Configuration object
*/
async function createTableModelSchema(session, testData, config) {
console.log('\n=== Creating Table Model Schema ===');
const startTime = Date.now();
try {
// Delete existing database if exists
console.log(`Deleting existing database: ${config.DATABASE_NAME}...`);
try {
await session.executeNonQueryStatement(
`DROP DATABASE ${config.DATABASE_NAME}`
);
console.log(' ✓ Existing database deleted');
} catch (error) {
if (error.message && (error.message.includes('does not exist') || error.message.includes('not exist'))) {
console.log(' ℹ Database does not exist, will create new one');
} else {
console.log(` Delete database: ${error.message}`);
}
}
// Create database
console.log(`Creating database: ${config.DATABASE_NAME}...`);
await session.executeNonQueryStatement(
`CREATE DATABASE ${config.DATABASE_NAME}`
);
console.log(' ✓ Database created');
// Use database
await session.executeNonQueryStatement(`USE ${config.DATABASE_NAME}`);
console.log(' ✓ Using database');
// Drop existing table if exists
try {
await session.executeNonQueryStatement(`DROP TABLE ${config.TABLE_NAME}`);
console.log(' ✓ Dropped existing table');
} catch (error) {
// Ignore if table doesn't exist
}
// Build CREATE TABLE statement from device structure
const columns = ['device_id STRING TAG']; // device_id is TAG for identification
// Add sensor columns from first device (all devices have same structure)
if (testData.devices && testData.devices.length > 0) {
const device = testData.devices[0];
for (let i = 0; i < device.measurements.length; i++) {
const measurementName = device.measurements[i];
const dataType = device.dataTypes[i];
const typeString = getDataTypeString(dataType);
// All sensors are FIELD columns (measurement values)
columns.push(`${measurementName} ${typeString} FIELD`);
}
}
const createTableSQL = `CREATE TABLE ${config.TABLE_NAME} (${columns.join(', ')})`;
console.log(`Creating table with ${columns.length} columns...`);
console.log(`SQL: ${createTableSQL}`);
await session.executeNonQueryStatement(createTableSQL);
console.log(' ✓ Table created');
const duration = Date.now() - startTime;
console.log(`\n Schema creation completed in ${(duration / 1000).toFixed(2)}s`);
} catch (error) {
console.error('✗ Schema creation failed:', error);
throw error;
}
}
/**
* Clean up test schema
* @param {Object} session - IoTDB session or pool
* @param {string} model - 'tree' or 'table'
* @param {Object} config - Configuration object
*/
async function cleanupSchema(session, model, config) {
console.log('\n=== Cleaning Up Schema ===');
try {
if (model === 'tree') {
console.log(`Dropping storage group: ${config.STORAGE_GROUP_PREFIX}...`);
try {
await session.executeNonQueryStatement(
`DELETE DATABASE ${config.STORAGE_GROUP_PREFIX}.*`
);
console.log(' ✓ Storage group deleted');
} catch (error) {
console.log(' ℹ Storage group does not exist or already deleted');
}
} else if (model === 'table') {
console.log(`Dropping database: ${config.DATABASE_NAME}...`);
try {
await session.executeNonQueryStatement(
`DROP DATABASE ${config.DATABASE_NAME}`
);
console.log(' ✓ Database dropped');
} catch (error) {
console.log(' ℹ Database does not exist or already deleted');
}
}
} catch (error) {
console.error('✗ Cleanup failed:', error.message);
// Don't throw - cleanup is best effort
}
}
/**
* Verify schema creation
* @param {Object} session - IoTDB session or pool
* @param {string} model - 'tree' or 'table'
* @param {Object} config - Configuration object
* @returns {boolean} True if schema exists
*/
async function verifySchema(session, model, config) {
console.log('\n=== Verifying Schema ===');
try {
if (model === 'tree') {
// Check if storage group exists
const result = await session.executeQueryStatement('SHOW DATABASES');
const databases = [];
while (await result.hasNext()) {
const row = result.next();
databases.push(row.getFields()[0]);
}
await result.close();
const exists = databases.some(db =>
db === config.STORAGE_GROUP_PREFIX || db.startsWith(config.STORAGE_GROUP_PREFIX + '.')
);
if (exists) {
console.log(` Storage group ${config.STORAGE_GROUP_PREFIX} exists`);
return true;
} else {
console.log(` Storage group ${config.STORAGE_GROUP_PREFIX} not found`);
return false;
}
} else if (model === 'table') {
// Check if database exists
const result = await session.executeQueryStatement('SHOW DATABASES');
const databases = [];
while (await result.hasNext()) {
const row = result.next();
databases.push(row.getFields()[0]);
}
await result.close();
const exists = databases.includes(config.DATABASE_NAME);
if (exists) {
console.log(` Database ${config.DATABASE_NAME} exists`);
return true;
} else {
console.log(` Database ${config.DATABASE_NAME} not found`);
return false;
}
}
} catch (error) {
console.error('✗ Verification failed:', error.message);
return false;
}
return false;
}
module.exports = {
createTreeModelSchema,
createTableModelSchema,
cleanupSchema,
verifySchema,
getDataTypeString,
};