// Callback function to evaluate an expression public object EvaluateCommon(ExpressionEval expr, FuncTypes functype, object[] values, int haccum, int naccum) { var lookup = GetLookup(expr, values); TypedValue retval = null; switch (functype) { case FuncTypes.Open: retval = expr.EvalOpen(lookup); break; case FuncTypes.Predicate: retval = expr.EvalPred(lookup); break; case FuncTypes.Aggregate: case FuncTypes.Ordered: var accblk = GetAccum(haccum, naccum); retval = expr.EvalHasFold(lookup, accblk as AccumulatorBlock); accblk.Result = retval; PutAccum(haccum, accblk); break; } return(SqlTarget.ToObjectDict[retval.DataType.BaseType](retval)); }
public override DataTable Restrict(ExpressionEval expr) { _database.RegisterExpressions(expr); var newtable = AddWhere(expr, HasGroupBy); Logger.WriteLine(4, "[Res '{0}']", newtable); return(newtable); }
//--- functions to manipulate queries // generate SQL code for restrict DataTableSql AddWhere(ExpressionEval expr, bool hasgroupby) { if (SqlWhereText != null) { return(DataTableSql.CreateFromSubquery(this).AddWhere(expr, hasgroupby)); } SqlWhereText = (hasgroupby) ? _gen.Having(expr) : _gen.Where(expr); return(this); }
// Reorder expressions to match heading public ExpressionEval[] Reorder(ExpressionEval[] exprs) { Logger.Assert(exprs.Length == Degree && exprs.All(e => FindIndex(e.Name) >= 0), "reorder mismatch"); var newexprs = new ExpressionEval[exprs.Length]; foreach (var e in exprs) { newexprs[FindIndex(e.Name)] = e; } return(newexprs); }
// Return value of attribute with tuple indexing // FIX: needs to get from this row to some other row via parent table to use as lookup public TypedValue ValueOffset(ExpressionEval expr, int index, OffsetModes mode) { var parent = Parent as DataTableLocal; Logger.Assert(parent != null); var ord = OrderedIndex.Offset(this, index, mode); var value = (ord == -1) ? expr.ReturnType.DefaultValue() : expr.EvalOpen(parent.GetRow(ord)); return(value); }
// Restrict -- new table containing rows that pass the test public override DataTable Restrict(ExpressionEval expr) { var newtable = DataTableLocal.Create(Heading); foreach (var row in GetRows()) //TODO:Enumerable { if (expr.EvalPred(row).Value) { newtable.AddRow(row); } } Logger.WriteLine(4, "Restrict {0}", newtable); return(newtable); }
// create a lookup for an expression from a set of values LookupHolder GetLookup(ExpressionEval expr, object[] ovalues) { // build the lookup var lookup = new LookupHolder(); for (int i = 0; i < expr.NumArgs; ++i) { var datatype = expr.Lookup.Columns[i].DataType; var value = (ovalues[i] == null) ? datatype.DefaultValue() : SqlTarget.FromObjectDict[datatype.BaseType](ovalues[i], datatype); lookup.LookupDict.Add(expr.Lookup.Columns[i].Name, value); } return(lookup); }
// Execute an update that is a DELETE (no exprs) or UPDATE public override DataTable UpdateTransform(ExpressionEval pred, ExpressionEval[] exprs) { _database.RegisterExpressions(pred); _database.RegisterExpressions(exprs); if (exprs.Length == 0) { string sql = _gen.Delete(TableName, pred); _database.OpenStatement(); _database.ExecuteCommand(sql); _database.CloseStatement(); } else { string sql = _gen.Update(TableName, pred, exprs); _database.OpenStatement(); _database.ExecuteCommand(sql); _database.CloseStatement(); } return(this); }
// Register an expression that will be used in a query // An expression is the RHS of an attribute assignment (not just a function) // Can only be Rename, Project, Open, Aggregate: only last two needed public bool RegisterExpression(ExpressionEval expr, int naccum) { if (!(expr.IsOpen || expr.HasFold)) { return(true); } // Check whether new registration or update (to new evaluator) // TODO: check it's the same expr var updating = ExprDict.ContainsKey(expr.Serial); var name = SqlGen.FuncName(expr); ExprDict[expr.Serial] = expr; Logger.WriteLine(3, $"Register {name} upd={updating} nacc={naccum} expr={expr}"); if (updating) { return(true); } // notify database, set up callbacks // may require sql to register (PG) // FIX: would be better in PostgresDatabase, but does not have access to sql gen (and data types). var args = expr.Lookup.Columns.Select(c => ToSqlCommon[c.DataType.BaseType]).ToArray(); var retn = ToSqlCommon[expr.ReturnType.BaseType]; if (expr.HasFold) { // note: type must match low level wrappers var stype = DataTypes.Number; var init = NumberValue.Zero; var col0 = new DataColumn[] { DataColumn.Create("_state_", stype) }; OptionalExpressionSql(_sqlgen.CreateFunction(name, col0.Concat(expr.Lookup.Columns).ToArray(), stype)); OptionalExpressionSql(_sqlgen.CreateFunction(name + "F", col0, expr.ReturnType)); OptionalExpressionSql(_sqlgen.CreateAggregate(name, expr.Lookup.Columns, stype, init, name, name + "F")); return(FunctionCreator.CreateAggFunction(name, expr.Serial, naccum, args, retn)); } // expr.IsOpen OptionalExpressionSql(_sqlgen.CreateFunction(name, expr.Lookup.Columns, expr.ReturnType)); return(FunctionCreator.CreateFunction(name, FuncTypes.Open, expr.Serial, args, retn)); }
// Recursive expansion // Creates new empty table, add seed, join op (only union for now) and expression public override DataTable Recurse(int flags, ExpressionEval expr) { Logger.WriteLine(4, "Recurse {0} {1}", flags, expr); Logger.Assert(expr.ReturnType == DataType); var newtable = DataTableLocal.Create(Heading); foreach (var row in _rows) { newtable.AddRaw(row); } // by ordinal, to main position and notice new rows for (var ord = 0; ord < newtable._rows.Count; ++ord) { var newrows = expr.EvalOpen(newtable._rows[ord]).AsTable(); foreach (var row in newrows.GetRows()) { newtable.AddRow(row); } } return(newtable); }
// Update Transform, handles Delete and Update public override DataTable UpdateTransform(ExpressionEval pred, ExpressionEval[] exprs) { Logger.WriteLine(4, "UpdateTransform {0}", Heading); var updating = (exprs.Length > 0); // false means this is just a delete var newexprs = (updating) ? Heading.Reorder(exprs) : null; // pass 1 - new rows var relins = DataTableLocal.Create(Heading); for (var ord = 0; ord < _rows.Count;) //TODO:Enumerable { if (pred.EvalPred(_rows[ord]).Value) { if (updating) { //if (exprs.Length > 0) relins.AddRow(_rows[ord].Transform(Heading, newexprs)); } // deleting a row will replace row at ord with a different one, not yet tested DeleteRaw(ord); } else { ord++; } } foreach (var row in relins.GetRows()) { AddRaw(row); } // TODO: update persistence store Logger.WriteLine(4, "[UpSelect={0}]", this); return(this); }
public override DataTable Recurse(int flags, ExpressionEval expr) { throw ProgramError.Fatal("Sql data", "while not supported"); }
public abstract DataTable UpdateTransform(ExpressionEval pred, ExpressionEval[] exprs);
public abstract DataTable Recurse(int flags, ExpressionEval expr);
public abstract DataTable Restrict(ExpressionEval expr);
// Evaluation engine for ByteCode TypedValue Run(ByteCode bcode, TypedValue aggregate, AccumulatorBlock accblock) { TypedValue retval = null; var reader = PersistReader.Create(bcode.bytes); while (reader.More) { var opcode = reader.ReadOpcode(); switch (opcode) { // Known literal, do not translate into value case Opcodes.LDVALUE: PushStack(reader.ReadValue()); break; // Known catalog variable, look up value //case Opcodes.LDCAT: // var catnam = reader.ReadString(); // var catval = CatVars.GetValue(catnam); // Logger.Assert(catval != null, $"{opcode}:{catnam}"); // if (catval.DataType is DataTypeCode) // catval = this.Exec((catval as CodeValue).Value.Code); // _stack.Push(catval); // break; // Catalog variable, look up value (could be code) case Opcodes.LDCAT: var catnam = reader.ReadString(); var catval = CatVars.GetValue(catnam); Logger.Assert(catval != null, $"{opcode}:{catnam}"); _stack.Push(catval); break; // Catalog variable, must be code, evaluate case Opcodes.LDCATV: var ctvnam = reader.ReadString(); var ctvval = CatVars.GetValue(ctvnam) as CodeValue; Logger.Assert(ctvval != null, $"{opcode}:{ctvnam}"); _stack.Push(this.Exec((ctvval as CodeValue).Value.Code)); break; // Catalog variable, must be code, as code value case Opcodes.LDCATR: var ctrnam = reader.ReadString(); var ctrval = CatVars.GetValue(ctrnam) as CodeValue; Logger.Assert(ctrval != null, $"{opcode}:{ctrnam}"); PushStack(CodeValue.Create(ExpressionEval.Create(this, ctrval.Value))); break; // Load value obtained using lookup by name case Opcodes.LDFIELD: var fldval = TypedValue.Empty; var fldnam = reader.ReadString(); var fldok = LookupValue(fldnam, ref fldval); Logger.Assert(fldok, $"{opcode}:{fldnam}"); PushStack(fldval); break; // Load aggregate value or use specified start value if not available case Opcodes.LDAGG: var aggval = reader.ReadValue(); PushStack(aggregate ?? aggval); break; // load accumulator by index, or fixed value if not available case Opcodes.LDACC: var accnum = reader.ReadInteger(); var accval = reader.ReadValue(); PushStack(accblock == null ? accval : accblock[accnum]); break; // Load a segment of code for later call, with this evaluator packaged in case Opcodes.LDSEG: var segexp = reader.ReadExpr(); var segval = CodeValue.Create(ExpressionEval.Create(this, segexp)); PushStack(segval); break; case Opcodes.LDLOOKUP: var lkpobj = PointerValue.Create(_lookups.Peek() as object); PushStack(lkpobj); break; case Opcodes.LDACCBLK: var acbobj = PointerValue.Create(accblock as object); PushStack(acbobj); break; case Opcodes.LDCOMP: var cmpudt = _stack.Pop() as UserValue; var cmpval = cmpudt.GetComponentValue(reader.ReadString()); PushStack(cmpval); break; case Opcodes.LDFIELDT: var fdttup = _stack.Pop() as TupleValue; var fdtval = fdttup.GetFieldValue(reader.ReadString()); PushStack(fdtval); break; // Call a function, fixed or variable arg count case Opcodes.CALL: case Opcodes.CALLV: case Opcodes.CALLVT: var calname = reader.ReadString(); var calmeth = typeof(Builtin).GetMethod(calname); var calnargs = reader.ReadByte(); var calnvargs = reader.ReadByte(); var calargs = new object[calnargs]; var calargx = calargs.Length - 1; if (opcode == Opcodes.CALLV) { var vargs = new CodeValue[calnvargs]; for (var j = vargs.Length - 1; j >= 0; --j) { vargs[j] = _stack.Pop() as CodeValue; } calargs[calargx--] = vargs; } else if (opcode == Opcodes.CALLVT) { var vargs = new TypedValue[calnvargs]; for (var j = vargs.Length - 1; j >= 0; --j) { vargs[j] = _stack.Pop() as TypedValue; } calargs[calargx--] = vargs; } for (; calargx >= 0; --calargx) { calargs[calargx] = _stack.Pop(); } var ret = calmeth.Invoke(_builtin, calargs) as TypedValue; _stack.Push(ret); //if (ret.DataType != DataTypes.Void) // _stack.Push(ret); break; case Opcodes.EOS: retval = _stack.Pop(); //retval = (_stack.Count > 0) ? _stack.Pop() : VoidValue.Void; break; default: throw new NotImplementedException(opcode.ToString()); } } if (retval == null) { retval = _stack.Pop(); } //Logger.Assert(retval != null, "stack"); return(retval); }