Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ QueryLeaf is a SQL to MongoDB compiler / translator.
- Format: `yarn format` (check: `yarn format:check`)
- Run all tests: `yarn test`
- Run individual package tests: `yarn test:lib`, `yarn test:cli`, `yarn test:server`, `yarn test:pg-server`
- Run single test: `cd packages/[package] && npx jest -t "test name"` or `npx jest path/to/test.test.ts -t "test name"`
- Run single test: `cd packages/[package] && yarn yarn -t "test name"` or `yarn jest path/to/test.test.ts -t "test name"`
- Integration tests: `yarn test:lib:integration` (requires Docker)
- Documentation: `yarn docs:serve` (dev), `yarn docs:build` (build)

Expand All @@ -24,4 +24,4 @@ QueryLeaf is a SQL to MongoDB compiler / translator.
- Error handling with proper try/catch blocks and meaningful error messages
- Use async/await for asynchronous code
- Follow existing patterns for similar functionality
- Tests should cover both unit and integration cases
- Tests should cover both unit and integration cases
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"test:unit": "bin/run-all test:unit",
"test:integration": "bin/run-all test:integration",
"test:lib": "yarn workspace @queryleaf/lib test",
"test:lib:integration": "yarn workspace @queryleaf/lib test:integration",
"test:cli": "yarn workspace @queryleaf/cli test",
"test:server": "yarn workspace @queryleaf/server test",
"test:pg-server": "yarn workspace @queryleaf/postgres-server test",
Expand Down
976 changes: 712 additions & 264 deletions packages/lib/src/compiler.ts

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion packages/lib/src/parser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Parser as NodeSqlParser } from 'node-sql-parser';
import { From, Parser as NodeSqlParser } from 'node-sql-parser';
import { SqlParser, SqlStatement } from './interfaces';
import debug from 'debug';

Expand Down Expand Up @@ -79,6 +79,8 @@ export class SqlParserImpl implements SqlParser {
database: 'PostgreSQL',
});

log('Preprocessed AST: ', JSON.stringify(ast, null, 2));

// Process the AST to properly handle nested fields
const processedAst = this.postProcessAst(ast);

Expand Down Expand Up @@ -249,6 +251,7 @@ export class SqlParserImpl implements SqlParser {
// It's likely a nested field, not a table reference
column.expr.column = `${column.expr.table}.${column.expr.column}`;
column.expr.table = null;
log(`Setting table to null for likely nested field: ${column.expr.column}`);
}
}
});
Expand Down
36 changes: 4 additions & 32 deletions packages/lib/tests/integration/alias.integration.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { ObjectId } from 'mongodb';
import { testSetup, createLogger, ensureArray, ensureDocument } from './test-setup';

const log = createLogger('alias');
import { testSetup, ensureArray, ensureDocument } from './test-setup';

describe('SQL Aliases Integration Tests', () => {
let db;
beforeAll(async () => {
await testSetup.init();
}, 30000); // 30 second timeout for container startup
Expand All @@ -23,15 +22,14 @@ describe('SQL Aliases Integration Tests', () => {

beforeEach(async () => {
// Clean up collections before each test
const db = testSetup.getDb();
db = testSetup.getDb();
await db.collection('customers').deleteMany({});
await db.collection('products').deleteMany({});
await db.collection('orders').deleteMany({});
});

afterEach(async () => {
// Clean up collections after each test
const db = testSetup.getDb();
await db.collection('customers').deleteMany({});
await db.collection('products').deleteMany({});
await db.collection('orders').deleteMany({});
Expand All @@ -40,7 +38,6 @@ describe('SQL Aliases Integration Tests', () => {
// Test case for SELECT with table alias
test('should return correct fields when using table alias in SELECT', async () => {
// Arrange
const db = testSetup.getDb();
await db.collection('customers').insertMany([
{ name: 'John Doe', email: 'john@example.com', active: true },
{ name: 'Jane Smith', email: 'jane@example.com', active: false }
Expand All @@ -51,7 +48,6 @@ describe('SQL Aliases Integration Tests', () => {
const sql = "SELECT c.name, c.active FROM customers c";

const results = ensureArray(await queryLeaf.execute(sql));
log('Table alias SELECT results:', JSON.stringify(results, null, 2));

// Assert
expect(results).toHaveLength(2);
Expand All @@ -64,7 +60,6 @@ describe('SQL Aliases Integration Tests', () => {
// Test case for just selecting a single field with table alias
test('should return single field when using table alias in SELECT', async () => {
// Arrange
const db = testSetup.getDb();
await db.collection('customers').insertMany([
{ name: 'John Doe', email: 'john@example.com', active: true },
{ name: 'Jane Smith', email: 'jane@example.com', active: false }
Expand All @@ -75,7 +70,6 @@ describe('SQL Aliases Integration Tests', () => {
const sql = "SELECT c.active FROM customers c";

const results = ensureArray(await queryLeaf.execute(sql));
log('Single field alias SELECT results:', JSON.stringify(results, null, 2));

// Assert
expect(results).toHaveLength(2);
Expand All @@ -88,7 +82,6 @@ describe('SQL Aliases Integration Tests', () => {
// Test case for table alias in WHERE clause
test('should filter correctly when using table alias in WHERE clause', async () => {
// Arrange
const db = testSetup.getDb();
await db.collection('customers').insertMany([
{ name: 'John Doe', email: 'john@example.com', active: true },
{ name: 'Jane Smith', email: 'jane@example.com', active: false }
Expand All @@ -99,7 +92,6 @@ describe('SQL Aliases Integration Tests', () => {
const sql = "SELECT c.name FROM customers c WHERE c.active = true";

const results = ensureArray(await queryLeaf.execute(sql));
log('WHERE clause with alias results:', JSON.stringify(results, null, 2));

// Assert
expect(results).toHaveLength(1);
Expand All @@ -109,7 +101,6 @@ describe('SQL Aliases Integration Tests', () => {
// Test case for combining table alias with column alias
test('should support combined table alias with column alias', async () => {
// Arrange
const db = testSetup.getDb();
await db.collection('customers').insertMany([
{ name: 'John Doe', email: 'john@example.com', active: true },
{ name: 'Jane Smith', email: 'jane@example.com', active: false }
Expand All @@ -120,7 +111,6 @@ describe('SQL Aliases Integration Tests', () => {
const sql = "SELECT c.name AS customer_name, c.active AS is_active FROM customers c";

const results = ensureArray(await queryLeaf.execute(sql));
log('Combined table and column alias results:', JSON.stringify(results, null, 2));

// Assert
expect(results).toHaveLength(2);
Expand Down Expand Up @@ -155,7 +145,6 @@ describe('SQL Aliases Integration Tests', () => {
// Test case for using table alias in UPDATE statement
test('should update correctly when using table alias in UPDATE', async () => {
// Arrange
const db = testSetup.getDb();
await db.collection('customers').insertMany([
{ name: 'John Doe', email: 'john@example.com', active: true },
{ name: 'Jane Smith', email: 'jane@example.com', active: false }
Expand All @@ -170,7 +159,6 @@ describe('SQL Aliases Integration Tests', () => {
// Verify with a SELECT
const selectSql = "SELECT name, active FROM customers";
const results = ensureArray(await queryLeaf.execute(selectSql));
log('UPDATE with alias results:', JSON.stringify(results, null, 2));

// Assert
expect(results).toHaveLength(2);
Expand All @@ -182,7 +170,6 @@ describe('SQL Aliases Integration Tests', () => {
// Test case for using table alias in DELETE statement
test('should delete correctly when using table alias in DELETE', async () => {
// Arrange
const db = testSetup.getDb();
await db.collection('customers').insertMany([
{ name: 'John Doe', email: 'john@example.com', active: true },
{ name: 'Jane Smith', email: 'jane@example.com', active: false }
Expand All @@ -197,7 +184,6 @@ describe('SQL Aliases Integration Tests', () => {
// Verify with a SELECT
const selectSql = "SELECT name, active FROM customers";
const results = ensureArray(await queryLeaf.execute(selectSql));
log('DELETE with alias results:', JSON.stringify(results, null, 2));

// Assert
expect(results).toHaveLength(1);
Expand All @@ -208,7 +194,6 @@ describe('SQL Aliases Integration Tests', () => {
// Test case for multiple table aliases in a query with JOIN
test('should handle multiple table aliases in a JOIN query', async () => {
// Arrange
const db = testSetup.getDb();
const johnId = new ObjectId();
const janeId = new ObjectId();

Expand All @@ -231,15 +216,8 @@ describe('SQL Aliases Integration Tests', () => {
JOIN orders o ON c._id = o.customerId
`;

// Add detailed logging before executing the query
console.log('EXECUTING JOIN QUERY:', sql);

const results = ensureArray(await queryLeaf.execute(sql));

// Print the detailed results for debugging
console.log('JOIN RESULTS LENGTH:', results.length);
console.log('JOIN RESULTS STRUCTURE:', JSON.stringify(results, null, 2));

// Assert
expect(results.length).toBeGreaterThan(0);

Expand All @@ -265,7 +243,6 @@ describe('SQL Aliases Integration Tests', () => {
// Test case for alias in ORDER BY clause
test('should sort correctly when using alias in ORDER BY', async () => {
// Arrange
const db = testSetup.getDb();
await db.collection('products').insertMany([
{ name: 'Laptop', category: 'Electronics', price: 1200 },
{ name: 'Mouse', category: 'Electronics', price: 25 },
Expand All @@ -277,7 +254,6 @@ describe('SQL Aliases Integration Tests', () => {
const sql = "SELECT p.name, p.price FROM products p ORDER BY p.price DESC";

const results = ensureArray(await queryLeaf.execute(sql));
log('ORDER BY with alias results:', JSON.stringify(results, null, 2));

// Assert
expect(results).toHaveLength(3);
Expand All @@ -290,7 +266,6 @@ describe('SQL Aliases Integration Tests', () => {
// Test case for using alias in GROUP BY clause
test('should group correctly when using alias in GROUP BY', async () => {
// Arrange
const db = testSetup.getDb();
await db.collection('products').insertMany([
{ name: 'Laptop', category: 'Electronics', price: 1200 },
{ name: 'Mouse', category: 'Electronics', price: 25 },
Expand All @@ -304,7 +279,6 @@ describe('SQL Aliases Integration Tests', () => {
const sql = "SELECT p.category, COUNT(*) as count FROM products p GROUP BY p.category";

const results = ensureArray(await queryLeaf.execute(sql));
log('GROUP BY with alias results:', JSON.stringify(results, null, 2));

// Assert
expect(results.length).toBe(2);
Expand Down Expand Up @@ -345,7 +319,6 @@ describe('SQL Aliases Integration Tests', () => {
// Test case for alias with functions
test('should handle alias with functions in SELECT', async () => {
// Arrange
const db = testSetup.getDb();
await db.collection('products').insertMany([
{ name: 'Laptop', price: 1200 },
{ name: 'Mouse', price: 25 },
Expand All @@ -357,7 +330,6 @@ describe('SQL Aliases Integration Tests', () => {
const sql = "SELECT p.name, UPPER(p.name) as upper_name FROM products p";

const results = ensureArray(await queryLeaf.execute(sql));
log('Function with alias results:', JSON.stringify(results, null, 2));

// Assert
expect(results).toHaveLength(3);
Expand All @@ -379,4 +351,4 @@ describe('SQL Aliases Integration Tests', () => {
}
}
});
});
});
Loading