@proddata/node-cratedb
Version:
Node.js client for CrateDB
143 lines • 6.26 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.StatementGenerator = void 0;
const Serializer_js_1 = require("./Serializer.js");
class StatementGenerator {
static createTable(tableName, schema, options) {
// Validate column definitions
Object.entries(schema).forEach(([col, definition]) => {
if ('type' in definition && definition.type !== 'object') {
if (definition.defaultValue !== undefined &&
definition.generatedAlways) {
throw new Error(`Column "${col}" cannot have both DEFAULT and GENERATED ALWAYS values`);
}
}
});
// Build column definitions
const columns = Object.entries(schema)
.map(([col, definition]) => this.buildColumnDefinition(col, definition))
.join(', ');
// Build primary key clause if any
const primaryKeys = Object.keys(schema)
.filter((col) => schema[col].primaryKey)
.map((col) => `"${col}"`)
.join(', ');
const primaryKeyClause = primaryKeys ? `, PRIMARY KEY(${primaryKeys})` : '';
// Start query construction
let query = `CREATE TABLE ${this.quoteIdentifier(tableName)} (${columns}${primaryKeyClause})`;
// Partition clause
if (options?.partitionedBy?.length) {
const partitionCols = options.partitionedBy.map((col) => `"${col}"`).join(', ');
query += ` PARTITIONED BY (${partitionCols})`;
}
// Cluster clause
if (options?.clusteredBy || options?.numberOfShards) {
if (options.clusteredBy && options.numberOfShards) {
query += ` CLUSTERED BY ("${options.clusteredBy}") INTO ${options.numberOfShards} SHARDS`;
}
else if (options.clusteredBy) {
query += ` CLUSTERED BY ("${options.clusteredBy}")`;
}
else if (options.numberOfShards) {
query += ` CLUSTERED INTO ${options.numberOfShards} SHARDS`;
}
}
// Replicas clause
if (options?.numberOfReplicas !== undefined) {
query += ` WITH (number_of_replicas = '${options.numberOfReplicas}')`;
}
return query + ';';
}
static dropTable(tableName) {
return `DROP TABLE IF EXISTS ${this.quoteIdentifier(tableName)};`;
}
static insert(tableName, keys, primaryKeys) {
const placeholders = keys.map(() => '?').join(', ');
const columns = keys.map((key) => `"${key}"`).join(', ');
let query = `INSERT INTO ${this.quoteIdentifier(tableName)} (${columns}) VALUES (${placeholders})`;
if (primaryKeys && primaryKeys.length > 0) {
const nonPrimaryKeys = keys.filter((key) => !primaryKeys.includes(key));
const updates = nonPrimaryKeys.map((key) => `"${key}" = excluded."${key}"`).join(', ');
const pkClause = primaryKeys.map((key) => `"${key}"`).join(', ');
query += ` ON CONFLICT (${pkClause}) DO UPDATE SET ${updates}`;
}
else {
query += ' ON CONFLICT DO NOTHING';
}
return query + ';';
}
static refresh(tableName) {
return `REFRESH TABLE ${this.quoteIdentifier(tableName)};`;
}
static optimize(tableName, options, partitions) {
let query = `OPTIMIZE TABLE ${this.quoteIdentifier(tableName)}`;
// Build options clause
if (options && Object.keys(options).length > 0) {
const optionsClauses = Object.entries(options)
.map(([key, value]) => `${key}=${Serializer_js_1.Serializer.serialize(value)}`)
.join(', ');
query += ` WITH (${optionsClauses})`;
}
// Build partitions clause
if (partitions && Object.keys(partitions).length > 0) {
const partitionClauses = Object.entries(partitions)
.map(([key, value]) => {
const val = typeof value === 'string' ? `'${value}'` : value;
return `${key}=${val}`;
})
.join(', ');
query += ` PARTITION (${partitionClauses})`;
}
return query + ';';
}
static getPrimaryKeys() {
return `
SELECT
column_name
FROM
information_schema.table_constraints tc
JOIN information_schema.key_column_usage kc
ON kc.table_catalog = tc.table_catalog
AND kc.table_schema = tc.table_schema
AND kc.table_name = tc.table_name
AND kc.constraint_name = tc.constraint_name
WHERE tc.constraint_type = 'PRIMARY KEY'
AND tc.table_schema = ?
AND tc.table_name = ?
ORDER BY kc.ordinal_position;`;
}
static quoteIdentifier(tableName) {
return tableName
.split('.')
.map((part) => `"${part}"`)
.join('.');
}
static _prepareOptions(options) {
const keys = Object.keys(options).map((key) => `"${key}"`);
const values = keys.map(() => '?');
const args = Object.values(options);
return { keys, values, args };
}
static buildColumnDefinition(colName, definition) {
if ('properties' in definition) {
// Handle OBJECT type
const objectMode = definition.mode ? `(${definition.mode.toUpperCase()})` : '';
const objectProps = Object.entries(definition.properties || {})
.map(([key, prop]) => this.buildColumnDefinition(key, prop))
.join(', ');
return `"${colName}" OBJECT${objectMode} AS (${objectProps})`;
}
const baseDefinition = definition;
// Regular column definition
let colDef = `"${colName}" ${definition.type.toUpperCase()}`;
if (baseDefinition.notNull)
colDef += ' NOT NULL';
if (baseDefinition.defaultValue !== undefined)
colDef += ` DEFAULT ${baseDefinition.defaultValue}`;
if (baseDefinition.generatedAlways)
colDef += ` GENERATED ALWAYS AS (${baseDefinition.generatedAlways})`;
return colDef;
}
}
exports.StatementGenerator = StatementGenerator;
//# sourceMappingURL=StatementGenerator.js.map