<commit_before>package tink.sql.macros;

import haxe.macro.Context;
import tink.sql.Target.JoinType;
import haxe.macro.Type;
import haxe.macro.Expr;

using haxe.macro.Tools;
using tink.MacroApi;

class Joins { 
  
  static function getRow(e:Expr)
    return Context.typeof(macro @:pos(e.pos) {
      var source = $e, x = null;
      source.stream().forEach(function (y) { x = y; return tink.streams.Stream.Handled.Resume; } );
      x;
    });
    
  static public function perform(type:JoinType, left:Expr, right:Expr) {
        
    var rowFields = new Array<Field>(),
        fieldsObj #if (haxe_ver >= 4) : Array<ObjectField> #end = [];
    
    function traverse(e:Expr, fieldsExpr:Expr, nullable:Bool) {
      
      function add(name, type, ?nested) {
        
        rowFields.push({
          name: name,
          pos: e.pos,
          kind: FProp('default', 'null', type),
        });              
        
        fieldsObj.push({
          field: name,
          expr: 
            if (nested) macro $fieldsExpr.fields.$name
            else macro $fieldsExpr.fields
        });
      }
      
      var parts = Filters.getArgs(e);
      switch parts {
        case [single]:
          
          add(single.name, getRow(e).toComplex());
          
        default:
          switch getRow(e).reduce().toComplex() {
            case TAnonymous([for (f in _) f.name => f] => byName):
              for (p in parts)
                add(p.name, switch byName[p.name] {
                  case null: e.reject('Lost track of ${p.name}');
                  case f: (f:Member).getVar().sure().type;
                }, true);
            default:
              e.reject();
          }
      }
      return parts;
    }
    
    var total = traverse(left, macro left, type == Right);
    total = total.concat(traverse(right, macro right, type == Left));//need separate statements because of evaluation order
    
    var f:Function = {
      expr: macro return null,
      ret: macro : tink.sql.Expr.Condition,
      args: [for (a in total) {
        name: a.name,
        type: a.t.toComplex({ direct: true }),
      }],
    }
    
    var rowType = TAnonymous(rowFields);
    var filterType = f.asExpr().typeof().sure().toComplex( { direct: true } );
    
    var ret = macro @:pos(left.pos) @:privateAccess {
      
      var left = $left,
          right = $right;
      
      function toCondition(filter:$filterType)
        return ${(macro filter).call([for (field in fieldsObj) field.expr])};
        
      var ret = new tink.sql.Dataset.JoinPoint(
        function (cond:$filterType) return new tink.sql.Dataset.Selectable(
          left.cnx, 
          ${EObjectDecl(fieldsObj).at()},
          tink.sql.Target.TJoin(
            left.target, 
            right.target, 
            ${joinTypeExpr(type)}, 
            toCondition(cond) && left.condition.where && right.condition.where
          ), 
          toCondition
        )
      );
      
      if (false) {
        (ret.on(null).stream() : tink.streams.RealStream<$rowType>);
      }
      
      ret;
      
    }
    
    return ret;
  }
  
  static function joinTypeExpr(t:JoinType)
    return switch t {
      case Inner: macro Inner;
      case Left: macro Left;
      case Right: macro Right;
    }
  
}<commit_msg>Fix passing conditions in joins<commit_after>package tink.sql.macros;

import haxe.macro.Context;
import tink.sql.Target.JoinType;
import haxe.macro.Type;
import haxe.macro.Expr;

using haxe.macro.Tools;
using tink.MacroApi;

class Joins { 
  
  static function getRow(e:Expr)
    return Context.typeof(macro @:pos(e.pos) {
      var source = $e, x = null;
      source.stream().forEach(function (y) { x = y; return tink.streams.Stream.Handled.Resume; } );
      x;
    });
    
  static public function perform(type:JoinType, left:Expr, right:Expr) {
        
    var rowFields = new Array<Field>(),
        fieldsObj #if (haxe_ver >= 4) : Array<ObjectField> #end = [];
    
    function traverse(e:Expr, fieldsExpr:Expr, nullable:Bool) {
      
      function add(name, type, ?nested) {
        
        rowFields.push({
          name: name,
          pos: e.pos,
          kind: FProp('default', 'null', type),
        });              
        
        fieldsObj.push({
          field: name,
          expr: 
            if (nested) macro $fieldsExpr.fields.$name
            else macro $fieldsExpr.fields
        });
      }
      
      var parts = Filters.getArgs(e);
      switch parts {
        case [single]:
          
          add(single.name, getRow(e).toComplex());
          
        default:
          switch getRow(e).reduce().toComplex() {
            case TAnonymous([for (f in _) f.name => f] => byName):
              for (p in parts)
                add(p.name, switch byName[p.name] {
                  case null: e.reject('Lost track of ${p.name}');
                  case f: (f:Member).getVar().sure().type;
                }, true);
            default:
              e.reject();
          }
      }
      return parts;
    }
    
    var total = traverse(left, macro left, type == Right);
    total = total.concat(traverse(right, macro right, type == Left));//need separate statements because of evaluation order
    
    var f:Function = {
      expr: macro return null,
      ret: macro : tink.sql.Expr.Condition,
      args: [for (a in total) {
        name: a.name,
        type: a.t.toComplex({ direct: true }),
      }],
    }
    
    var rowType = TAnonymous(rowFields);
    var filterType = f.asExpr().typeof().sure().toComplex( { direct: true } );
    
    var ret = macro @:pos(left.pos) @:privateAccess {
      
      var left = $left,
          right = $right;
      
      function toCondition(filter:$filterType)
        return ${(macro filter).call([for (field in fieldsObj) field.expr])};
        
      var ret = new tink.sql.Dataset.JoinPoint(
        function (cond:$filterType) return new tink.sql.Dataset.Selectable(
          left.cnx, 
          ${EObjectDecl(fieldsObj).at()},
          tink.sql.Target.TJoin(
            left.target, 
            right.target, 
            ${joinTypeExpr(type)}, 
            toCondition(cond)
          ),
          toCondition,
          {
            where: left.condition.where && right.condition.where,
            having: left.condition.having && right.condition.having
          }
        )
      );
      
      if (false) {
        (ret.on(null).stream() : tink.streams.RealStream<$rowType>);
      }
      
      ret;
      
    }
    
    return ret;
  }
  
  static function joinTypeExpr(t:JoinType)
    return switch t {
      case Inner: macro Inner;
      case Left: macro Left;
      case Right: macro Right;
    }
  
}
{'fraction_non_alphanumeric': 0.12762159750111557, 'fraction_numerical': 0.00029748624126134165, 'mean_word_length': 1.7534807534807535, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4200641', 'n_tokens_mistral': 2085, 'n_tokens_neox': 2027, 'n_words': 464}