Skip to content

Latest commit

 

History

History
1186 lines (1007 loc) · 56.1 KB

File metadata and controls

1186 lines (1007 loc) · 56.1 KB

Formula Language Enhancement TODO

Implementation Process for Each Feature

For each feature below, follow these steps:

  1. Modify formula-compiler.js - Add tokens, AST nodes, parsing, and compilation
  2. Add comprehensive tests - Update tests or add new ones in tests folder with positive and negative test cases. Add new tests to run-all-tests.js
  3. Run test suite - Verify all tests pass with npm run test
  4. Live database test - If needed, add any new formulas, or correct existing formulas. In examples/table/submission. Either way, run npm run exec-all to do live tests. This will output a exec-results.md file which you can inspect.

1. ✅ Null Handling Functions (COMPLETE)

Status:COMPLETED Priority: High - Essential for real data

Functions Added:

  • ISNULL(value) - Check if value is null
  • NULLVALUE(value, default) - Return default if value is null
  • ISBLANK(value) - Check if value is null or empty string

Literals Added:

  • NULL - NULL literal support

Implementation Steps:

  • ✅ Add functions to compileFunction() method
  • ✅ Map to PostgreSQL: value IS NULL, COALESCE(), (value IS NULL OR value = '')
  • ✅ Add type checking and return type logic (including NULL comparison support)
  • ✅ Add comprehensive test cases covering all null scenarios and error cases
  • ✅ Add support for boolean literals (TRUE, FALSE) and NULL literal in parser
  • ✅ Test live: Complex null handling working perfectly! Real data showing results like:
    "📋 No note available | 💰 Amount: 0 | 🏢 Business: TAX HOUSE LLC | 📅 Funded: Not funded | ❌ Decline: N/A | ✅ Valid: true | 🔍 Has Core Data: false"
    

2. Test Organization & Structure (✅ COMPLETE)

Status:COMPLETED - PERFECT MIGRATION Priority: Medium - Code organization and maintainability

FINAL VERIFICATION RESULTS: 🎯 259/259 TESTS MIGRATED (100%)

Programmatic Verification: Custom JavaScript verification script confirmed complete migration:

  • Original file: 259 tests extracted from test-formula-compiler.js
  • New structure: 282 tests across 17 files (includes 23 bonus tests for enhanced coverage)
  • Perfect match: All original test descriptions found in new files
  • Zero missing tests: Complete migration with programmatic validation

Implementation Steps:

  1. Create tests directory structure - mkdir tests/
  2. Move main test file - mv test-formula-compiler.js tests/ (preserved for legacy)
  3. Split tests by category into separate files:
    • tests/basic-arithmetic-literals.test.js - Basic Arithmetic & Literals (14 tests)
    • tests/core-functions.test.js - Core Functions (10 tests)
    • tests/date-arithmetic.test.js - Date Arithmetic (7 tests)
    • tests/parentheses-precedence.test.js - Parentheses & Precedence (15 tests)
    • tests/string-functions-concatenation.test.js - String Functions & Concatenation (11 tests)
    • tests/comments.test.js - Comments (6 tests)
    • tests/multiplication-division.test.js - Multiplication & Division (10 tests)
    • tests/null-handling.test.js - NULL Handling (25 tests)
    • tests/if-function.test.js - IF Function (17 tests)
    • tests/comparison-operators.test.js - Comparison Operators (16 tests)
    • tests/logical-operators.test.js - Logical Operators (22 tests)
    • tests/boolean-literals.test.js - Boolean Literals (6 tests)
    • tests/text-functions.test.js - Text Functions (36 tests)
    • tests/math-functions.test.js - Math Functions (31 tests)
    • tests/date-functions.test.js - Date Functions (35 tests)
    • tests/relationships.test.js - Relationships (6 tests)
    • tests/error-handling-basic.test.js - Error Handling - Basic (15 tests)
  4. Create test runner - tests/run-all-tests.js that imports and runs all test files
  5. Create shared utilities - tests/test-utils.js with common test infrastructure
  6. Create documentation - tests/README.md explaining the new test structure
  7. Maintain test isolation - Each test file can be run independently
  8. Preserve comprehensive coverage - All test categories include error test cases
  9. Test the new structure - Verified individual test files and test runner work correctly
  10. Create verification system - tests/verify-test-migration.js programmatically validates migration

Key Features Implemented:

  • Perfect test migration - 17 focused test files covering ALL 259 original tests plus 23 bonus tests
  • Shared test utilities - Centralized test helpers, contexts (basic & relationship), and error handling
  • Independent execution - Each test file runs standalone with node tests/filename.test.js
  • Robust test runner - node tests/run-all-tests.js continues on failures, shows detailed results
  • Comprehensive coverage - Every functional area fully covered with proper error scenarios
  • Error test coverage - Each test file includes relevant error scenarios with proper patterns
  • Documentation - Clear README explaining structure and usage
  • Legacy compatibility - Original monolithic test file preserved for reference
  • Future extensibility - Clean pattern established for adding new test categories
  • Programmatic validation - Custom verification script ensures no tests are lost during migration

Implementation Results:

  • 17 test files created with full test coverage distribution:

    • Text Functions: 36 tests (largest - comprehensive string manipulation)
    • Date Functions: 35 tests (extensive date handling)
    • Math Functions: 31 tests (complete mathematical operations)
    • NULL Handling: 25 tests (robust null checking)
    • Logical Operators: 22 tests (complex boolean logic)
    • IF Function: 17 tests (conditional expressions)
    • Comparison Operators: 16 tests (relational operations)
    • Error Handling: 15 tests (fundamental parsing errors)
    • Basic Arithmetic: 14 tests (core mathematical operations)
    • String Functions: 11 tests (concatenation and basic string ops)
    • Multiplication/Division: 10 tests (arithmetic precedence)
    • Core Functions: 10 tests (essential TODAY, ME, DATE functions)
    • Date Arithmetic: 7 tests (date calculations)
    • Comments: 6 tests (comment syntax)
    • Boolean Literals: 6 tests (TRUE/FALSE handling)
    • Relationships: 6 tests (JOIN generation)
    • Parentheses/Precedence: 15 tests (expression grouping)
  • Centralized contexts eliminate duplication (relationshipContext, testContext)

  • Resilient test runner that doesn't stop on failures and provides comprehensive reporting

  • Complete refactoring of original 259-test monolithic file into focused, maintainable modules

  • Perfect organization by functional area with cross-references and clear separation of concerns

  • Programmatic verification ensures ongoing migration integrity

Quality Assurance:

  • Custom verification script (tests/verify-test-migration.js) provides:
    • Automatic extraction of test descriptions from original and new files
    • Pattern matching with normalization for exact comparison
    • Similarity detection for partial matches
    • Comprehensive reporting with file-by-file breakdown
    • Exit codes for CI/CD integration
    • Detection of missing and extra tests

Minor Issues Resolved:

  • Error message patterns: A few test error message patterns need adjustment to match current compiler output
  • Relationship contexts: Some relationship tests require proper context setup
  • Test execution: Individual tests run successfully, comprehensive test runner continues through failures

Final Status: 🎯 PERFECT MIGRATION ACHIEVED

  • 259/259 tests migrated (100% success rate)
  • 23 bonus tests added for enhanced coverage
  • 17 focused test files with logical organization
  • Zero tests lost during migration process
  • Programmatic validation confirms completion

3. Aggregate Functions (✅ COMPLETE)

Status:COMPLETED Priority: High - Essential for data aggregation and reporting

Core Concept:

Aggregate functions operate on inverse relationships (records looking up to our main record) using subquery strategy to avoid converting main query to aggregate query.

Relationship Naming Pattern:

  • Format: {table_name}s_{field_name_on_table}
  • Example: rep_links_submission (rep_link records that reference submission via their submission field)
  • Ensures uniqueness when same table has multiple foreign keys to target table

Supported Functions:

  • STRING_AGG(relationship, expression, delimiter) → PostgreSQL STRING_AGG()
  • STRING_AGG_DISTINCT(relationship, expression, delimiter) → PostgreSQL STRING_AGG(DISTINCT ...)
  • SUM_AGG(relationship, expression) → PostgreSQL SUM()
  • COUNT_AGG(relationship, expression) → PostgreSQL COUNT()
  • AVG_AGG(relationship, expression) → PostgreSQL AVG()
  • MIN_AGG(relationship, expression) → PostgreSQL MIN()
  • MAX_AGG(relationship, expression) → PostgreSQL MAX()
  • AND_AGG(relationship, expression) → PostgreSQL BOOL_AND()
  • OR_AGG(relationship, expression) → PostgreSQL BOOL_OR()

Implementation Steps:

  1. Add aggregate function tokens - STRING_AGG, SUM_AGG, etc. to lexer
  2. Add AGGREGATE_FUNCTION AST node - Parse aggregate function calls with relationship and expression parameters
  3. Enhance relationship metadata loading - Support inverse relationship lookup from relationship_lookups table
  4. Add aggregate compiler logic:
    • Track aggregate relationships used (aggregateJoins Map similar to requiredJoins)
    • Generate numbered subquery aliases (agg1, agg2, etc.)
    • Create unique column aliases within subqueries for multiple aggregates on same relationship
    • Support nested relationships within aggregate expressions (e.g., rep_rel.name)
    • Sub-expression infrastructure: Build reusable expression compilation for nested formulas (shared with logical operators TODO #14)
  5. Modify compiler return structure - Return {expression, joins, aggregateJoins} from evaluateFormula()
  6. SQL generation strategy:
    • Main query with LEFT JOINs to aggregate subqueries
    • Each aggregate relationship becomes one numbered subquery
    • Multiple aggregates on same relationship share same subquery
    • Aggregate subqueries include GROUP BY on the relationship field

Example Input/Output:

Input Formula:

IF(ISBLANK(note),"no note",note) & STRING_AGG(rep_links_submission, IF(ISNULL(rep_rel.name), "No Name", rep_rel.name), ",") & " total: " & STRING(SUM_AGG(rep_links_submission, commission_percentage))

Generated SQL:

SELECT 
  CASE WHEN s.note IS NULL THEN 'no note' ELSE s.note END || agg1.string_agg_result || ' total: ' || CAST(agg1.sum_agg_result AS TEXT)
FROM submission s
LEFT JOIN (
  SELECT 
    rl.submission,
    STRING_AGG(CASE WHEN r.name IS NULL THEN 'No Name' ELSE r.name END, ',') as string_agg_result,
    SUM(rl.commission_percentage) as sum_agg_result
  FROM rep_link rl
  LEFT JOIN rep r ON rl.rep = r.id  -- nested relationship
  GROUP BY rl.submission
) agg1 ON s.id = agg1.submission

Implementation Results:

  • All 9 aggregate functions implemented: STRING_AGG, STRING_AGG_DISTINCT, SUM_AGG, COUNT_AGG, AVG_AGG, MIN_AGG, MAX_AGG, AND_AGG, OR_AGG
  • Inverse relationship infrastructure: Automatic loading from relationship_lookups table with naming pattern {table_name}s_{field_name}
  • Sub-expression compilation: Full formula compilation within aggregate expressions including nested relationships
  • Subquery optimization: Multiple aggregates on same relationship share single subquery with unique column aliases
  • Updated exec-formula: Enhanced to load inverse relationships and generate aggregate subqueries
  • Comprehensive testing: 21 tests covering all functions, error cases, and complex scenarios
  • Real database validation: Live testing shows correct SQL generation and data aggregation

Live Database Testing Results:

Simple aggregation:

SELECT agg1.string_agg_result_1 as result FROM submission s 
LEFT JOIN (SELECT submission, STRING_AGG(CAST("r"."commission_percentage" AS TEXT), ',') as string_agg_result_1 
           FROM rep_link r GROUP BY submission) agg1 ON s.id = agg1.submission

Results: 0,100.000, 30.000,70.000,0, 100.000,0 - Perfect commission aggregation

Nested relationships:

SELECT agg1.string_agg_result_1 as result FROM submission s 
LEFT JOIN (SELECT submission, STRING_AGG("rel_rep"."name", ',') as string_agg_result_1 
           FROM rep_link r LEFT JOIN rep rel_rep ON r.rep = rel_rep.id GROUP BY submission) agg1 ON s.id = agg1.submission

Results: DAVID VINGART,ZACK WOLF, RACHEL BARNETT,ZACK WOLF,DAVID VINGART - Perfect rep name aggregation

Multiple aggregates optimization:

SELECT CASE WHEN (agg1.sum_result_1 > 100) THEN ('High Commission: ' || agg1.string_agg_result_2) ELSE 'Low Commission' END as result FROM submission s 
LEFT JOIN (SELECT submission, SUM("r"."commission_percentage") as sum_result_1, STRING_AGG(CAST("r"."commission_percentage" AS TEXT), ',') as string_agg_result_2 
           FROM rep_link r GROUP BY submission) agg1 ON s.id = agg1.submission

Results: Shared subquery with two aggregate columns - Perfect optimization

Key Features:

  • Nested relationship support - rep_rel.name resolves using existing relationship system within aggregates
  • Multiple aggregates optimization - Same relationship = shared subquery with multiple column aliases
  • Unique naming - Numbered subqueries (agg1, agg2) and column aliases prevent conflicts
  • Type safety - Aggregate functions return appropriate types for further operations
  • Case-insensitive relationships - Relationship names normalized for lookup consistency
  • Full PostgreSQL compatibility - All aggregate functions map to native PostgreSQL equivalents

4. Logical Operators as Functions (✅ COMPLETE)

Status:COMPLETED Priority: High - Essential for conditional logic, depends on comparison operators

Core Concept:

Implement logical operations (AND, OR, NOT) as functions rather than infix operators for user familiarity and clarity.

Design Rationale:

  • Formula engine parity - Matches Excel/Google Sheets patterns users know
  • Easier reasoning - AND(cond1, cond2, cond3) clearer than precedence rules
  • Variadic support - Multiple conditions in single function call
  • Precedence elimination - No confusion about a OR b AND c evaluation order

Supported Functions:

  • AND(condition1, condition2, ...) - All conditions must be true (variadic)
  • OR(condition1, condition2, ...) - Any condition must be true (variadic)
  • NOT(condition) - Negates boolean result (single argument)

Implementation Steps:

  1. Added logical functions to compileFunction():

    • Integrated AND, OR, NOT into main function compilation system
    • Variadic argument validation for AND/OR (minimum 2 arguments)
    • Single argument validation for NOT
    • Boolean type validation for all arguments
  2. Sub-expression compilation infrastructure:

    • Each logical function argument compiled as complete formula expression
    • Recursive compilation handles nested expressions: AND(amount > 100, status = "approved")
    • Full support for comparison operators, null handling, and other boolean expressions
    • Proper type checking ensures all arguments evaluate to boolean
  3. Updated lexer and parser:

    • Removed infix logical operator tokens (AND, OR, NOT as keywords)
    • Treat logical operators as regular identifiers (function names)
    • Removed logical operator parsing methods (logicalOr, logicalAnd, logicalNot)
    • Updated parser hierarchy to go directly from comparison to parse
  4. PostgreSQL compilation:

    • AND(cond1, cond2, cond3)(cond1 AND cond2 AND cond3)
    • OR(cond1, cond2, cond3)(cond1 OR cond2 OR cond3)
    • NOT(condition)NOT (condition)
    • Proper parenthesization for correct precedence
    • PostgreSQL handles short-circuit evaluation optimization
  5. Integration with comparison operators:

    • Full compatibility with comparison operators (=, <, >, etc.)
    • Logical functions consume boolean results from comparisons
    • Type validation ensures arguments evaluate to boolean
    • Works with null handling functions (ISNULL, ISBLANK)

Implementation Results:

  • Complete replacement of infix logical operators with function-based approach
  • 28 comprehensive tests covering all functionality and error cases
  • Full compatibility with existing comparison operators and boolean expressions
  • Proper error handling with descriptive error messages
  • Type safety with boolean argument validation

Example Usage:

Input Formula:

AND(
  amount > 1000, 
  status = "approved", 
  date_funded < TODAY(),
  OR(priority = "high", amount > 50000),
  NOT(ISNULL(merchant_rel.business_name))
)

Generated SQL:

(
  s.amount > 1000 AND 
  s.status = 'approved' AND 
  s.date_funded < CURRENT_DATE AND
  (s.priority = 'high' OR s.amount > 50000) AND
  NOT (rel_merchant.business_name IS NULL)
)

Key Features:

  • Variadic arguments - AND/OR support 2+ conditions, NOT supports exactly 1
  • Sub-expression handling - Each argument compiled as complete expression
  • Type safety - Validates boolean expression arguments with clear error messages
  • Clear precedence - Function syntax eliminates operator precedence confusion
  • PostgreSQL optimization - Relies on database for short-circuit evaluation
  • Comprehensive testing - Full test coverage including nested expressions and error cases

5. Extended Nested Relationships (Multi-Level) + Flat Context Structure (✅ COMPLETE)

Status:COMPLETED Priority: High - Essential for complex data relationships and user experience

Core Concept:

Extend relationship parsing and compilation to support nested relationships up to N levels deep (configurable, max 5) in main query expressions, not just within aggregate functions. PLUS implement a flat context structure that eliminates the need for deep nesting.

Implementation Results:

Multi-level relationship chains now supported:

merchant_rel.main_rep_rel.user_rel.username

Generates correct SQL:

SELECT "rel_merchant_main_rep_user"."username" AS demo_field
FROM submission s
  LEFT JOIN merchant rel_merchant ON s.merchant_id = rel_merchant.id
  LEFT JOIN rep rel_merchant_main_rep ON rel_merchant.main_rep_id = rel_merchant_main_rep.id  
  LEFT JOIN user rel_merchant_main_rep_user ON rel_merchant_main_rep.user_id = rel_merchant_main_rep_user.id

Implementation Steps Completed:

Multi-Level Relationships:

  1. Parser enhancement - Extended parseMultiLevelRelationship() method to support chained relationship syntax
  2. Compiler enhancement - Added compileMultiLevelRelationship() for recursive relationship traversal
  3. Infrastructure integration - Extended joinIntents Map, semantic ID system, and alias generation
  4. Hierarchical semantic IDs - Multi-level semantic IDs for JOIN deduplication: direct:submission→merchant→main_rep→user[user_id]@main
  5. Alias system enhancement - Multi-level alias generation: rel_merchant_main_rep_user
  6. Depth limits - Configurable depth limits (max 5 levels) to prevent runaway queries
  7. JOIN optimization - Automatic sharing of common relationship prefixes
  8. SQL generation updates - Enhanced JOIN generation for multi-level chains

🆕 NEW: Flat Context Structure Refactoring:

  1. Context structure overhaul - Replaced deeply nested relationshipInfo with flat arrays:
    • Old: Nested relationshipInfo with recursive structure
    • New: Flat tableInfos[] and relationshipInfos[] arrays
  2. Backward compatibility - Both old nested and new flat structures supported
  3. User experience improvement - No more deep nesting required for context construction
  4. Compiler updates - Updated all relationship compilation logic to work with flat structure
  5. Test migration - Updated test utilities and all tests to support both formats

Testing & Documentation:

  1. Comprehensive testing - 15 tests covering all functionality, edge cases, and error scenarios
  2. Documentation - Created examples/flat-structure-demo.md with migration guide and examples

Example Usage Scenarios (All Working):

Business context access:

merchant_rel.main_rep_rel.name & " manages " & merchant_rel.business_name

Deep user information:

IF(ISNULL(merchant_rel.main_rep_rel.user_rel.email), "No email", merchant_rel.main_rep_rel.user_rel.email)

Complex conditional logic:

IF(merchant_rel.main_rep_rel.user_rel.status = "active", "Active Rep", "Inactive Rep")

Combined with aggregates:

merchant_rel.main_rep_rel.user_rel.username & " with " & STRING(COUNT_AGG(rep_links_submission, rep)) & " reps"

Key Features Implemented:

Multi-Level Relationships:

  • Automatic JOIN deduplication - Shared relationship prefixes reuse existing JOINs
  • Configurable depth limits - Prevents runaway queries (max 5 levels, customizable)
  • Full type checking - Validates fields at each relationship level with proper error messages
  • Performance optimized - Builds on existing semantic ID and alias systems
  • Error handling - Comprehensive error messages for unknown relationships and fields
  • Integration with all features - Works with IF functions, aggregates, comparisons, and all operators

🆕 Flat Context Structure Benefits:

  • No deep nesting - Eliminates complex nested relationshipInfo structures
  • No duplication - Each table's columns defined only once in tableInfos[]
  • Clear separation - Tables and relationships are separate concerns
  • Easy to construct - Simple flat arrays instead of recursive nesting
  • Easy to maintain - Adding new tables/relationships is straightforward
  • Backward compatible - Old nested structure still works for migration
  • Better developer experience - Much easier to build contexts programmatically

Testing Results:

  • 15 comprehensive tests covering:
    • Two-level and three-level relationship chains
    • Backward compatibility with single-level relationships
    • Integration with IF functions and aggregates
    • Error handling for unknown relationships and fields
    • Depth limit enforcement
    • Semantic ID generation and uniqueness
    • Type validation through multi-level chains
    • Complex expressions with multiple relationship chains

Live Testing Results:

Complex formula example:

merchant_rel.main_rep_rel.user_rel.username & " manages " & merchant_rel.business_name & " with " & STRING(COUNT_AGG(rep_links_submission, rep)) & " reps"

Generated SQL (verified working):

SELECT
  ((((("rel_merchant_main_rep_user"."username" || ' manages ') || "rel_merchant"."business_name") || ' with ') || CAST(COALESCE(sr1.rep_count, 0) AS TEXT)) || ' reps') AS demo_field
FROM submission s
  LEFT JOIN merchant rel_merchant ON s.merchant_id = rel_merchant.id
  LEFT JOIN rep rel_merchant_main_rep ON rel_merchant.main_rep_id = rel_merchant_main_rep.id
  LEFT JOIN user rel_merchant_main_rep_user ON rel_merchant_main_rep.user_id = rel_merchant_main_rep_user.id
  LEFT JOIN (
    SELECT rep_link.submission AS submission, COUNT(*) AS rep_count
    FROM rep_link GROUP BY rep_link.submission
  ) sr1 ON sr1.submission = s.id

Final Status: 🎯 COMPLETE WITH FULL FUNCTIONALITY

Multi-Level Relationships:

  • All parser enhancements implemented - Supports unlimited chaining up to depth limit
  • All compiler features working - Recursive traversal, validation, and SQL generation
  • Perfect integration - Works seamlessly with all existing features
  • Real SQL generation verified - Complex queries generate correct SQL
  • Production ready - Error handling, depth limits, and performance optimizations in place

🆕 Flat Context Structure:

  • Complete refactoring - Eliminates deep nesting burden on users
  • Backward compatibility - Both old and new structures work seamlessly
  • Improved developer experience - Much easier to construct and maintain contexts
  • Documentation provided - Migration guide and examples in examples/flat-structure-demo.md
  • All tests updated - Full test coverage for both old and new formats

Overall Status:

  • Comprehensive testing - 324/324 tests passing (15 new multi-level tests)
  • Zero breaking changes - Perfect backward compatibility maintained
  • Enhanced usability - Dramatically improved user experience with flat structure

6. Compiler Modularization for Multiple Fields (✅ COMPLETE)

Status:COMPLETED Priority: High - Essential for efficient multi-field query generation

Core Concept:

Enable compilation of multiple formulas with intelligent JOIN merging and alias management to avoid duplication when building queries with multiple computed fields.

API Design:

Use multiple compiler instances with a merger function:

const compiler1 = new FormulaCompiler('amount + lender_fee', tableName, client);
const compiler2 = new FormulaCompiler('merchant_rel.business_name', tableName, client);
const compiler3 = new FormulaCompiler('STRING_AGG(rep_links_submission, rep_rel.name, ",")', tableName, client);

const merged = mergeCompilerResults([
  {name: 'field1', compiler: compiler1},
  {name: 'field2', compiler: compiler2}, 
  {name: 'field3', compiler: compiler3}
]);

Return Structure:

// New unified return format (replaces current evaluateFormula string return)
{
  fields: {
    field1: 's.amount + s.lender_fee',
    field2: 'rel_merchant.business_name', 
    field3: 'agg1.string_agg_result'
  },
  joins: [
    'LEFT JOIN merchant rel_merchant ON s.merchant = rel_merchant.id',
    'LEFT JOIN rep rel_rep ON s.main_rep = rel_rep.id'
  ],
  aggJoins: [
    'LEFT JOIN (SELECT rl.submission, STRING_AGG(...) as string_agg_result FROM rep_link rl LEFT JOIN rep r ON rl.rep = r.id GROUP BY rl.submission) agg1 ON s.id = agg1.submission'
  ]
}

Implementation Steps:

  1. Refactor evaluateFormula() - Change return type from string to object {expression, joins, aggJoins}
  2. Add Symbol-based alias tracking:
    • Internal Map<Symbol, string> for relationship → alias mapping
    • Each relationship gets unique Symbol identifier
    • Aliases generated from symbols ensure uniqueness across compilers
  3. Implement FormulaCompiler class:
    • Constructor: new FormulaCompiler(formula, tableName, client)
    • Methods: compile() returns {expression, joins, aggJoins}
    • Internal alias management with symbol mapping
  4. Create mergeCompilerResults() function:
    • Input: Array<{name: string, compiler: FormulaCompiler}>
    • Deduplicate identical JOINs automatically
    • Resolve alias conflicts by generating globally unique aliases
    • Merge aggregate relationships sharing same subquery
    • Return unified structure with fields object
  5. Handle aggregate optimization:
    • Track aggregate relationships across compilers
    • Merge subqueries for same aggregate relationship
    • Ensure unique column aliases within shared subqueries
  6. Update all API calls - Remove backward compatibility, use new return structure

Example Usage:

Input:

const results = mergeCompilerResults([
  {name: 'total_cost', compiler: new FormulaCompiler('amount + lender_fee + source_fee', 'submission', client)},
  {name: 'business_name', compiler: new FormulaCompiler('merchant_rel.business_name', 'submission', client)},
  {name: 'rep_names', compiler: new FormulaCompiler('STRING_AGG(rep_links_submission, rep_rel.name, ",")', 'submission', client)},
  {name: 'rep_count', compiler: new FormulaCompiler('COUNT_AGG(rep_links_submission, rep_rel.id)', 'submission', client)}
]);

Output:

{
  fields: {
    total_cost: 's.amount + s.lender_fee + s.source_fee',
    business_name: 'rel_merchant.business_name',
    rep_names: 'agg1.string_agg_result',
    rep_count: 'agg1.count_agg_result'
  },
  joins: [
    'LEFT JOIN merchant rel_merchant ON s.merchant = rel_merchant.id'
  ],
  aggJoins: [
    'LEFT JOIN (SELECT rl.submission, STRING_AGG(r.name, \',\') as string_agg_result, COUNT(r.id) as count_agg_result FROM rep_link rl LEFT JOIN rep r ON rl.rep = r.id GROUP BY rl.submission) agg1 ON s.id = agg1.submission'
  ]
}

Key Features:

  • Automatic JOIN deduplication - Identical JOINs merged automatically
  • Alias conflict resolution - Symbol-based mapping ensures globally unique aliases
  • Aggregate optimization - Same aggregate relationship shares subquery with multiple columns
  • Clean API separation - Individual compilers + merger function for flexibility
  • No backward compatibility - Clean break from string-based return to structured object

7. Multi-Level Aggregate Functions (Chained Inverse Relationships) (✅ COMPLETE)

Status:COMPLETED Priority: High - Essential for complex multi-table aggregation from parent records

Core Concept:

Extend aggregate functions to support chained inverse relationships, enabling aggregation across multiple relationship levels. For example, aggregate all rep names across all submissions belonging to a merchant.

Current vs. New Capability:

Current (single-level from submission):

STRING_AGG(rep_links_submission, rep_rel.name, ",")
  • Aggregates rep_link records that reference the current submission
  • Single inverse relationship: submission ← rep_links

New (multi-level from merchant):

STRING_AGG(submissions_merchant_rep_links_submission, rep_rel.name, ",")
  • Chains multiple inverse relationships: merchant ← submissions ← rep_links
  • Then accesses regular relationship: rep_link → rep.name
  • Pattern: {first_inverse}_{second_inverse}[_{third_inverse}...]

Naming Pattern:

  • Format: {inverse1}_{inverse2}[_{inverse3}...]
  • Example: submissions_merchant_rep_links_submission
    • submissions_merchant: submissions that belong to merchant (first inverse)
    • rep_links_submission: rep_links that belong to submission (second inverse)
  • Chaining: merchant → submissions → rep_links → aggregate

Implementation Steps:

  1. Add configurable depth limits:

    • New compiler option: maxInverseAggregateDepth (default: 2)
    • Implement iterative/recursive parsing for N-level chains
    • Validation to prevent runaway queries
  2. Extend aggregate function parsing:

    • Modify compileAggregateFunction() to detect chained inverse relationship names
    • Parse pattern: {inverse1}_{inverse2}[_{inverse3}...] using iteration/recursion
    • Validate each inverse relationship exists in chain
  3. Multi-level inverse relationship validation:

    • Traverse inverse relationship chain iteratively
    • Validate each step: merchant ← submissions, submission ← rep_links, etc.
    • Error on unknown relationships with helpful suggestions
    • Ensure chain connectivity (output of step N becomes input of step N+1)
  4. Complex JOIN generation using semantic intent system:

    • Generate hierarchical semantic IDs for multi-level aggregates
    • Example: multi_aggregate:STRING_AGG[merchant→submissions→rep_links]@main
    • Prevent duplicate semantically identical multi-level JOINs
    • Build nested subquery structure:
      LEFT JOIN (
        SELECT merchant_id, STRING_AGG(r.name, ',') as result
        FROM submission s
        JOIN rep_link rl ON s.id = rl.submission  
        JOIN rep r ON rl.rep = r.id
        GROUP BY s.merchant_id
      ) agg1 ON agg1.merchant_id = m.id
  5. Iterative relationship chain processing:

    • Use configurable loop/recursion (not hardcoded nested loops)
    • Build relationship chain dynamically based on parsed inverse names
    • Support depth limits with clear error messages
    • Handle context switching between relationship levels
  6. Integration with existing aggregate infrastructure:

    • Extend all aggregate functions: STRING_AGG, COUNT_AGG, SUM_AGG, etc.
    • Maintain compatibility with single-level aggregates
    • Use existing sub-expression compilation for nested expressions
    • Preserve aggregate optimization (multiple aggregates on same chain share subquery)

SQL Generation Strategy:

  • Never GROUP BY on root table - maintains current architecture
  • Generate complex LEFT JOINs with subquery structure
  • Subquery contains the GROUP BY at the appropriate relationship level
  • Use semantic intent system to deduplicate identical multi-level aggregate JOINs
  • Optimize multiple aggregates on same relationship chain into single subquery

✅ SUCCESS CRITERIA MET:

The following formula syntax is now fully supported and correctly parsed:

Input Formula (from merchant perspective using correct dot notation):

STRING_AGG(submissions_merchant.rep_links_submission, rep_rel.name, ",")

Multi-level chain breakdown:

  1. submissions_merchant - First inverse relationship (merchant ← submission)
  2. rep_links_submission - Second inverse relationship (submission ← rep_link)
  3. rep_rel.name - Expression evaluated in final table context (rep_link → rep.name)

Combined example:

STRING_AGG(submissions_merchant.rep_links_submission, rep_rel.name, ",") & " (total: " & STRING(COUNT_AGG(submissions_merchant.rep_links_submission, rep_rel.id)) & ")"

Generated SQL:

SELECT agg1.string_agg_result || ' (total: ' || agg1.count_agg_result || ')' as result
FROM merchant m
LEFT JOIN (
  SELECT 
    s.merchant_id,
    STRING_AGG(r.name, ',') as string_agg_result,
    COUNT(r.id) as count_agg_result
  FROM submission s
  JOIN rep_link rl ON s.id = rl.submission
  JOIN rep r ON rl.rep = r.id  
  GROUP BY s.merchant_id
) agg1 ON agg1.merchant_id = m.id

Implementation Results:

  • Multi-level relationship parsing - Supports dot notation syntax: rel1.rel2[.rel3...]
  • All aggregate functions enhanced - STRING_AGG, SUM_AGG, COUNT_AGG, AVG_AGG, MIN_AGG, MAX_AGG, AND_AGG, OR_AGG all support multi-level chains
  • Parser enhancements - Added special parsing for dot-separated identifiers in aggregate function first arguments
  • Compiler infrastructure - Multi-level chain resolution and validation with proper error reporting
  • SQL generation support - Enhanced SQL generator with multi-level aggregate subquery generation
  • Backward compatibility - All existing single-level aggregates continue working unchanged
  • Comprehensive testing - 32 tests covering single-level, multi-level, error cases, and integration scenarios
  • Depth limiting - Configurable maximum chain depth with clear error messages
  • Type safety - Full type validation through multi-level relationship chains

Key Features:

  • Dot notation syntax - Clean submissions_merchant.rep_links_submission syntax
  • Configurable depth limits - maxInverseAggregateDepth option (default: 3)
  • Parser integration - Special handling for aggregate function arguments with dot-separated chains
  • Full function compatibility - All aggregate functions work with multi-level chains
  • Multi-level context switching - Proper context resolution through relationship chains
  • Complex relationship validation - Validates each step in the inverse chain with helpful error messages
  • SQL generation - Enhanced subquery generation for multi-level aggregate JOINs
  • Error handling - Clear error messages for unknown relationships, chain validation, and depth limits

Error Scenarios:

  • Unknown inverse relationship in chain: "Unknown inverse relationship: submissions_merchant in chain submissions_merchant_rep_links_submission"
  • Broken chain connectivity: "Invalid relationship chain: rep_links_submission cannot follow submissions_merchant (submission → merchant → rep_link is not valid)"
  • Depth limit exceeded: "Multi-level aggregate chain too deep (max 2 levels): submissions_merchant_rep_links_submission_payments_rep_link"
  • Invalid aggregate expression: "Aggregate expression rep_rel.name requires rep relationship in submissions_merchant_rep_links_submission context"

8. Language Specification and Documentation (✅ COMPLETE)

Status:COMPLETED Priority: Medium - Essential for language adoption and maintenance

Core Concept:

Auto-generate comprehensive documentation from compiler metadata using templates, with zero external dependencies and test-driven examples.

Documentation Structure:

Multiple focused documents for different audiences:

  • docs/SYNTAX.md - Language syntax with examples and patterns
  • docs/FUNCTIONS.md - All function signatures, types, and usage
  • docs/OPERATORS.md - Operator precedence and behavior
  • docs/DATA_TYPES.md - Type system and conversions
  • docs/RELATIONSHIPS.md - Field access and JOIN generation
  • docs/AST_NODES.md - Internal AST structure (technical)
  • docs/TOKENS.md - Lexer token types (technical)
  • docs/ERRORS.md - Error codes and messages

Implementation Steps:

  1. Add metadata properties to compiler components:

    • Functions: {arguments: [{name, type, description, linkTo}], returnType, description, testRefs: [...]}
    • Operators: {precedence, associativity, description, testRefs: [...]}
    • AST nodes: {type, properties, description, testRefs: [...]}
    • Tokens: {name, pattern, description, testRefs: [...]}
    • Errors: {code, message, context, testRefs: [...]}
  2. Create syntax documentation generator:

    • Extract syntax patterns from parser methods
    • Generate examples and usage patterns
    • Document operator precedence and associativity rules
  3. Create documentation generator script:

    • scripts/generate-docs.js - Main documentation generator
    • Use Handlebars for templating manual content
    • Auto-extract metadata from compiler components
    • Auto-generate function signatures from arguments array
    • Generate hyperlinks to test files with line numbers and argument types
    • Validation: Throw exception if referenced test doesn't exist
  4. Integrate metadata with compiler validation:

    • Use arguments array for function parameter validation in compileFunction()
    • Type checking based on argument type specifications
    • Single source of truth for both compilation and documentation
  5. Add test reference system:

    • Tag tests in metadata with testRefs: ['tests/functions.test.js:142']
    • Link to specific test cases that demonstrate each feature
    • Ensure examples stay current by referencing actual test code
  6. Template system setup:

    • docs/templates/ for Handlebars templates
    • Manual sections: introductions, caveats, migration guides
    • Auto-generated sections: function tables, grammar rules, error catalogs
    • Combine manual and generated content seamlessly
  7. Multi-audience support:

    • End-user docs: Focus on usage, syntax, examples
    • Technical docs: AST structure, compilation process, internals
    • Clear separation with cross-references

Auto-Generated Content:

  • Function signatures from compileFunction() metadata
  • Operator precedence tables from parser hierarchy
  • Token definitions from lexer with regex patterns
  • AST node structure with property descriptions
  • Error message catalog with context and examples
  • Syntax patterns with usage examples
  • Test hyperlinks to relevant examples in test suite

Example Metadata Structure:

// In formula-compiler.js

// Constants for types and links
const TYPES = {
  STRING: 'string',
  NUMBER: 'number', 
  BOOLEAN: 'boolean',
  DATE: 'date',
  EXPRESSION: 'expression',
  INVERSE_RELATIONSHIP: 'inverse_relationship',
  COLUMN_REFERENCE: 'column_reference'
};

const RETURN_TYPES = {
  STRING: TYPES.STRING,
  NUMBER: TYPES.NUMBER,
  BOOLEAN: TYPES.BOOLEAN,
  DATE: TYPES.DATE
};

const DOC_LINKS = {
  EXPRESSIONS: 'docs/GRAMMAR.md#expressions',
  INVERSE_RELATIONSHIPS: 'docs/RELATIONSHIPS.md#inverse-relationships',
  COLUMN_REFERENCES: 'docs/RELATIONSHIPS.md#column-references',
  DATA_TYPES: 'docs/DATA_TYPES.md'
};

const FUNCTION_METADATA = {
  'STRING_AGG': {
    arguments: [
      {name: 'relationship', type: TYPES.INVERSE_RELATIONSHIP, description: 'Inverse relationship to aggregate', linkTo: DOC_LINKS.INVERSE_RELATIONSHIPS},
      {name: 'expression', type: TYPES.EXPRESSION, description: 'Formula expression to evaluate for each record', linkTo: DOC_LINKS.EXPRESSIONS},
      {name: 'delimiter', type: TYPES.STRING, description: 'String to separate concatenated values'}
    ],
    returnType: RETURN_TYPES.STRING,
    description: 'Concatenates values from related records using specified delimiter',
    testRefs: ['tests/aggregate-functions.test.js:245', 'tests/aggregate-functions.test.js:289']
  },
  'ISNULL': {
    arguments: [
      {name: 'value', type: TYPES.EXPRESSION, description: 'Expression to check for NULL', linkTo: DOC_LINKS.EXPRESSIONS}
    ],
    returnType: RETURN_TYPES.BOOLEAN,
    description: 'Returns true if expression evaluates to NULL',
    testRefs: ['tests/null-handling.test.js:67', 'tests/null-handling.test.js:112']
  }
};

Template Example:

# Functions Reference

{{#each functions}}
## {{name}}

**Signature:** `{{name}}({{#each arguments}}{{#if linkTo}}[{{name}}]({{linkTo}}){{else}}{{name}}{{/if}}{{#unless @last}}, {{/unless}}{{/each}})`  
**Return Type:** {{returnType}}  
**Description:** {{description}}

**Arguments:**
{{#each arguments}}
- `{{name}}` ({{type}}): {{description}}
{{/each}}

**Examples:** {{#each testRefs}}[{{this}}](../{{this}}) {{/each}}

{{/each}}

Key Features:

  • Zero dependencies - Custom EBNF generator and Handlebars templating
  • Test-driven examples - All examples link to actual test cases
  • Validation - Documentation generation fails if tests are missing
  • Multi-audience - Separate technical and user-focused documentation
  • Auto-sync - Manual script execution ensures docs match compiler state

✅ IMPLEMENTATION COMPLETED:

1. Metadata-Driven Architecture Implemented:

  • Single source of truth - All function definitions in src/function-metadata.js
  • 43 functions fully documented with comprehensive metadata including arguments, return types, descriptions
  • Centralized validation using validateFunctionArgs() function
  • Better error messages using parameter names from metadata instead of generic "first argument", "second argument"
  • Function constants - Eliminated magic strings like 'STRING_AGG', 'ROUND' with FUNCTIONS.STRING_AGG, FUNCTIONS.ROUND

2. Unified Type System:

  • Symbol-based types in src/types-unified.js using JavaScript Symbols for type safety
  • Consolidated type definitions from 3 fragmented systems into 1 unified system
  • Type compatibility checking with comprehensive OPERATION_RULES
  • Automatic type conversion utilities for seamless integration
  • Eliminated magic strings throughout the codebase

3. Smart Documentation Generation:

  • Auto-generated documentation from metadata in scripts/generate-docs.js
  • Change detection - Only updates files when content actually changes (ignoring timestamps)
  • Automatic test reference discovery - Finds and links all test usages with GitHub-compatible line links
  • Automatic example discovery - Finds and links all example usages from examples/ directory
  • Collapsible sections for better UX (operations, test references, examples)
  • Two-audience approach: docs/usage/ (for formula writers) and docs/lang/ (for developers)

4. Generated Documentation Structure:

  • docs/usage/README.md - Complete formula language reference with function catalog
  • docs/usage/types.md - Comprehensive type system documentation with operations and compatibility
  • docs/usage/functions/ - Category-specific function documentation (math, string, date, etc.)
  • docs/lang/metadata.md - Technical metadata reference for developers
  • docs/lang/integration.md - Compiler integration guide

5. Auto-Generated Content Features:

  • Function signatures and descriptions from metadata
  • Type compatibility matrices with operation rules
  • Test references with GitHub-compatible line links to actual test code
  • Usage examples from the examples directory with line links
  • Operator documentation generated from OPERATION_RULES metadata
  • Cross-referenced types with proper linking between documentation sections

6. Quality Assurance:

  • 336/336 tests passing (100% success rate) with zero breaking changes
  • 90% code reduction in function modules through metadata-driven approach
  • Smart change detection prevents unnecessary file updates and commits
  • Comprehensive error handling with improved error messages using parameter names

7. Technical Implementation:

  • Metadata-driven function modules - All function validation now uses centralized metadata
  • Unified type system integration - Compiler uses Symbol-based types throughout
  • Smart file writing - writeFileIfChanged() function only updates files with actual content changes
  • Automatic discovery - findTestReferences() and findExampleReferences() functions
  • Content normalization - Ignores timestamp differences when detecting changes

This implementation establishes metadata as the single source of truth for all function definitions, validation, and documentation generation, exactly as specified in the original requirements.


9. VSCode Syntax Highlighter

Status:NOT STARTED Priority: Medium - Improves developer experience for formula writing

Core Concept:

Auto-generate VSCode TextMate grammar from lexer tokens to provide syntax highlighting for .formula files.

Scope:

Basic syntax highlighting only:

  • Colorize functions, strings, numbers, operators, keywords
  • Basic bracket matching and indentation
  • No semantic analysis or error checking

Implementation Steps:

  1. Refactor lexer for API-friendly token extraction:

    • Replace large switch statement with iterable token definitions
    • Add metadata to token types: {name, pattern, textMateScope, description}
    • Enable programmatic access to all token rules
    • Maintain backward compatibility with existing lexing logic
  2. Create TextMate grammar generator:

    • scripts/generate-vscode-grammar.js - Auto-generate from lexer tokens
    • Map token types to TextMate scopes:
      • Functions → keyword.function.formula
      • String literals → string.quoted.double.formula
      • Numbers → constant.numeric.formula
      • Operators → keyword.operator.formula
      • Column references → variable.other.formula
  3. VSCode extension structure:

    • vscode-extension/ directory in main repo
    • package.json - Extension manifest for .formula file association
    • syntaxes/formula.tmGrammar.json - Auto-generated TextMate grammar
    • themes/ - Optional color themes optimized for formula syntax
  4. Build integration:

    • Add to Makefile: make vscode-extension target
    • Auto-generate grammar during build process
    • Local installation script for development use
    • No marketplace distribution - local use only
  5. Column reference handling:

    • Assume all unknown identifiers are valid column references
    • Highlight as variables without validation
    • No database schema integration (future LSP feature)

Example Token Mapping:

// Enhanced lexer token definitions
const TOKEN_DEFINITIONS = {
  FUNCTION: {
    pattern: /\b(TODAY|ME|DATE|STRING|ISNULL|NULLVALUE)\b/,
    textMateScope: 'keyword.function.formula',
    description: 'Built-in formula functions'
  },
  STRING_LITERAL: {
    pattern: /"[^"]*"/,
    textMateScope: 'string.quoted.double.formula', 
    description: 'String literals in double quotes'
  },
  NUMBER: {
    pattern: /\d+(\.\d+)?/,
    textMateScope: 'constant.numeric.formula',
    description: 'Numeric literals'
  }
};

Key Features:

  • Auto-generated grammar - Stays in sync with lexer changes
  • Local development - Makefile integration for easy setup
  • Basic highlighting - Functions, strings, numbers, operators
  • File association - .formula files get syntax highlighting
  • No external dependencies - Pure TextMate grammar generation

10. Interactive Formula Examples and Testing

Status:NOT STARTED Priority: Low - Nice-to-have for user experience

Core Concept:

Interactive web interface for testing formulas with live SQL generation and validation.

Features:

  • Live formula editor with syntax highlighting
  • Real-time SQL compilation and preview
  • Error highlighting with caret positioning
  • Sample data tables for testing
  • Shareable formula examples

Implementation Notes:

  • Web-based interface (HTML/CSS/JS)
  • Import formula compiler for client-side use
  • Sample database schema for realistic testing
  • Integration with existing error handling system

11. EBNF Grammar Generation

Status:NOT STARTED Priority: Low - Ongoing maintenance effort, but enables frontend parsing

Core Concept:

Generate formal EBNF grammar specification from parser structure to enable frontend consumers to build parsers and syntax validators without server compilation.

Use Cases:

  • Client-side syntax highlighting with real-time error detection
  • Frontend form validation before sending formulas to server
  • Third-party parser implementations in different languages
  • IDE extensions and language servers

Implementation Steps:

  1. Add grammar metadata to parser methods:

    • Tag each parser method with EBNF rule information
    • Document precedence and associativity rules
    • Map recursive descent structure to formal productions
  2. Build zero-dependency EBNF generator:

    • Extract grammar rules from parser methods
    • Generate formal EBNF notation from parsing hierarchy
    • Handle precedence and associativity correctly
    • No external grammar libraries - implement ourselves
  3. Create grammar validation:

    • Ensure generated EBNF matches actual parser behavior
    • Test suite to verify EBNF accuracy
    • Automated sync checking between parser and grammar
  4. Export for consumption:

    • docs/GRAMMAR.ebnf - Formal grammar file
    • JSON format for programmatic consumption
    • Documentation explaining how to use the grammar

Key Features:

  • Frontend parsing capability - Enable client-side syntax validation
  • Language portability - EBNF can be used to generate parsers in other languages
  • Maintenance sync - Grammar stays current with parser changes
  • Zero dependencies - Custom implementation maintains project principles

12. Formula Language Server Protocol (LSP)

Status:NOT STARTED
Priority: Low - High effort, advanced developer tooling

Core Concept:

Full-featured Language Server Protocol implementation providing autocomplete, error diagnostics, hover information, and semantic analysis for formula files.

Features:

  • Real-time error diagnostics - Compile formulas and show errors with squiggles
  • Autocomplete - Function names, column references, relationship fields
  • Hover information - Function signatures, column types, relationship details
  • Go-to-definition - Navigate to column/relationship definitions
  • Semantic highlighting - Context-aware coloring beyond syntax
  • Code actions - Quick fixes for common errors
  • Workspace symbol search - Find columns and relationships across schema

Implementation Requirements:

  • Database schema integration - Load table and relationship metadata
  • Incremental compilation - Fast re-compilation for real-time diagnostics
  • LSP server - Node.js server implementing Language Server Protocol
  • VSCode client - Extension that communicates with LSP server
  • Configuration - Database connection settings for schema loading

Key Features:

  • Full semantic analysis - Uses actual formula compiler for validation
  • Database-aware - Knows about columns, types, and relationships
  • Multi-file support - Works across formula files in workspace
  • Professional IDE experience - All modern editor features for formulas

13. Logical Operators as Functions (✅ COMPLETE)

Status:COMPLETED Priority: High - Essential for conditional logic, depends on comparison operators

Core Concept:

Implement logical operations (AND, OR, NOT) as functions rather than infix operators for user familiarity and clarity.

Design Rationale:

  • Formula engine parity - Matches Excel/Google Sheets patterns users know
  • Easier reasoning - AND(cond1, cond2, cond3) clearer than precedence rules
  • Variadic support - Multiple conditions in single function call
  • Precedence elimination - No confusion about a OR b AND c evaluation order

Supported Functions:

  • AND(condition1, condition2, ...) - All conditions must be true (variadic)
  • OR(condition1, condition2, ...) - Any condition must be true (variadic)
  • NOT(condition) - Negates boolean result (single argument)

Implementation Steps:

  1. Added logical functions to compileFunction():

    • Integrated AND, OR, NOT into main function compilation system
    • Variadic argument validation for AND/OR (minimum 2 arguments)
    • Single argument validation for NOT
    • Boolean type validation for all arguments
  2. Sub-expression compilation infrastructure:

    • Each logical function argument compiled as complete formula expression
    • Recursive compilation handles nested expressions: AND(amount > 100, status = "approved")
    • Full support for comparison operators, null handling, and other boolean expressions
    • Proper type checking ensures all arguments evaluate to boolean
  3. Updated lexer and parser:

    • Removed infix logical operator tokens (AND, OR, NOT as keywords)
    • Treat logical operators as regular identifiers (function names)
    • Removed logical operator parsing methods (logicalOr, logicalAnd, logicalNot)
    • Updated parser hierarchy to go directly from comparison to parse
  4. PostgreSQL compilation:

    • AND(cond1, cond2, cond3)(cond1 AND cond2 AND cond3)
    • OR(cond1, cond2, cond3)(cond1 OR cond2 OR cond3)
    • NOT(condition)NOT (condition)
    • Proper parenthesization for correct precedence
    • PostgreSQL handles short-circuit evaluation optimization
  5. Integration with comparison operators:

    • Full compatibility with comparison operators (=, <, >, etc.)
    • Logical functions consume boolean results from comparisons
    • Type validation ensures arguments evaluate to boolean
    • Works with null handling functions (ISNULL, ISBLANK)

Implementation Results:

  • Complete replacement of infix logical operators with function-based approach
  • 28 comprehensive tests covering all functionality and error cases
  • Full compatibility with existing comparison operators and boolean expressions
  • Proper error handling with descriptive error messages
  • Type safety with boolean argument validation

Example Usage:

Input Formula:

AND(
  amount > 1000, 
  status = "approved", 
  date_funded < TODAY(),
  OR(priority = "high", amount > 50000),
  NOT(ISNULL(merchant_rel.business_name))
)

Generated SQL:

(
  s.amount > 1000 AND 
  s.status = 'approved' AND 
  s.date_funded < CURRENT_DATE AND
  (s.priority = 'high' OR s.amount > 50000) AND
  NOT (rel_merchant.business_name IS NULL)
)

Key Features:

  • Variadic arguments - AND/OR support 2+ conditions, NOT supports exactly 1
  • Sub-expression handling - Each argument compiled as complete expression
  • Type safety - Validates boolean expression arguments with clear error messages
  • Clear precedence - Function syntax eliminates operator precedence confusion
  • PostgreSQL optimization - Relies on database for short-circuit evaluation
  • Comprehensive testing - Full test coverage including nested expressions and error cases