EV-ClickHouse
view release on metacpan or search on metacpan
snprintf(buf, sizeof(buf), "%04d-%02d-%02d",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
return newSVpvn(buf, 10);
}
/* Convert epoch seconds to "YYYY-MM-DD HH:MM:SS" in UTC. */
/* Format a UNIX epoch as "YYYY-MM-DD HH:MM:SS". use_local=1 formats in the
* caller's currently-active TZ (set via set_tz); otherwise UTC. */
static SV* epoch_to_datetime_sv_ex(uint32_t epoch, int use_local) {
time_t t = (time_t)epoch;
struct tm tm;
char buf[20];
struct tm *r = use_local ? localtime_r(&t, &tm) : gmtime_r(&t, &tm);
if (!r) return newSVpvn("0000-00-00 00:00:00", 19);
snprintf(buf, sizeof(buf), "%04d-%02d-%02d %02d:%02d:%02d",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
return newSVpvn(buf, 19);
}
/* Convert DateTime64 to "YYYY-MM-DD HH:MM:SS.fff...", use_local=1 for localtime */
static SV* dt64_to_datetime_sv_ex(int64_t val, int precision, int use_local) {
int64_t scale = 1;
int p;
int64_t epoch, frac;
time_t t;
struct tm tm;
char buf[32];
int n;
for (p = 0; p < precision; p++) scale *= 10;
epoch = val / scale;
frac = val % scale;
if (frac < 0) { epoch--; frac += scale; }
t = (time_t)epoch;
if (use_local) {
if (!localtime_r(&t, &tm)) return newSVpvn("0000-00-00 00:00:00", 19);
} else {
if (!gmtime_r(&t, &tm)) return newSVpvn("0000-00-00 00:00:00", 19);
}
n = snprintf(buf, sizeof(buf), "%04d-%02d-%02d %02d:%02d:%02d",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
if (precision > 0 && n < 30) {
char fracbuf[16];
int fi;
snprintf(fracbuf, sizeof(fracbuf), "%0*lld", precision, (long long)frac);
buf[n++] = '.';
for (fi = 0; fi < precision && n < 31; fi++)
buf[n++] = fracbuf[fi];
}
return newSVpvn(buf, n);
}
/* Set TZ env var and tzset(); returns saved old TZ (caller frees).
* Safe because EV is single-threaded and the set_tz / decode / restore_tz
* window contains no Perl callback dispatch. */
static char* set_tz(const char *tz) {
char *saved = safe_strdup(getenv("TZ"));
setenv("TZ", tz, 1);
tzset();
return saved;
}
/* Restore TZ from saved value (which may be NULL), then free saved */
static void restore_tz(char *saved) {
if (saved) {
setenv("TZ", saved, 1);
Safefree(saved);
} else {
unsetenv("TZ");
}
tzset();
}
/* Compute 10^n as double */
static double pow10_int(int n) {
double r = 1.0;
int i;
for (i = 0; i < n; i++) r *= 10.0;
return r;
}
/* Parse enum label for a given code from type string like "Enum8('a'=1,'b'=2)" */
static SV* enum_label_for_code(const char *type_str, size_t type_str_len, int code) {
/* Find the opening '(' */
const char *p = memchr(type_str, '(', type_str_len);
const char *end;
if (!p) return newSViv(code);
p++;
end = type_str + type_str_len - 1; /* skip closing ')' */
while (p < end) {
/* Skip whitespace */
while (p < end && *p == ' ') p++;
if (p >= end || *p != '\'') break;
p++; /* skip opening quote */
/* Read label (handle escaped quotes) */
{
const char *label_start = p;
size_t label_len;
int val;
while (p < end && !(*p == '\'' && (p + 1 >= end || *(p+1) != '\''))) {
if (*p == '\'' && p + 1 < end && *(p+1) == '\'') { p += 2; continue; }
p++;
}
label_len = p - label_start;
if (p < end) p++; /* skip closing quote */
/* Skip ' = ' */
while (p < end && (*p == ' ' || *p == '=')) p++;
/* Read integer value */
val = (int)strtol(p, NULL, 10);
if (val == code) return newSVpvn(label_start, label_len);
/* Skip to next entry */
while (p < end && *p != ',') p++;
if (p < end) p++; /* skip comma */
}
}
/* Not found â return numeric code */
return newSViv(code);
}
/*
* Decode a column of `nrows` values from the native binary format.
* Returns an array of SVs (one per row). Returns NULL on failure.
( run in 0.765 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )