rawsql-ts
Version:
High-performance SQL parser and AST analyzer written in TypeScript. Provides fast parsing and advanced transformation capabilities.
151 lines • 6.76 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.FixtureCteBuilder = void 0;
const Clause_1 = require("../models/Clause");
const SelectQuery_1 = require("../models/SelectQuery");
const ValueComponent_1 = require("../models/ValueComponent");
const DDLToFixtureConverter_1 = require("./DDLToFixtureConverter");
class FixtureCteBuilder {
/**
* Creates fixture definitions from a SQL string containing DDL (CREATE TABLE) and INSERT statements.
*
* @param sql The SQL string containing DDL and INSERTs.
* @returns An array of FixtureTableDefinition objects.
*/
static fromSQL(sql) {
const fixtureJson = DDLToFixtureConverter_1.DDLToFixtureConverter.convert(sql);
return this.fromJSON(fixtureJson);
}
/**
* Converts JSON fixture definitions to FixtureTableDefinition format.
* Accepts an object where keys are table names and values contain columns and rows.
*
* @param jsonDefinitions Object with table definitions
* @returns Array of FixtureTableDefinition
*
* @example
* ```typescript
* const json = {
* users: {
* columns: [
* { name: 'id', type: 'integer' },
* { name: 'name', type: 'text' }
* ],
* rows: [
* { id: 1, name: 'Alice' },
* { id: 2, name: 'Bob' }
* ]
* }
* };
* const fixtures = FixtureCteBuilder.fromJSON(json);
* ```
*/
static fromJSON(jsonDefinitions) {
const fixtures = [];
for (const [tableName, def] of Object.entries(jsonDefinitions)) {
if (def && Array.isArray(def.columns)) {
const columns = def.columns.map(c => ({
name: c.name,
typeName: c.type,
defaultValue: c.default
}));
let rows = [];
if (Array.isArray(def.rows)) {
// Convert array of objects to array of arrays based on column order
rows = def.rows.map(rowObj => {
return columns.map(col => {
return rowObj[col.name] !== undefined ? rowObj[col.name] : null;
});
});
}
fixtures.push({
tableName,
columns,
rows
});
}
}
return fixtures;
}
/** Builds CommonTable representations for the provided fixtures. */
static buildFixtures(fixtures) {
return fixtures.map((fixture) => this.buildFixture(fixture));
}
static buildFixture(fixture) {
const query = this.buildSelectQuery(fixture);
// Wrap the query into a CommonTable for later WITH clause injection.
return new Clause_1.CommonTable(query, fixture.tableName, null);
}
static buildSelectQuery(fixture) {
const columnCount = fixture.columns.length;
// Always produce at least one row even when the fixture carries zero entries.
const rows = fixture.rows.length > 0 ? fixture.rows : [new Array(columnCount).fill(null)];
const selectQueries = rows.map((row) => this.buildSelectRow(fixture.columns, row));
if (selectQueries.length === 0) {
throw new Error('No rows to build SELECT query');
}
let result = selectQueries[0];
// Build UNION ALL chain for multiple rows
for (let i = 1; i < selectQueries.length; i++) {
// Both SimpleSelectQuery and BinarySelectQuery have toUnionAll/unionAll methods
if (result instanceof SelectQuery_1.SimpleSelectQuery) {
result = result.toUnionAll(selectQueries[i]);
}
else {
// BinarySelectQuery has unionAll method
result = result.unionAll(selectQueries[i]);
}
}
// Handle empty fixture case: add WHERE 1 = 0 to make it return no rows
if (fixture.rows.length === 0 && result instanceof SelectQuery_1.SimpleSelectQuery) {
const falseCondition = new ValueComponent_1.BinaryExpression(new ValueComponent_1.LiteralValue(1), '=', new ValueComponent_1.LiteralValue(0));
result.whereClause = new Clause_1.WhereClause(falseCondition);
}
return result;
}
static buildSelectRow(columns, row) {
// Build select items that respect optional type annotations.
const items = columns.map((column, index) => {
const value = index < row.length ? row[index] : null;
const literalValue = this.createLiteralValue(value);
let expression = literalValue;
if (column.typeName) {
const typeValue = new ValueComponent_1.TypeValue(null, new ValueComponent_1.RawString(column.typeName));
expression = new ValueComponent_1.CastExpression(literalValue, typeValue);
}
return new Clause_1.SelectItem(expression, column.name);
});
const selectClause = new Clause_1.SelectClause(items);
return new SelectQuery_1.SimpleSelectQuery({ selectClause });
}
static createLiteralValue(value) {
if (value === null || value === undefined) {
return new ValueComponent_1.LiteralValue(null);
}
if (typeof value === 'number') {
return new ValueComponent_1.LiteralValue(Number.isFinite(value) ? value : null);
}
if (typeof value === 'boolean') {
// Preserve boolean literals so the printer emits TRUE/FALSE instead of quoted strings
return new ValueComponent_1.LiteralValue(value);
}
if (typeof value === 'bigint') {
// Convert bigint to string to preserve precision
// LiteralValue accepts string|number|boolean|null, and when isStringLiteral is false,
// the printer will output it as-is without quotes
return new ValueComponent_1.LiteralValue(value.toString());
}
if (typeof Buffer !== 'undefined' && value instanceof Buffer) {
// For Buffer, we'll create a hex string literal
return new ValueComponent_1.LiteralValue(`X'${value.toString('hex')}'`);
}
if (typeof value === 'string') {
// Store the raw string value WITHOUT quotes or escaping
// The SqlPrinter will handle escaping when printing
return new ValueComponent_1.LiteralValue(value, undefined, true);
}
return new ValueComponent_1.LiteralValue(String(value), undefined, true);
}
}
exports.FixtureCteBuilder = FixtureCteBuilder;
//# sourceMappingURL=FixtureCteBuilder.js.map