using FreeSql; using FreeSql.DataAnnotations; using FreeSql.Internal.CommonProvider; using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Numerics; using System.Text; using System.Threading; /// /// QuestDB lambda 表达式树扩展解析 /// https://questdb.io/docs/reference/function/aggregation/ /// [ExpressionCall] public static class QuestFunc { public static ThreadLocal expContext = new ThreadLocal(); static ExpressionCallContext ec => expContext.Value; static T call(string rt) { ec.Result = rt; return default(T); } public static decimal avg(object value) => call($"avg({ec.ParsedContent["value"]})"); public static long count() => call($"count(*)"); public static long count(object column_name) => call($"count({ec.ParsedContent["column_name"]})"); public static long count_distinct(object column_name) => call($"count_distinct({ec.ParsedContent["column_name"]})"); public static string first(object column_name) => call($"first({ec.ParsedContent["column_name"]})"); public static string last(object column_name) => call($"last({ec.ParsedContent["column_name"]})"); public static decimal haversine_dist_deg(decimal lat, decimal lon, DateTime ts) => call($"haversine_dist_deg({ec.ParsedContent["lat"]},{ec.ParsedContent["lon"]},{ec.ParsedContent["ts"]})"); public static decimal ksum(object value) => call($"ksum({ec.ParsedContent["value"]})"); public static T max(T value) => call($"max({ec.ParsedContent["value"]})"); public static T min(T value) => call($"min({ec.ParsedContent["value"]})"); public static decimal nsum(object value) => call($"nsum({ec.ParsedContent["value"]})"); public static decimal stddev_samp(object value) => call($"stddev_samp({ec.ParsedContent["value"]})"); public static decimal sum(object value) => call($"sum({ec.ParsedContent["value"]})"); public static bool isOrdered(object column) => call($"isOrdered({ec.ParsedContent["column"]})"); public static T coalesce(object value1, object value2) => call($"coalesce({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]})"); public static T coalesce(object value1, object value2, object value3) => call($"coalesce({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]},{ec.ParsedContent["value3"]})"); public static T coalesce(object value1, object value2, object value3, object value4) => call($"coalesce({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]},{ec.ParsedContent["value3"]},{ec.ParsedContent["value4"]})"); public static T coalesce(object value1, object value2, object value3, object value4, object value5) => call($"coalesce({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]},{ec.ParsedContent["value3"]},{ec.ParsedContent["value4"]},{ec.ParsedContent["value5"]})"); public static T nullif(object value1, object value2) => call($"nullif({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]})"); public static DateTime date_trunc([RawValue] date_trunc_unit unit, object timestamp) => call($"date_trunc('{unit.ToString()}',{ec.ParsedContent["timestamp"]})"); public enum date_trunc_unit { millennium, decade, century, year, quarter, month, week, day, hour, minute, second, milliseconds, microseconds, } public static DateTime dateadd([RawValue] date_period period, [RawValue] long n, object startDate) => call($"dateadd('{(char)(int)period}',{n},{ec.ParsedContent["startDate"]})"); public static long datediff([RawValue] date_period period, object date1, object date2) => call($"datediff('{(char)period}',{ec.ParsedContent["date1"]},{ec.ParsedContent["date2"]})"); public enum date_period { second = 's', minute = 'm', hour = 'h', day = 'd', week = 'w', month = 'M', year = 'y', } public static int day(object timestamp) => call($"day({ec.ParsedContent["timestamp"]})"); public static int day_of_week(object timestamp) => call($"day_of_week({ec.ParsedContent["timestamp"]})"); public static int day_of_week_sunday_first(object timestamp) => call($"day_of_week_sunday_first({ec.ParsedContent["timestamp"]})"); public static long extract([RawValue] extract_unit unit, object timestamp) => call($"extract({unit.ToString()} from {ec.ParsedContent["timestamp"]})"); public enum extract_unit { millennium, epoch, decade, century, year, isoyear, /// /// day of year /// doy, quarter, month, week, /// /// day of week /// dow, isodow, day, hour, minute, second, microseconds, milliseconds, } public static int hour(object timestamp) => call($"hour({ec.ParsedContent["timestamp"]})"); public static bool is_leap_year(object timestamp) => call($"is_leap_year({ec.ParsedContent["timestamp"]})"); public static bool days_in_month(object timestamp) => call($"days_in_month({ec.ParsedContent["timestamp"]})"); public static int micros(object timestamp) => call($"micros({ec.ParsedContent["timestamp"]})"); public static int millis(object timestamp) => call($"millis({ec.ParsedContent["timestamp"]})"); public static int minute(object timestamp) => call($"minute({ec.ParsedContent["timestamp"]})"); public static int month(object timestamp) => call($"month({ec.ParsedContent["timestamp"]})"); public static DateTime now() => call($"now()"); public static DateTime pg_postmaster_start_time() => call($"pg_postmaster_start_time()"); public static int second(object timestamp) => call($"second({ec.ParsedContent["timestamp"]})"); /// /// Use now() with WHERE clause filter. /// /// public static DateTime systimestamp() => call($"systimestamp()"); /// /// Use now() with WHERE clause filter. /// /// public static DateTime sysdate() => call($"sysdate()"); public static DateTime timestamp_ceil([RawValue] timestamp_ceil_unit unit, object timestamp) => call($"timestamp_ceil({(char)unit},{ec.ParsedContent["timestamp"]})"); public static DateTime timestamp_floor([RawValue] timestamp_ceil_unit unit, object timestamp) => call($"timestamp_floor({(char)unit},{ec.ParsedContent["timestamp"]})"); public enum timestamp_ceil_unit { milliseconds = 'T', seconds = 's', minutes = 'm', hours = 'h', days = 'd', months = 'M', year = 'y', } public static DateTime timestamp_shuffle(object timestamp_1, object timestamp_2) => call($"timestamp_shuffle({ec.ParsedContent["timestamp_1"]},{ec.ParsedContent["timestamp_2"]})"); public static DateTime to_date(string str, string format) => call($"to_date({ec.ParsedContent["str"]},{ec.ParsedContent["format"]})"); public static string to_str(DateTime value, string format) => call($"to_str({ec.ParsedContent["value"]},{ec.ParsedContent["format"]})"); public static DateTime to_timestamp(string str, string format) => call($"to_timestamp({ec.ParsedContent["str"]},{ec.ParsedContent["format"]})"); public static DateTime to_timezone(DateTime timestamp, string timezone) => call($"to_timezone({ec.ParsedContent["timestamp"]},{ec.ParsedContent["timezone"]})"); public static DateTime to_utc(DateTime timestamp, string timezone) => call($"to_utc({ec.ParsedContent["timestamp"]},{ec.ParsedContent["timezone"]})"); public static int week_of_year(object timestamp) => call($"week_of_year({ec.ParsedContent["timestamp"]})"); public static int year(object timestamp) => call($"year({ec.ParsedContent["timestamp"]})"); public static T abs(T value) => call($"abs({ec.ParsedContent["value"]})"); public static decimal log(object value) => call($"log({ec.ParsedContent["value"]})"); public static decimal power(object _base, object exponent) => call($"power({ec.ParsedContent["_base"]},{ec.ParsedContent["exponent"]})"); public static T round(T value, int scale) => call($"round({ec.ParsedContent["value"]},{ec.ParsedContent["scale"]})"); public static T round_down(T value, int scale) => call($"round_down({ec.ParsedContent["value"]},{ec.ParsedContent["scale"]})"); public static T round_half_even(T value, int scale) => call($"round_half_even({ec.ParsedContent["value"]},{ec.ParsedContent["scale"]})"); public static T round_up(T value, int scale) => call($"round_up({ec.ParsedContent["value"]},{ec.ParsedContent["scale"]})"); public static string size_pretty(long value) => call($"size_pretty({ec.ParsedContent["value"]})"); public static decimal sqrt(object value) => call($"sqrt({ec.ParsedContent["value"]})"); public static bool rnd_boolean() => call($"rnd_boolean()"); public static byte rnd_byte() => call($"rnd_byte()"); public static byte rnd_byte(byte min, byte max) => call($"rnd_byte({ec.ParsedContent["min"]},{ec.ParsedContent["max"]})"); public static short rnd_short() => call($"rnd_short()"); public static short rnd_short(short min, short max) => call($"rnd_short({ec.ParsedContent["min"]},{ec.ParsedContent["max"]})"); public static int rnd_int() => call($"rnd_int()"); public static int rnd_int(int min, int max) => call($"rnd_int({ec.ParsedContent["min"]},{ec.ParsedContent["max"]})"); public static int rnd_int(int min, int max, int nanRate) => call($"rnd_int({ec.ParsedContent["min"]},{ec.ParsedContent["max"]},{ec.ParsedContent["nanRate"]})"); public static long rnd_long() => call($"rnd_long()"); public static long rnd_long(long min, long max) => call($"rnd_long({ec.ParsedContent["min"]},{ec.ParsedContent["max"]})"); public static long rnd_long(long min, long max, int nanRate) => call($"rnd_long({ec.ParsedContent["min"]},{ec.ParsedContent["max"]},{ec.ParsedContent["nanRate"]})"); public static BigInteger rnd_long256() => call($"rnd_long256()"); public static BigInteger rnd_long256(BigInteger min, BigInteger max) => call($"rnd_long256({ec.ParsedContent["min"]},{ec.ParsedContent["max"]})"); public static BigInteger rnd_long256(BigInteger min, BigInteger max, int nanRate) => call($"rnd_long256({ec.ParsedContent["min"]},{ec.ParsedContent["max"]},{ec.ParsedContent["nanRate"]})"); public static float rnd_float() => call($"rnd_float()"); public static float rnd_float(int nanRate) => call($"rnd_float({ec.ParsedContent["nanRate"]})"); public static double rnd_double() => call($"rnd_double()"); public static double rnd_double(int nanRate) => call($"rnd_double({ec.ParsedContent["nanRate"]})"); public static DateTime rnd_date(DateTime min, DateTime max) => call($"rnd_date({ec.ParsedContent["min"]},{ec.ParsedContent["max"]},0)"); public static DateTime rnd_date(DateTime min, DateTime max, int nanRate) => call($"rnd_date({ec.ParsedContent["min"]},{ec.ParsedContent["max"]},{ec.ParsedContent["nanRate"]})"); public static DateTime rnd_timestamp(DateTime min, DateTime max) => call($"rnd_timestamp({ec.ParsedContent["min"]},{ec.ParsedContent["max"]},0)"); public static DateTime rnd_timestamp(DateTime min, DateTime max, int nanRate) => call($"rnd_timestamp({ec.ParsedContent["min"]},{ec.ParsedContent["max"]},{ec.ParsedContent["nanRate"]})"); public static char rnd_char() => call($"rnd_char()"); public static string rnd_symbol([RawValue] string[] symbolList) => call($"rnd_symbol({string.Join(",", symbolList.Select(a => ec.FormatSql(a)))})"); public static string rnd_symbol(int list_size, int minLength, int maxLength, int nullRate) => call($"rnd_symbol({ec.ParsedContent["list_size"]},{ec.ParsedContent["minLength"]},{ec.ParsedContent["maxLength"]},{ec.ParsedContent["nullRate"]})"); public static string rnd_str([RawValue] string[] stringList) => call($"rnd_str({string.Join(",", stringList.Select(a => ec.FormatSql(a)))})"); public static string rnd_str(int list_size, int minLength, int maxLength, int nullRate) => call($"rnd_str({ec.ParsedContent["list_size"]},{ec.ParsedContent["minLength"]},{ec.ParsedContent["maxLength"]},{ec.ParsedContent["nullRate"]})"); public static byte[] rnd_bin() => call($"rnd_bin()"); public static byte[] rnd_bin(long minBytes, int maxBytes, int nullRate) => call($"rnd_bin({ec.ParsedContent["minBytes"]},{ec.ParsedContent["maxBytes"]},{ec.ParsedContent["nullRate"]})"); public static Guid rnd_uuid4() => call($"rnd_uuid4()"); public static byte[] rnd_geohash(int bits) => call($"rnd_geohash({ec.ParsedContent["bits"]})"); public static byte[] make_geohash(decimal lon, decimal lat, int bits) => call($"make_geohash({ec.ParsedContent["lon"]},{ec.ParsedContent["lat"]},{ec.ParsedContent["bits"]})"); public static string concat(object value1, object value2) => call($"coalesce({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]})"); public static string concat(object value1, object value2, object value3) => call($"coalesce({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]},{ec.ParsedContent["value3"]})"); public static string concat(object value1, object value2, object value3, object value4) => call($"coalesce({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]},{ec.ParsedContent["value3"]},{ec.ParsedContent["value4"]})"); public static string concat(object value1, object value2, object value3, object value4, object value5) => call($"coalesce({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]},{ec.ParsedContent["value3"]},{ec.ParsedContent["value4"]},{ec.ParsedContent["value5"]})"); public static string concat(object value1, object value2, object value3, object value4, object value5, object value6) => call($"coalesce({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]},{ec.ParsedContent["value3"]},{ec.ParsedContent["value4"]},{ec.ParsedContent["value5"]},{ec.ParsedContent["value6"]})"); public static string concat(object value1, object value2, object value3, object value4, object value5, object value6, object value7) => call($"coalesce({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]},{ec.ParsedContent["value3"]},{ec.ParsedContent["value4"]},{ec.ParsedContent["value5"]},{ec.ParsedContent["value6"]},{ec.ParsedContent["value7"]})"); public static string concat(object value1, object value2, object value3, object value4, object value5, object value6, object value7, object value8) => call($"coalesce({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]},{ec.ParsedContent["value3"]},{ec.ParsedContent["value4"]},{ec.ParsedContent["value5"]},{ec.ParsedContent["value6"]},{ec.ParsedContent["value7"]},{ec.ParsedContent["value8"]})"); public static string concat(object value1, object value2, object value3, object value4, object value5, object value6, object value7, object value8, object value9) => call($"coalesce({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]},{ec.ParsedContent["value3"]},{ec.ParsedContent["value4"]},{ec.ParsedContent["value5"]},{ec.ParsedContent["value6"]},{ec.ParsedContent["value7"]},{ec.ParsedContent["value8"]},{ec.ParsedContent["value9"]})"); public static string concat(object value1, object value2, object value3, object value4, object value5, object value6, object value7, object value8, object value9, object value10) => call($"coalesce({ec.ParsedContent["value1"]},{ec.ParsedContent["value2"]},{ec.ParsedContent["value3"]},{ec.ParsedContent["value4"]},{ec.ParsedContent["value5"]},{ec.ParsedContent["value6"]},{ec.ParsedContent["value7"]},{ec.ParsedContent["value8"]},{ec.ParsedContent["value9"]},{ec.ParsedContent["value10"]})"); public static long length(object value) => call($"length({ec.ParsedContent["value"]})"); public static string left(string str, int count) => call($"left({ec.ParsedContent["str"]},{ec.ParsedContent["count"]})"); public static string right(string str, int count) => call($"right({ec.ParsedContent["str"]},{ec.ParsedContent["count"]})"); public static int strpos(object str, string substr) => call($"strpos({ec.ParsedContent["str"]},{ec.ParsedContent["substr"]})"); public static string substring(string str, int start, int length) => call($"substring({ec.ParsedContent["str"]},{ec.ParsedContent["start"]},{ec.ParsedContent["length"]})"); public static string lower(string str) => call($"lower({ec.ParsedContent["str"]})"); public static string upper(string str) => call($"upper({ec.ParsedContent["str"]})"); public static DateTime timestamp_sequence(DateTime startTimestamp, long step) => call($"timestamp_sequence({ec.ParsedContent["startTimestamp"]},{ec.ParsedContent["step"]})"); public static string regexp_replace(string str1, string regex, string str2) => call($"regexp_replace({ec.ParsedContent["str1"]},{ec.ParsedContent["regex"]},{ec.ParsedContent["str2"]})"); public static bool regex_match(string str, string regex) => call($"{ec.ParsedContent["str"]} ~ {ec.ParsedContent["regex"]}"); public static bool regex_not_match(string str, string regex) => call($"{ec.ParsedContent["str"]} !~ {ec.ParsedContent["regex"]}"); public static bool like(string str, string pattern) => call($"{ec.ParsedContent["str"]} LIKE {ec.ParsedContent["pattern"]}"); public static bool not_like(string str, string pattern) => call($"{ec.ParsedContent["str"]} NOT LIKE {ec.ParsedContent["pattern"]}"); public static bool ilike(string str, string pattern) => call($"{ec.ParsedContent["str"]} ILIKE {ec.ParsedContent["pattern"]}"); public static bool not_ilike(string str, string pattern) => call($"{ec.ParsedContent["str"]} NOT ILIKE {ec.ParsedContent["pattern"]}"); public static bool within(object geo, object geohash1) => call($"{ec.ParsedContent["str"]} within({ec.ParsedContent["geohash1"]})"); public static bool within(object geo, object geohash1, object geohash2) => call($"{ec.ParsedContent["str"]} within({ec.ParsedContent["geohash1"]},{ec.ParsedContent["geohash2"]})"); public static bool within(object geo, object geohash1, object geohash2, object geohash3) => call($"{ec.ParsedContent["str"]} within({ec.ParsedContent["geohash1"]},{ec.ParsedContent["geohash2"]},{ec.ParsedContent["geohash3"]})"); public static bool within(object geo, object geohash1, object geohash2, object geohash3, object geohash4) => call($"{ec.ParsedContent["str"]} within({ec.ParsedContent["geohash1"]},{ec.ParsedContent["geohash2"]},{ec.ParsedContent["geohash3"]},{ec.ParsedContent["geohash4"]})"); public static bool within(object geo, object geohash1, object geohash2, object geohash3, object geohash4, object geohash5) => call($"{ec.ParsedContent["str"]} within({ec.ParsedContent["geohash1"]},{ec.ParsedContent["geohash2"]},{ec.ParsedContent["geohash3"]},{ec.ParsedContent["geohash4"]},{ec.ParsedContent["geohash5"]})"); } partial class QuestDbGlobalExtensions { /// /// QuestDB lambda 表达式树扩展解析 /// fsql.SelectLongSequence(10, () => new { str1 = qdbfunc.rnd_str(10, 5, 8, 0), ... })... /// SELECT rnd_str(10,5,8,0) FROM long_sequence(10) /// public static ISelect SelectLongSequence(this IFreeSql fsql, long iterations, Expression> selector) { var selector2 = Expression.Lambda>(selector.Body, Expression.Parameter(typeof(object), "a")); var tablename = $"(long_sequence ({iterations}))"; return fsql.Select().AsTable((t, old) => tablename).WithTempQuery(selector2); } /// /// QuestDB lambda 表达式树扩展解析 /// fsql.SelectTimestampSequence(10, () => new { str1 = qdbfunc.rnd_str(10, 5, 8, 0), ... })... /// SELECT rnd_str(10,5,8,0) FROM long_sequence(10) /// public static ISelect SelectTimestampSequence(this IFreeSql fsql, DateTime startTimestamp, TimeSpan step, Expression> selector) { var selector2 = Expression.Lambda>(selector.Body, Expression.Parameter(typeof(object), "a")); var tablename = $"(timestamp_sequence (to_timestamp('{startTimestamp.ToString("yyyy-MM-dd HH:mm:ss")}', 'yyyy-MM-dd HH:mm:ss'), {Math.Ceiling(step.TotalMilliseconds / 1000)}))"; return fsql.Select().AsTable((t, old) => tablename).WithTempQuery(selector2); } }