FORMAT TabSeparated */
|
1547
|
0
|
|
|
|
|
|
size_t isql_cap = table_len + 64; |
|
1548
|
|
|
|
|
|
|
char *insert_sql; |
|
1549
|
0
|
|
|
|
|
|
Newx(insert_sql, isql_cap, char); |
|
1550
|
0
|
|
|
|
|
|
int isql_len = snprintf(insert_sql, isql_cap, |
|
1551
|
|
|
|
|
|
|
"INSERT INTO %.*s FORMAT TabSeparated", |
|
1552
|
|
|
|
|
|
|
(int)table_len, table); |
|
1553
|
|
|
|
|
|
|
|
|
1554
|
0
|
|
|
|
|
|
const char *query_id = NULL; |
|
1555
|
0
|
|
|
|
|
|
STRLEN query_id_len = 0; |
|
1556
|
0
|
|
|
|
|
|
size_t params_cap = 128 |
|
1557
|
0
|
0
|
|
|
|
|
+ (self->database ? strlen(self->database) * 3 : 0) |
|
1558
|
0
|
0
|
|
|
|
|
+ (self->session_id ? strlen(self->session_id) * 3 : 0) |
|
1559
|
0
|
|
|
|
|
|
+ (size_t)isql_len * 3 |
|
1560
|
0
|
|
|
|
|
|
+ settings_url_params_size(defaults, overrides); |
|
1561
|
|
|
|
|
|
|
char *params; |
|
1562
|
0
|
|
|
|
|
|
size_t plen = 0; |
|
1563
|
0
|
|
|
|
|
|
Newx(params, params_cap, char); |
|
1564
|
0
|
0
|
|
|
|
|
if (self->database) { |
|
1565
|
0
|
|
|
|
|
|
size_t db_len = strlen(self->database); |
|
1566
|
|
|
|
|
|
|
char *enc_db; |
|
1567
|
0
|
|
|
|
|
|
Newx(enc_db, db_len * 3 + 1, char); |
|
1568
|
0
|
|
|
|
|
|
size_t enc_len = url_encode(self->database, db_len, enc_db); |
|
1569
|
0
|
|
|
|
|
|
plen = (size_t)snprintf(params, params_cap, "?database=%.*s&wait_end_of_query=1", |
|
1570
|
|
|
|
|
|
|
(int)enc_len, enc_db); |
|
1571
|
0
|
|
|
|
|
|
Safefree(enc_db); |
|
1572
|
|
|
|
|
|
|
} else { |
|
1573
|
0
|
|
|
|
|
|
plen = (size_t)snprintf(params, params_cap, "?wait_end_of_query=1"); |
|
1574
|
|
|
|
|
|
|
} |
|
1575
|
0
|
0
|
|
|
|
|
if (self->session_id) { |
|
1576
|
0
|
|
|
|
|
|
size_t sid_len = strlen(self->session_id); |
|
1577
|
|
|
|
|
|
|
char *enc_sid; |
|
1578
|
0
|
|
|
|
|
|
Newx(enc_sid, sid_len * 3 + 1, char); |
|
1579
|
0
|
|
|
|
|
|
size_t enc_len = url_encode(self->session_id, sid_len, enc_sid); |
|
1580
|
0
|
|
|
|
|
|
plen += (size_t)snprintf(params + plen, params_cap - plen, |
|
1581
|
|
|
|
|
|
|
"&session_id=%.*s", (int)enc_len, enc_sid); |
|
1582
|
0
|
|
|
|
|
|
Safefree(enc_sid); |
|
1583
|
|
|
|
|
|
|
} |
|
1584
|
|
|
|
|
|
|
{ |
|
1585
|
|
|
|
|
|
|
char *enc_q; |
|
1586
|
0
|
|
|
|
|
|
Newx(enc_q, isql_len * 3 + 1, char); |
|
1587
|
0
|
|
|
|
|
|
size_t enc_len = url_encode(insert_sql, isql_len, enc_q); |
|
1588
|
0
|
|
|
|
|
|
Safefree(insert_sql); |
|
1589
|
0
|
|
|
|
|
|
plen += (size_t)snprintf(params + plen, params_cap - plen, |
|
1590
|
|
|
|
|
|
|
"&query=%.*s", (int)enc_len, enc_q); |
|
1591
|
0
|
|
|
|
|
|
Safefree(enc_q); |
|
1592
|
|
|
|
|
|
|
} |
|
1593
|
0
|
|
|
|
|
|
plen = append_settings_url_params(params, plen, |
|
1594
|
|
|
|
|
|
|
defaults, overrides, |
|
1595
|
|
|
|
|
|
|
&query_id, &query_id_len); |
|
1596
|
0
|
0
|
|
|
|
|
if (query_id) { |
|
1597
|
0
|
|
|
|
|
|
size_t need = plen + 10 + query_id_len * 3 + 1; |
|
1598
|
0
|
0
|
|
|
|
|
if (need > params_cap) { |
|
1599
|
0
|
|
|
|
|
|
params_cap = need; |
|
1600
|
0
|
|
|
|
|
|
Renew(params, params_cap, char); |
|
1601
|
|
|
|
|
|
|
} |
|
1602
|
0
|
|
|
|
|
|
plen += (size_t)snprintf(params + plen, params_cap - plen, "&query_id="); |
|
1603
|
0
|
|
|
|
|
|
plen += url_encode(query_id, query_id_len, params + plen); |
|
1604
|
|
|
|
|
|
|
} |
|
1605
|
0
|
|
|
|
|
|
params[plen] = '\0'; |
|
1606
|
|
|
|
|
|
|
|
|
1607
|
0
|
|
|
|
|
|
req_cap = 512 + body_len + plen |
|
1608
|
0
|
0
|
|
|
|
|
+ (self->host ? strlen(self->host) : 0) |
|
1609
|
0
|
0
|
|
|
|
|
+ (self->user ? strlen(self->user) : 0) |
|
1610
|
0
|
0
|
|
|
|
|
+ (self->password ? strlen(self->password) : 0); |
|
1611
|
0
|
|
|
|
|
|
Newx(req, req_cap, char); |
|
1612
|
|
|
|
|
|
|
|
|
1613
|
0
|
|
|
|
|
|
pos += snprintf(req + pos, req_cap - pos, |
|
1614
|
|
|
|
|
|
|
"POST /%s HTTP/1.1\r\n", params); |
|
1615
|
0
|
|
|
|
|
|
Safefree(params); |
|
1616
|
0
|
|
|
|
|
|
pos += snprintf(req + pos, req_cap - pos, |
|
1617
|
|
|
|
|
|
|
"Host: %s:%u\r\n", self->host, self->port); |
|
1618
|
0
|
0
|
|
|
|
|
if (self->user) { |
|
1619
|
0
|
|
|
|
|
|
pos += snprintf(req + pos, req_cap - pos, |
|
1620
|
|
|
|
|
|
|
"X-ClickHouse-User: %s\r\n", self->user); |
|
1621
|
|
|
|
|
|
|
} |
|
1622
|
0
|
0
|
|
|
|
|
if (self->password && self->password[0]) { |
|
|
|
0
|
|
|
|
|
|
|
1623
|
0
|
|
|
|
|
|
pos += snprintf(req + pos, req_cap - pos, |
|
1624
|
|
|
|
|
|
|
"X-ClickHouse-Key: %s\r\n", self->password); |
|
1625
|
|
|
|
|
|
|
} |
|
1626
|
0
|
|
|
|
|
|
pos += snprintf(req + pos, req_cap - pos, "Connection: keep-alive\r\n"); |
|
1627
|
|
|
|
|
|
|
|
|
1628
|
0
|
0
|
|
|
|
|
if (do_compress) |
|
1629
|
0
|
|
|
|
|
|
pos += snprintf(req + pos, req_cap - pos, "Accept-Encoding: gzip\r\n"); |
|
1630
|
|
|
|
|
|
|
|
|
1631
|
0
|
0
|
|
|
|
|
if (content_encoding) |
|
1632
|
0
|
|
|
|
|
|
pos += snprintf(req + pos, req_cap - pos, "%s", content_encoding); |
|
1633
|
|
|
|
|
|
|
|
|
1634
|
0
|
|
|
|
|
|
pos += snprintf(req + pos, req_cap - pos, |
|
1635
|
|
|
|
|
|
|
"Content-Length: %lu\r\n\r\n", (unsigned long)body_len); |
|
1636
|
|
|
|
|
|
|
|
|
1637
|
0
|
0
|
|
|
|
|
if (body_len > 0) { |
|
1638
|
0
|
0
|
|
|
|
|
if (pos + body_len > req_cap) { |
|
1639
|
0
|
|
|
|
|
|
req_cap = pos + body_len + 1; |
|
1640
|
0
|
|
|
|
|
|
Renew(req, req_cap, char); |
|
1641
|
|
|
|
|
|
|
} |
|
1642
|
0
|
0
|
|
|
|
|
Copy(body ? body : data, req + pos, body_len, char); |
|
1643
|
0
|
|
|
|
|
|
pos += body_len; |
|
1644
|
|
|
|
|
|
|
} |
|
1645
|
|
|
|
|
|
|
|
|
1646
|
0
|
0
|
|
|
|
|
if (body) Safefree(body); |
|
1647
|
|
|
|
|
|
|
|
|
1648
|
0
|
|
|
|
|
|
*req_len = pos; |
|
1649
|
0
|
|
|
|
|
|
return req; |
|
1650
|
|
|
|
|
|
|
} |
|
1651
|
|
|
|
|
|
|
|
|
1652
|
|
|
|
|
|
|
/* Build HTTP GET /ping request */ |
|
1653
|
0
|
|
|
|
|
|
static char* build_http_ping_request(ev_clickhouse_t *self, size_t *req_len) { |
|
1654
|
|
|
|
|
|
|
char *req; |
|
1655
|
0
|
0
|
|
|
|
|
size_t req_cap = 128 + (self->host ? strlen(self->host) : 0); |
|
1656
|
0
|
|
|
|
|
|
size_t pos = 0; |
|
1657
|
|
|
|
|
|
|
|
|
1658
|
0
|
|
|
|
|
|
Newx(req, req_cap, char); |
|
1659
|
0
|
|
|
|
|
|
pos = snprintf(req, req_cap, |
|
1660
|
|
|
|
|
|
|
"GET /ping HTTP/1.1\r\n" |
|
1661
|
|
|
|
|
|
|
"Host: %s:%u\r\n" |
|
1662
|
|
|
|
|
|
|
"Connection: keep-alive\r\n\r\n", |
|
1663
|
|
|
|
|
|
|
self->host, self->port); |
|
1664
|
0
|
0
|
|
|
|
|
if (pos >= req_cap) pos = req_cap - 1; |
|
1665
|
0
|
|
|
|
|
|
*req_len = pos; |
|
1666
|
0
|
|
|
|
|
|
return req; |
|
1667
|
|
|
|
|
|
|
} |
|
1668
|
|
|
|
|
|
|
|
|
1669
|
|
|
|
|
|
|
/* --- HTTP response parsing --- */ |
|
1670
|
|
|
|
|
|
|
|
|
1671
|
|
|
|
|
|
|
/* Find \r\n\r\n in recv_buf. Returns offset past it, or 0 if not found. */ |
|
1672
|
0
|
|
|
|
|
|
static size_t find_header_end(const char *buf, size_t len) { |
|
1673
|
|
|
|
|
|
|
size_t i; |
|
1674
|
0
|
0
|
|
|
|
|
if (len < 4) return 0; |
|
1675
|
0
|
0
|
|
|
|
|
for (i = 0; i <= len - 4; i++) { |
|
1676
|
0
|
0
|
|
|
|
|
if (buf[i] == '\r' && buf[i+1] == '\n' && |
|
|
|
0
|
|
|
|
|
|
|
1677
|
0
|
0
|
|
|
|
|
buf[i+2] == '\r' && buf[i+3] == '\n') { |
|
|
|
0
|
|
|
|
|
|
|
1678
|
0
|
|
|
|
|
|
return i + 4; |
|
1679
|
|
|
|
|
|
|
} |
|
1680
|
|
|
|
|
|
|
} |
|
1681
|
0
|
|
|
|
|
|
return 0; |
|
1682
|
|
|
|
|
|
|
} |
|
1683
|
|
|
|
|
|
|
|
|
1684
|
|
|
|
|
|
|
/* Extract ClickHouse error code from HTTP error body ("Code: NNN. ...") */ |
|
1685
|
0
|
|
|
|
|
|
static int32_t parse_ch_error_code(const char *body, size_t len) { |
|
1686
|
0
|
0
|
|
|
|
|
if (len > 6 && memcmp(body, "Code: ", 6) == 0) |
|
|
|
0
|
|
|
|
|
|
|
1687
|
0
|
|
|
|
|
|
return (int32_t)atoi(body + 6); |
|
1688
|
0
|
|
|
|
|
|
return 0; |
|
1689
|
|
|
|
|
|
|
} |
|
1690
|
|
|
|
|
|
|
|
|
1691
|
|
|
|
|
|
|
/* Parse HTTP status line, extract status code */ |
|
1692
|
0
|
|
|
|
|
|
static int parse_http_status(const char *buf, size_t len) { |
|
1693
|
|
|
|
|
|
|
/* HTTP/1.1 200 OK\r\n */ |
|
1694
|
0
|
|
|
|
|
|
const char *p = buf; |
|
1695
|
0
|
|
|
|
|
|
const char *end = buf + len; |
|
1696
|
|
|
|
|
|
|
int status; |
|
1697
|
|
|
|
|
|
|
|
|
1698
|
|
|
|
|
|
|
/* skip "HTTP/1.x " */ |
|
1699
|
0
|
0
|
|
|
|
|
while (p < end && *p != ' ') p++; |
|
|
|
0
|
|
|
|
|
|
|
1700
|
0
|
0
|
|
|
|
|
if (p >= end) return 0; |
|
1701
|
0
|
|
|
|
|
|
p++; |
|
1702
|
|
|
|
|
|
|
|
|
1703
|
0
|
|
|
|
|
|
status = atoi(p); |
|
1704
|
0
|
0
|
|
|
|
|
if (status < 100 || status > 599) return 500; /* treat malformed as server error */ |
|
|
|
0
|
|
|
|
|
|
|
1705
|
0
|
|
|
|
|
|
return status; |
|
1706
|
|
|
|
|
|
|
} |
|
1707
|
|
|
|
|
|
|
|
|
1708
|
|
|
|
|
|
|
/* Find header value (case-insensitive). Returns pointer into buf or NULL. */ |
|
1709
|
0
|
|
|
|
|
|
static const char* find_header(const char *headers, size_t headers_len, |
|
1710
|
|
|
|
|
|
|
const char *name, size_t *value_len) { |
|
1711
|
0
|
|
|
|
|
|
size_t name_len = strlen(name); |
|
1712
|
0
|
|
|
|
|
|
const char *p = headers; |
|
1713
|
0
|
|
|
|
|
|
const char *end = headers + headers_len; |
|
1714
|
|
|
|
|
|
|
|
|
1715
|
0
|
0
|
|
|
|
|
while (p < end) { |
|
1716
|
0
|
|
|
|
|
|
const char *line_end = p; |
|
1717
|
0
|
0
|
|
|
|
|
while (line_end < end && *line_end != '\r') line_end++; |
|
|
|
0
|
|
|
|
|
|
|
1718
|
|
|
|
|
|
|
|
|
1719
|
0
|
0
|
|
|
|
|
if ((size_t)(line_end - p) > name_len + 1 && p[name_len] == ':') { |
|
|
|
0
|
|
|
|
|
|
|
1720
|
0
|
|
|
|
|
|
int match = 1; |
|
1721
|
|
|
|
|
|
|
size_t i; |
|
1722
|
0
|
0
|
|
|
|
|
for (i = 0; i < name_len; i++) { |
|
1723
|
0
|
0
|
|
|
|
|
if (tolower((unsigned char)p[i]) != tolower((unsigned char)name[i])) { |
|
1724
|
0
|
|
|
|
|
|
match = 0; |
|
1725
|
0
|
|
|
|
|
|
break; |
|
1726
|
|
|
|
|
|
|
} |
|
1727
|
|
|
|
|
|
|
} |
|
1728
|
0
|
0
|
|
|
|
|
if (match) { |
|
1729
|
0
|
|
|
|
|
|
const char *val = p + name_len + 1; |
|
1730
|
0
|
0
|
|
|
|
|
while (val < line_end && *val == ' ') val++; |
|
|
|
0
|
|
|
|
|
|
|
1731
|
0
|
|
|
|
|
|
*value_len = line_end - val; |
|
1732
|
0
|
|
|
|
|
|
return val; |
|
1733
|
|
|
|
|
|
|
} |
|
1734
|
|
|
|
|
|
|
} |
|
1735
|
|
|
|
|
|
|
|
|
1736
|
|
|
|
|
|
|
/* advance past \r\n */ |
|
1737
|
0
|
0
|
|
|
|
|
if (line_end + 2 <= end) p = line_end + 2; |
|
1738
|
0
|
|
|
|
|
|
else break; |
|
1739
|
|
|
|
|
|
|
} |
|
1740
|
0
|
|
|
|
|
|
return NULL; |
|
1741
|
|
|
|
|
|
|
} |
|
1742
|
|
|
|
|
|
|
|
|
1743
|
|
|
|
|
|
|
/* Parse a complete HTTP response from recv_buf. */ |
|
1744
|
0
|
|
|
|
|
|
static void process_http_response(ev_clickhouse_t *self) { |
|
1745
|
|
|
|
|
|
|
size_t hdr_end; |
|
1746
|
|
|
|
|
|
|
int status; |
|
1747
|
|
|
|
|
|
|
const char *val; |
|
1748
|
|
|
|
|
|
|
size_t val_len; |
|
1749
|
0
|
|
|
|
|
|
size_t content_length = 0; |
|
1750
|
0
|
|
|
|
|
|
int chunked = 0; |
|
1751
|
0
|
|
|
|
|
|
int is_gzip = 0; |
|
1752
|
|
|
|
|
|
|
const char *body; |
|
1753
|
|
|
|
|
|
|
size_t body_len; |
|
1754
|
0
|
|
|
|
|
|
char *decoded = NULL; |
|
1755
|
0
|
|
|
|
|
|
size_t decoded_len = 0; |
|
1756
|
0
|
|
|
|
|
|
size_t decoded_cap = 0; |
|
1757
|
|
|
|
|
|
|
|
|
1758
|
0
|
0
|
|
|
|
|
if (self->recv_len == 0 || self->send_count == 0) return; |
|
|
|
0
|
|
|
|
|
|
|
1759
|
|
|
|
|
|
|
|
|
1760
|
|
|
|
|
|
|
/* find headers end */ |
|
1761
|
0
|
|
|
|
|
|
hdr_end = find_header_end(self->recv_buf, self->recv_len); |
|
1762
|
0
|
0
|
|
|
|
|
if (hdr_end == 0) return; /* need more data */ |
|
1763
|
|
|
|
|
|
|
|
|
1764
|
|
|
|
|
|
|
/* parse status */ |
|
1765
|
0
|
|
|
|
|
|
status = parse_http_status(self->recv_buf, hdr_end); |
|
1766
|
|
|
|
|
|
|
|
|
1767
|
|
|
|
|
|
|
/* parse Content-Length */ |
|
1768
|
0
|
|
|
|
|
|
val = find_header(self->recv_buf, hdr_end, "Content-Length", &val_len); |
|
1769
|
0
|
0
|
|
|
|
|
if (val) { |
|
1770
|
0
|
|
|
|
|
|
content_length = (size_t)strtoul(val, NULL, 10); |
|
1771
|
|
|
|
|
|
|
} |
|
1772
|
|
|
|
|
|
|
|
|
1773
|
|
|
|
|
|
|
/* check Transfer-Encoding: chunked */ |
|
1774
|
0
|
|
|
|
|
|
val = find_header(self->recv_buf, hdr_end, "Transfer-Encoding", &val_len); |
|
1775
|
0
|
0
|
|
|
|
|
if (val && val_len >= 7 && strncasecmp(val, "chunked", 7) == 0) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1776
|
0
|
|
|
|
|
|
chunked = 1; |
|
1777
|
|
|
|
|
|
|
} |
|
1778
|
|
|
|
|
|
|
|
|
1779
|
|
|
|
|
|
|
/* check Content-Encoding: gzip */ |
|
1780
|
0
|
|
|
|
|
|
val = find_header(self->recv_buf, hdr_end, "Content-Encoding", &val_len); |
|
1781
|
0
|
0
|
|
|
|
|
if (val && val_len >= 4 && strncasecmp(val, "gzip", 4) == 0) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1782
|
0
|
|
|
|
|
|
is_gzip = 1; |
|
1783
|
|
|
|
|
|
|
} |
|
1784
|
|
|
|
|
|
|
|
|
1785
|
0
|
0
|
|
|
|
|
if (chunked) { |
|
1786
|
|
|
|
|
|
|
/* decode chunked transfer encoding */ |
|
1787
|
0
|
|
|
|
|
|
const char *cp = self->recv_buf + hdr_end; |
|
1788
|
0
|
|
|
|
|
|
const char *cp_end = self->recv_buf + self->recv_len; |
|
1789
|
|
|
|
|
|
|
|
|
1790
|
|
|
|
|
|
|
{ |
|
1791
|
0
|
|
|
|
|
|
int chunked_complete = 0; |
|
1792
|
0
|
0
|
|
|
|
|
while (cp < cp_end) { |
|
1793
|
|
|
|
|
|
|
/* read chunk size */ |
|
1794
|
0
|
|
|
|
|
|
const char *nl = cp; |
|
1795
|
|
|
|
|
|
|
unsigned long chunk_size; |
|
1796
|
0
|
0
|
|
|
|
|
while (nl < cp_end && *nl != '\r') nl++; |
|
|
|
0
|
|
|
|
|
|
|
1797
|
0
|
0
|
|
|
|
|
if (nl + 2 > cp_end) goto need_more; /* need more data */ |
|
1798
|
|
|
|
|
|
|
|
|
1799
|
0
|
|
|
|
|
|
chunk_size = strtoul(cp, NULL, 16); |
|
1800
|
0
|
|
|
|
|
|
cp = nl + 2; /* skip \r\n */ |
|
1801
|
|
|
|
|
|
|
|
|
1802
|
0
|
0
|
|
|
|
|
if (chunk_size == 0) { |
|
1803
|
|
|
|
|
|
|
/* terminal chunk; skip trailing \r\n */ |
|
1804
|
0
|
0
|
|
|
|
|
if (cp + 2 > cp_end) goto need_more; |
|
1805
|
0
|
|
|
|
|
|
cp += 2; |
|
1806
|
0
|
|
|
|
|
|
chunked_complete = 1; |
|
1807
|
0
|
|
|
|
|
|
break; |
|
1808
|
|
|
|
|
|
|
} |
|
1809
|
|
|
|
|
|
|
|
|
1810
|
0
|
0
|
|
|
|
|
if ((size_t)(cp_end - cp) < 2 |
|
1811
|
0
|
0
|
|
|
|
|
|| chunk_size > (size_t)(cp_end - cp) - 2) goto need_more; |
|
1812
|
|
|
|
|
|
|
|
|
1813
|
|
|
|
|
|
|
/* guard against overflow and unbounded growth — |
|
1814
|
|
|
|
|
|
|
* close connection since remaining chunks would |
|
1815
|
|
|
|
|
|
|
* corrupt the stream for subsequent requests */ |
|
1816
|
0
|
0
|
|
|
|
|
if (decoded_len + chunk_size < decoded_len |
|
1817
|
0
|
0
|
|
|
|
|
|| decoded_len + chunk_size > CH_MAX_DECOMPRESS_SIZE) { |
|
1818
|
0
|
0
|
|
|
|
|
if (decoded) Safefree(decoded); |
|
1819
|
0
|
|
|
|
|
|
self->send_count--; |
|
1820
|
0
|
|
|
|
|
|
int destroyed = deliver_error(self, "chunked response too large"); |
|
1821
|
0
|
0
|
|
|
|
|
if (destroyed) return; |
|
1822
|
0
|
0
|
|
|
|
|
if (cancel_pending(self, "connection closed")) return; |
|
1823
|
0
|
|
|
|
|
|
cleanup_connection(self); |
|
1824
|
0
|
|
|
|
|
|
return; |
|
1825
|
|
|
|
|
|
|
} |
|
1826
|
0
|
0
|
|
|
|
|
if (decoded == NULL) { |
|
1827
|
0
|
|
|
|
|
|
decoded_cap = chunk_size + 256; |
|
1828
|
0
|
|
|
|
|
|
Newx(decoded, decoded_cap, char); |
|
1829
|
0
|
0
|
|
|
|
|
} else if (decoded_len + chunk_size > decoded_cap) { |
|
1830
|
0
|
|
|
|
|
|
decoded_cap = (decoded_len + chunk_size) * 2; |
|
1831
|
0
|
|
|
|
|
|
Renew(decoded, decoded_cap, char); |
|
1832
|
|
|
|
|
|
|
} |
|
1833
|
0
|
|
|
|
|
|
Copy(cp, decoded + decoded_len, chunk_size, char); |
|
1834
|
0
|
|
|
|
|
|
decoded_len += chunk_size; |
|
1835
|
0
|
|
|
|
|
|
cp += chunk_size + 2; /* skip chunk data + \r\n */ |
|
1836
|
|
|
|
|
|
|
} |
|
1837
|
|
|
|
|
|
|
|
|
1838
|
0
|
0
|
|
|
|
|
if (!chunked_complete) goto need_more; |
|
1839
|
|
|
|
|
|
|
} |
|
1840
|
|
|
|
|
|
|
|
|
1841
|
0
|
|
|
|
|
|
body = decoded; |
|
1842
|
0
|
|
|
|
|
|
body_len = decoded_len; |
|
1843
|
|
|
|
|
|
|
|
|
1844
|
|
|
|
|
|
|
/* deliver response */ |
|
1845
|
0
|
|
|
|
|
|
self->send_count--; |
|
1846
|
0
|
0
|
|
|
|
|
if (status == 200) { |
|
1847
|
0
|
|
|
|
|
|
char *final_body = (char *)body; |
|
1848
|
0
|
|
|
|
|
|
size_t final_len = body_len; |
|
1849
|
|
|
|
|
|
|
|
|
1850
|
0
|
0
|
|
|
|
|
if (is_gzip && body_len > 0) { |
|
|
|
0
|
|
|
|
|
|
|
1851
|
|
|
|
|
|
|
size_t dec_len; |
|
1852
|
0
|
|
|
|
|
|
char *dec = gzip_decompress(body, body_len, &dec_len); |
|
1853
|
0
|
0
|
|
|
|
|
if (dec) { |
|
1854
|
0
|
|
|
|
|
|
final_body = dec; |
|
1855
|
0
|
|
|
|
|
|
final_len = dec_len; |
|
1856
|
|
|
|
|
|
|
} else { |
|
1857
|
0
|
0
|
|
|
|
|
if (decoded) Safefree(decoded); |
|
1858
|
0
|
|
|
|
|
|
size_t consumed = cp - self->recv_buf; |
|
1859
|
0
|
0
|
|
|
|
|
if (consumed < self->recv_len) |
|
1860
|
0
|
|
|
|
|
|
memmove(self->recv_buf, self->recv_buf + consumed, |
|
1861
|
0
|
|
|
|
|
|
self->recv_len - consumed); |
|
1862
|
0
|
|
|
|
|
|
self->recv_len -= consumed; |
|
1863
|
0
|
|
|
|
|
|
int destroyed = deliver_error(self, "gzip decompression failed"); |
|
1864
|
0
|
0
|
|
|
|
|
if (destroyed) return; |
|
1865
|
0
|
|
|
|
|
|
goto done; |
|
1866
|
|
|
|
|
|
|
} |
|
1867
|
|
|
|
|
|
|
} |
|
1868
|
|
|
|
|
|
|
|
|
1869
|
|
|
|
|
|
|
{ |
|
1870
|
0
|
|
|
|
|
|
int is_raw = peek_cb_raw(self); |
|
1871
|
0
|
|
|
|
|
|
size_t consumed = cp - self->recv_buf; |
|
1872
|
|
|
|
|
|
|
|
|
1873
|
0
|
0
|
|
|
|
|
if (is_raw) { |
|
1874
|
|
|
|
|
|
|
/* raw mode — deliver body as scalar, skip TSV parsing */ |
|
1875
|
0
|
|
|
|
|
|
int destroyed = deliver_raw_body(self, final_body, final_len); |
|
1876
|
0
|
0
|
|
|
|
|
if (final_body != body) Safefree(final_body); |
|
1877
|
0
|
0
|
|
|
|
|
if (decoded) Safefree(decoded); |
|
1878
|
0
|
0
|
|
|
|
|
if (consumed < self->recv_len) |
|
1879
|
0
|
|
|
|
|
|
memmove(self->recv_buf, self->recv_buf + consumed, |
|
1880
|
0
|
|
|
|
|
|
self->recv_len - consumed); |
|
1881
|
0
|
|
|
|
|
|
self->recv_len -= consumed; |
|
1882
|
0
|
0
|
|
|
|
|
if (destroyed) return; |
|
1883
|
|
|
|
|
|
|
} else { |
|
1884
|
0
|
|
|
|
|
|
AV *rows = NULL; |
|
1885
|
0
|
0
|
|
|
|
|
if (final_len > 0) |
|
1886
|
0
|
|
|
|
|
|
rows = parse_tab_separated(final_body, final_len); |
|
1887
|
0
|
0
|
|
|
|
|
if (final_body != body) Safefree(final_body); |
|
1888
|
0
|
0
|
|
|
|
|
if (decoded) Safefree(decoded); |
|
1889
|
0
|
0
|
|
|
|
|
if (consumed < self->recv_len) |
|
1890
|
0
|
|
|
|
|
|
memmove(self->recv_buf, self->recv_buf + consumed, |
|
1891
|
0
|
|
|
|
|
|
self->recv_len - consumed); |
|
1892
|
0
|
|
|
|
|
|
self->recv_len -= consumed; |
|
1893
|
0
|
0
|
|
|
|
|
if (deliver_rows(self, rows)) return; |
|
1894
|
|
|
|
|
|
|
} |
|
1895
|
|
|
|
|
|
|
} |
|
1896
|
|
|
|
|
|
|
} else { |
|
1897
|
|
|
|
|
|
|
/* error */ |
|
1898
|
|
|
|
|
|
|
char *errmsg; |
|
1899
|
0
|
|
|
|
|
|
char *err_body = (char *)body; |
|
1900
|
0
|
|
|
|
|
|
size_t err_len = body_len; |
|
1901
|
|
|
|
|
|
|
|
|
1902
|
0
|
0
|
|
|
|
|
if (is_gzip && body_len > 0) { |
|
|
|
0
|
|
|
|
|
|
|
1903
|
|
|
|
|
|
|
size_t dec_len; |
|
1904
|
0
|
|
|
|
|
|
char *dec = gzip_decompress(body, body_len, &dec_len); |
|
1905
|
0
|
0
|
|
|
|
|
if (dec) { |
|
1906
|
0
|
|
|
|
|
|
err_body = dec; |
|
1907
|
0
|
|
|
|
|
|
err_len = dec_len; |
|
1908
|
|
|
|
|
|
|
} |
|
1909
|
|
|
|
|
|
|
} |
|
1910
|
|
|
|
|
|
|
|
|
1911
|
0
|
0
|
|
|
|
|
while (err_len > 0 && (err_body[err_len-1] == '\n' || err_body[err_len-1] == '\r')) |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1912
|
0
|
|
|
|
|
|
err_len--; |
|
1913
|
0
|
|
|
|
|
|
self->last_error_code = parse_ch_error_code(err_body, err_len); |
|
1914
|
0
|
|
|
|
|
|
Newx(errmsg, err_len + 64, char); |
|
1915
|
0
|
|
|
|
|
|
snprintf(errmsg, err_len + 64, "HTTP %d: %.*s", |
|
1916
|
|
|
|
|
|
|
status, (int)err_len, err_body); |
|
1917
|
0
|
0
|
|
|
|
|
if (err_body != body) Safefree(err_body); |
|
1918
|
0
|
0
|
|
|
|
|
if (decoded) Safefree(decoded); |
|
1919
|
|
|
|
|
|
|
|
|
1920
|
0
|
|
|
|
|
|
size_t consumed = cp - self->recv_buf; |
|
1921
|
0
|
0
|
|
|
|
|
if (consumed < self->recv_len) { |
|
1922
|
0
|
|
|
|
|
|
memmove(self->recv_buf, self->recv_buf + consumed, |
|
1923
|
0
|
|
|
|
|
|
self->recv_len - consumed); |
|
1924
|
|
|
|
|
|
|
} |
|
1925
|
0
|
|
|
|
|
|
self->recv_len -= consumed; |
|
1926
|
|
|
|
|
|
|
|
|
1927
|
0
|
|
|
|
|
|
int destroyed = deliver_error(self, errmsg); |
|
1928
|
0
|
|
|
|
|
|
Safefree(errmsg); |
|
1929
|
0
|
0
|
|
|
|
|
if (destroyed) return; |
|
1930
|
|
|
|
|
|
|
} |
|
1931
|
|
|
|
|
|
|
} else { |
|
1932
|
|
|
|
|
|
|
/* Content-Length based */ |
|
1933
|
0
|
0
|
|
|
|
|
if (self->recv_len < hdr_end + content_length) return; /* need more data */ |
|
1934
|
|
|
|
|
|
|
|
|
1935
|
0
|
|
|
|
|
|
body = self->recv_buf + hdr_end; |
|
1936
|
0
|
|
|
|
|
|
body_len = content_length; |
|
1937
|
|
|
|
|
|
|
|
|
1938
|
0
|
|
|
|
|
|
self->send_count--; |
|
1939
|
0
|
0
|
|
|
|
|
if (status == 200) { |
|
1940
|
0
|
|
|
|
|
|
char *final_body = (char *)body; |
|
1941
|
0
|
|
|
|
|
|
size_t final_len = body_len; |
|
1942
|
|
|
|
|
|
|
|
|
1943
|
0
|
0
|
|
|
|
|
if (is_gzip && body_len > 0) { |
|
|
|
0
|
|
|
|
|
|
|
1944
|
|
|
|
|
|
|
size_t dec_len; |
|
1945
|
0
|
|
|
|
|
|
char *dec = gzip_decompress(body, body_len, &dec_len); |
|
1946
|
0
|
0
|
|
|
|
|
if (dec) { |
|
1947
|
0
|
|
|
|
|
|
final_body = dec; |
|
1948
|
0
|
|
|
|
|
|
final_len = dec_len; |
|
1949
|
|
|
|
|
|
|
} else { |
|
1950
|
0
|
|
|
|
|
|
size_t consumed = hdr_end + content_length; |
|
1951
|
0
|
0
|
|
|
|
|
if (consumed < self->recv_len) |
|
1952
|
0
|
|
|
|
|
|
memmove(self->recv_buf, self->recv_buf + consumed, |
|
1953
|
0
|
|
|
|
|
|
self->recv_len - consumed); |
|
1954
|
0
|
|
|
|
|
|
self->recv_len -= consumed; |
|
1955
|
0
|
|
|
|
|
|
int destroyed = deliver_error(self, "gzip decompression failed"); |
|
1956
|
0
|
0
|
|
|
|
|
if (destroyed) return; |
|
1957
|
0
|
|
|
|
|
|
goto done; |
|
1958
|
|
|
|
|
|
|
} |
|
1959
|
|
|
|
|
|
|
} |
|
1960
|
|
|
|
|
|
|
|
|
1961
|
|
|
|
|
|
|
{ |
|
1962
|
0
|
|
|
|
|
|
int is_raw = peek_cb_raw(self); |
|
1963
|
0
|
|
|
|
|
|
size_t consumed = hdr_end + content_length; |
|
1964
|
|
|
|
|
|
|
|
|
1965
|
0
|
0
|
|
|
|
|
if (is_raw) { |
|
1966
|
0
|
|
|
|
|
|
int destroyed = deliver_raw_body(self, final_body, final_len); |
|
1967
|
0
|
0
|
|
|
|
|
if (final_body != body) Safefree(final_body); |
|
1968
|
0
|
0
|
|
|
|
|
if (consumed < self->recv_len) |
|
1969
|
0
|
|
|
|
|
|
memmove(self->recv_buf, self->recv_buf + consumed, |
|
1970
|
0
|
|
|
|
|
|
self->recv_len - consumed); |
|
1971
|
0
|
|
|
|
|
|
self->recv_len -= consumed; |
|
1972
|
0
|
0
|
|
|
|
|
if (destroyed) return; |
|
1973
|
|
|
|
|
|
|
} else { |
|
1974
|
0
|
|
|
|
|
|
AV *rows = NULL; |
|
1975
|
0
|
0
|
|
|
|
|
if (final_len > 0) |
|
1976
|
0
|
|
|
|
|
|
rows = parse_tab_separated(final_body, final_len); |
|
1977
|
0
|
0
|
|
|
|
|
if (final_body != body) Safefree(final_body); |
|
1978
|
0
|
0
|
|
|
|
|
if (consumed < self->recv_len) |
|
1979
|
0
|
|
|
|
|
|
memmove(self->recv_buf, self->recv_buf + consumed, |
|
1980
|
0
|
|
|
|
|
|
self->recv_len - consumed); |
|
1981
|
0
|
|
|
|
|
|
self->recv_len -= consumed; |
|
1982
|
0
|
0
|
|
|
|
|
if (deliver_rows(self, rows)) return; |
|
1983
|
|
|
|
|
|
|
} |
|
1984
|
|
|
|
|
|
|
} |
|
1985
|
|
|
|
|
|
|
} else { |
|
1986
|
|
|
|
|
|
|
char *errmsg; |
|
1987
|
0
|
|
|
|
|
|
char *err_body = (char *)body; |
|
1988
|
0
|
|
|
|
|
|
size_t err_len = body_len; |
|
1989
|
|
|
|
|
|
|
|
|
1990
|
0
|
0
|
|
|
|
|
if (is_gzip && body_len > 0) { |
|
|
|
0
|
|
|
|
|
|
|
1991
|
|
|
|
|
|
|
size_t dec_len; |
|
1992
|
0
|
|
|
|
|
|
char *dec = gzip_decompress(body, body_len, &dec_len); |
|
1993
|
0
|
0
|
|
|
|
|
if (dec) { |
|
1994
|
0
|
|
|
|
|
|
err_body = dec; |
|
1995
|
0
|
|
|
|
|
|
err_len = dec_len; |
|
1996
|
|
|
|
|
|
|
} |
|
1997
|
|
|
|
|
|
|
} |
|
1998
|
|
|
|
|
|
|
|
|
1999
|
0
|
0
|
|
|
|
|
while (err_len > 0 && (err_body[err_len-1] == '\n' || err_body[err_len-1] == '\r')) |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
2000
|
0
|
|
|
|
|
|
err_len--; |
|
2001
|
0
|
|
|
|
|
|
self->last_error_code = parse_ch_error_code(err_body, err_len); |
|
2002
|
0
|
|
|
|
|
|
Newx(errmsg, err_len + 64, char); |
|
2003
|
0
|
|
|
|
|
|
snprintf(errmsg, err_len + 64, "HTTP %d: %.*s", |
|
2004
|
|
|
|
|
|
|
status, (int)err_len, err_body); |
|
2005
|
|
|
|
|
|
|
|
|
2006
|
0
|
0
|
|
|
|
|
if (err_body != body) Safefree(err_body); |
|
2007
|
|
|
|
|
|
|
|
|
2008
|
0
|
|
|
|
|
|
size_t consumed = hdr_end + content_length; |
|
2009
|
0
|
0
|
|
|
|
|
if (consumed < self->recv_len) { |
|
2010
|
0
|
|
|
|
|
|
memmove(self->recv_buf, self->recv_buf + consumed, |
|
2011
|
0
|
|
|
|
|
|
self->recv_len - consumed); |
|
2012
|
|
|
|
|
|
|
} |
|
2013
|
0
|
|
|
|
|
|
self->recv_len -= consumed; |
|
2014
|
|
|
|
|
|
|
|
|
2015
|
0
|
|
|
|
|
|
int destroyed = deliver_error(self, errmsg); |
|
2016
|
0
|
|
|
|
|
|
Safefree(errmsg); |
|
2017
|
0
|
0
|
|
|
|
|
if (destroyed) return; |
|
2018
|
|
|
|
|
|
|
} |
|
2019
|
|
|
|
|
|
|
} |
|
2020
|
|
|
|
|
|
|
|
|
2021
|
0
|
0
|
|
|
|
|
if (self->magic != EV_CH_MAGIC) return; |
|
2022
|
|
|
|
|
|
|
|
|
2023
|
0
|
|
|
|
|
|
done: |
|
2024
|
|
|
|
|
|
|
/* Stop query timeout timer on response */ |
|
2025
|
0
|
0
|
|
|
|
|
if (self->timing) { |
|
2026
|
0
|
|
|
|
|
|
ev_timer_stop(self->loop, &self->timer); |
|
2027
|
0
|
|
|
|
|
|
self->timing = 0; |
|
2028
|
|
|
|
|
|
|
} |
|
2029
|
0
|
|
|
|
|
|
pipeline_advance(self); |
|
2030
|
0
|
|
|
|
|
|
return; |
|
2031
|
|
|
|
|
|
|
|
|
2032
|
0
|
|
|
|
|
|
need_more: |
|
2033
|
|
|
|
|
|
|
/* incomplete response — keep reading */ |
|
2034
|
0
|
0
|
|
|
|
|
if (decoded) Safefree(decoded); |
|
2035
|
0
|
|
|
|
|
|
return; |
|
2036
|
|
|
|
|
|
|
} |
|
2037
|
|
|
|
|
|
|
|
|
2038
|
|
|
|
|
|
|
/* --- Native protocol packet builders --- */ |
|
2039
|
|
|
|
|
|
|
|
|
2040
|
0
|
|
|
|
|
|
static char* build_native_hello(ev_clickhouse_t *self, size_t *out_len) { |
|
2041
|
|
|
|
|
|
|
native_buf_t b; |
|
2042
|
0
|
|
|
|
|
|
nbuf_init(&b); |
|
2043
|
|
|
|
|
|
|
|
|
2044
|
0
|
|
|
|
|
|
nbuf_varuint(&b, CLIENT_HELLO); |
|
2045
|
0
|
|
|
|
|
|
nbuf_cstring(&b, CH_CLIENT_NAME); |
|
2046
|
0
|
|
|
|
|
|
nbuf_varuint(&b, CH_CLIENT_VERSION_MAJOR); |
|
2047
|
0
|
|
|
|
|
|
nbuf_varuint(&b, CH_CLIENT_VERSION_MINOR); |
|
2048
|
0
|
|
|
|
|
|
nbuf_varuint(&b, CH_CLIENT_REVISION); |
|
2049
|
0
|
0
|
|
|
|
|
nbuf_cstring(&b, self->database ? self->database : "default"); |
|
2050
|
0
|
0
|
|
|
|
|
nbuf_cstring(&b, self->user ? self->user : "default"); |
|
2051
|
0
|
0
|
|
|
|
|
nbuf_cstring(&b, self->password ? self->password : ""); |
|
2052
|
|
|
|
|
|
|
|
|
2053
|
0
|
|
|
|
|
|
*out_len = b.len; |
|
2054
|
0
|
|
|
|
|
|
return b.data; |
|
2055
|
|
|
|
|
|
|
} |
|
2056
|
|
|
|
|
|
|
|
|
2057
|
0
|
|
|
|
|
|
static char* build_native_ping(size_t *out_len) { |
|
2058
|
|
|
|
|
|
|
native_buf_t b; |
|
2059
|
0
|
|
|
|
|
|
nbuf_init(&b); |
|
2060
|
0
|
|
|
|
|
|
nbuf_varuint(&b, CLIENT_PING); |
|
2061
|
0
|
|
|
|
|
|
*out_len = b.len; |
|
2062
|
0
|
|
|
|
|
|
return b.data; |
|
2063
|
|
|
|
|
|
|
} |
|
2064
|
|
|
|
|
|
|
|
|
2065
|
|
|
|
|
|
|
/* Build an empty Data block (signals end of client data after Query) */ |
|
2066
|
0
|
|
|
|
|
|
static void nbuf_empty_data_block(native_buf_t *b, int do_compress) { |
|
2067
|
0
|
|
|
|
|
|
nbuf_varuint(b, CLIENT_DATA); |
|
2068
|
0
|
|
|
|
|
|
nbuf_cstring(b, ""); /* table name — outside compression */ |
|
2069
|
|
|
|
|
|
|
|
|
2070
|
|
|
|
|
|
|
/* block body: block info + num_cols + num_rows */ |
|
2071
|
|
|
|
|
|
|
#ifdef HAVE_LZ4 |
|
2072
|
|
|
|
|
|
|
if (do_compress) { |
|
2073
|
|
|
|
|
|
|
native_buf_t body; |
|
2074
|
|
|
|
|
|
|
char *compressed; |
|
2075
|
|
|
|
|
|
|
size_t comp_len; |
|
2076
|
|
|
|
|
|
|
|
|
2077
|
|
|
|
|
|
|
nbuf_init(&body); |
|
2078
|
|
|
|
|
|
|
nbuf_varuint(&body, 1); /* field_num = 1 */ |
|
2079
|
|
|
|
|
|
|
nbuf_u8(&body, 0); /* is_overflows = false */ |
|
2080
|
|
|
|
|
|
|
nbuf_varuint(&body, 2); /* field_num = 2 */ |
|
2081
|
|
|
|
|
|
|
{ |
|
2082
|
|
|
|
|
|
|
int32_t bucket = -1; |
|
2083
|
|
|
|
|
|
|
nbuf_append(&body, (const char *)&bucket, 4); |
|
2084
|
|
|
|
|
|
|
} |
|
2085
|
|
|
|
|
|
|
nbuf_varuint(&body, 0); /* end of block info */ |
|
2086
|
|
|
|
|
|
|
nbuf_varuint(&body, 0); /* num_columns = 0 */ |
|
2087
|
|
|
|
|
|
|
nbuf_varuint(&body, 0); /* num_rows = 0 */ |
|
2088
|
|
|
|
|
|
|
|
|
2089
|
|
|
|
|
|
|
compressed = ch_lz4_compress(body.data, body.len, &comp_len); |
|
2090
|
|
|
|
|
|
|
Safefree(body.data); |
|
2091
|
|
|
|
|
|
|
if (compressed) { |
|
2092
|
|
|
|
|
|
|
nbuf_append(b, compressed, comp_len); |
|
2093
|
|
|
|
|
|
|
Safefree(compressed); |
|
2094
|
|
|
|
|
|
|
return; |
|
2095
|
|
|
|
|
|
|
} |
|
2096
|
|
|
|
|
|
|
/* LZ4 failed (should never happen) — fall through to uncompressed */ |
|
2097
|
|
|
|
|
|
|
} |
|
2098
|
|
|
|
|
|
|
#else |
|
2099
|
|
|
|
|
|
|
(void)do_compress; |
|
2100
|
|
|
|
|
|
|
#endif |
|
2101
|
|
|
|
|
|
|
|
|
2102
|
|
|
|
|
|
|
/* block info (revision >= DBMS_MIN_REVISION_WITH_BLOCK_INFO) */ |
|
2103
|
0
|
|
|
|
|
|
nbuf_varuint(b, 1); /* field_num = 1 */ |
|
2104
|
0
|
|
|
|
|
|
nbuf_u8(b, 0); /* is_overflows = false */ |
|
2105
|
0
|
|
|
|
|
|
nbuf_varuint(b, 2); /* field_num = 2 */ |
|
2106
|
|
|
|
|
|
|
{ |
|
2107
|
0
|
|
|
|
|
|
int32_t bucket = -1; |
|
2108
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&bucket, 4); /* bucket_num = -1 */ |
|
2109
|
|
|
|
|
|
|
} |
|
2110
|
0
|
|
|
|
|
|
nbuf_varuint(b, 0); /* end of block info */ |
|
2111
|
0
|
|
|
|
|
|
nbuf_varuint(b, 0); /* num_columns = 0 */ |
|
2112
|
0
|
|
|
|
|
|
nbuf_varuint(b, 0); /* num_rows = 0 */ |
|
2113
|
0
|
|
|
|
|
|
} |
|
2114
|
|
|
|
|
|
|
|
|
2115
|
0
|
|
|
|
|
|
static char* build_native_query(ev_clickhouse_t *self, const char *sql, |
|
2116
|
|
|
|
|
|
|
size_t sql_len, HV *defaults, |
|
2117
|
|
|
|
|
|
|
HV *overrides, size_t *out_len) { |
|
2118
|
|
|
|
|
|
|
native_buf_t b; |
|
2119
|
0
|
|
|
|
|
|
const char *query_id = NULL; |
|
2120
|
0
|
|
|
|
|
|
STRLEN query_id_len = 0; |
|
2121
|
0
|
|
|
|
|
|
nbuf_init(&b); |
|
2122
|
|
|
|
|
|
|
|
|
2123
|
|
|
|
|
|
|
/* Pre-scan settings for query_id (needed before settings block) */ |
|
2124
|
|
|
|
|
|
|
{ |
|
2125
|
|
|
|
|
|
|
SV **svp; |
|
2126
|
0
|
0
|
|
|
|
|
if (overrides && (svp = hv_fetch(overrides, "query_id", 8, 0))) |
|
|
|
0
|
|
|
|
|
|
|
2127
|
0
|
|
|
|
|
|
query_id = SvPV(*svp, query_id_len); |
|
2128
|
0
|
0
|
|
|
|
|
else if (defaults && (svp = hv_fetch(defaults, "query_id", 8, 0))) |
|
|
|
0
|
|
|
|
|
|
|
2129
|
0
|
|
|
|
|
|
query_id = SvPV(*svp, query_id_len); |
|
2130
|
|
|
|
|
|
|
} |
|
2131
|
|
|
|
|
|
|
|
|
2132
|
|
|
|
|
|
|
/* Query packet */ |
|
2133
|
0
|
|
|
|
|
|
nbuf_varuint(&b, CLIENT_QUERY); |
|
2134
|
0
|
0
|
|
|
|
|
if (query_id) { |
|
2135
|
0
|
|
|
|
|
|
nbuf_varuint(&b, (uint64_t)query_id_len); |
|
2136
|
0
|
|
|
|
|
|
nbuf_append(&b, query_id, query_id_len); |
|
2137
|
|
|
|
|
|
|
} else { |
|
2138
|
0
|
|
|
|
|
|
nbuf_cstring(&b, ""); /* query_id (empty = auto) */ |
|
2139
|
|
|
|
|
|
|
} |
|
2140
|
|
|
|
|
|
|
|
|
2141
|
|
|
|
|
|
|
/* Client info — field order must match ClientInfo::read() */ |
|
2142
|
0
|
|
|
|
|
|
nbuf_u8(&b, QUERY_INITIAL); |
|
2143
|
0
|
|
|
|
|
|
nbuf_cstring(&b, ""); /* initial_user */ |
|
2144
|
0
|
|
|
|
|
|
nbuf_cstring(&b, ""); /* initial_query_id */ |
|
2145
|
0
|
|
|
|
|
|
nbuf_cstring(&b, "[::ffff:127.0.0.1]:0"); /* initial_address */ |
|
2146
|
|
|
|
|
|
|
|
|
2147
|
|
|
|
|
|
|
/* initial_query_start_time_microseconds (revision >= 54449) */ |
|
2148
|
|
|
|
|
|
|
{ |
|
2149
|
0
|
|
|
|
|
|
uint64_t zero64 = 0; |
|
2150
|
0
|
|
|
|
|
|
nbuf_append(&b, (const char *)&zero64, 8); |
|
2151
|
|
|
|
|
|
|
} |
|
2152
|
|
|
|
|
|
|
|
|
2153
|
|
|
|
|
|
|
/* iface_type: 1=TCP, os_user, client_hostname, client_name */ |
|
2154
|
0
|
|
|
|
|
|
nbuf_u8(&b, 1); |
|
2155
|
0
|
|
|
|
|
|
nbuf_cstring(&b, ""); /* os_user */ |
|
2156
|
0
|
|
|
|
|
|
nbuf_cstring(&b, ""); /* client_hostname */ |
|
2157
|
0
|
|
|
|
|
|
nbuf_cstring(&b, CH_CLIENT_NAME); |
|
2158
|
0
|
|
|
|
|
|
nbuf_varuint(&b, CH_CLIENT_VERSION_MAJOR); |
|
2159
|
0
|
|
|
|
|
|
nbuf_varuint(&b, CH_CLIENT_VERSION_MINOR); |
|
2160
|
0
|
|
|
|
|
|
nbuf_varuint(&b, CH_CLIENT_REVISION); |
|
2161
|
|
|
|
|
|
|
|
|
2162
|
|
|
|
|
|
|
/* quota_key_in_client_info (always present, revision >= ~54060) */ |
|
2163
|
0
|
|
|
|
|
|
nbuf_cstring(&b, ""); |
|
2164
|
|
|
|
|
|
|
|
|
2165
|
|
|
|
|
|
|
/* distributed_depth (revision >= 54448) */ |
|
2166
|
0
|
|
|
|
|
|
nbuf_varuint(&b, 0); |
|
2167
|
|
|
|
|
|
|
|
|
2168
|
|
|
|
|
|
|
/* version_patch (revision >= 54401) */ |
|
2169
|
0
|
|
|
|
|
|
nbuf_varuint(&b, 0); |
|
2170
|
|
|
|
|
|
|
|
|
2171
|
|
|
|
|
|
|
/* OpenTelemetry trace context (revision >= 54442): no trace */ |
|
2172
|
0
|
|
|
|
|
|
nbuf_u8(&b, 0); |
|
2173
|
|
|
|
|
|
|
|
|
2174
|
|
|
|
|
|
|
/* parallel_replicas (revision >= 54453) */ |
|
2175
|
0
|
|
|
|
|
|
nbuf_varuint(&b, 0); /* collaborate_with_initiator */ |
|
2176
|
0
|
|
|
|
|
|
nbuf_varuint(&b, 0); /* count_participating_replicas */ |
|
2177
|
0
|
|
|
|
|
|
nbuf_varuint(&b, 0); /* number_of_current_replica */ |
|
2178
|
|
|
|
|
|
|
|
|
2179
|
|
|
|
|
|
|
/* Settings (serialized as strings: revision >= 54429) |
|
2180
|
|
|
|
|
|
|
* Format: repeated (String name, UInt8 is_important, String value), |
|
2181
|
|
|
|
|
|
|
* terminated by empty name. */ |
|
2182
|
0
|
|
|
|
|
|
write_native_settings(&b, defaults, overrides, NULL, NULL); |
|
2183
|
0
|
|
|
|
|
|
nbuf_cstring(&b, ""); /* empty name = end of settings */ |
|
2184
|
|
|
|
|
|
|
|
|
2185
|
|
|
|
|
|
|
/* interserver_secret: empty string (revision >= 54441) */ |
|
2186
|
0
|
|
|
|
|
|
nbuf_cstring(&b, ""); |
|
2187
|
|
|
|
|
|
|
|
|
2188
|
|
|
|
|
|
|
/* state (stage), compression, query */ |
|
2189
|
0
|
|
|
|
|
|
nbuf_varuint(&b, STAGE_COMPLETE); |
|
2190
|
|
|
|
|
|
|
#ifdef HAVE_LZ4 |
|
2191
|
|
|
|
|
|
|
nbuf_varuint(&b, self->compress ? 1 : 0); |
|
2192
|
|
|
|
|
|
|
#else |
|
2193
|
0
|
|
|
|
|
|
nbuf_varuint(&b, 0); |
|
2194
|
|
|
|
|
|
|
#endif |
|
2195
|
0
|
|
|
|
|
|
nbuf_string(&b, sql, sql_len); |
|
2196
|
|
|
|
|
|
|
|
|
2197
|
|
|
|
|
|
|
/* Parameters block (revision >= 54459): |
|
2198
|
|
|
|
|
|
|
* Format: repeated (String name, VarUInt flags, String value), |
|
2199
|
|
|
|
|
|
|
* terminated by empty name. Values are wrapped in single quotes |
|
2200
|
|
|
|
|
|
|
* (ClickHouse Field::dump format for strings — the server uses |
|
2201
|
|
|
|
|
|
|
* Field::restoreFromDump to parse them). */ |
|
2202
|
0
|
0
|
|
|
|
|
if (overrides) { |
|
2203
|
|
|
|
|
|
|
HE *pe; |
|
2204
|
0
|
|
|
|
|
|
hv_iterinit(overrides); |
|
2205
|
0
|
0
|
|
|
|
|
while ((pe = hv_iternext(overrides))) { |
|
2206
|
|
|
|
|
|
|
I32 klen; |
|
2207
|
|
|
|
|
|
|
STRLEN vlen; |
|
2208
|
0
|
|
|
|
|
|
char *key = hv_iterkey(pe, &klen); |
|
2209
|
0
|
|
|
|
|
|
char *val = SvPV(hv_iterval(overrides, pe), vlen); |
|
2210
|
0
|
0
|
|
|
|
|
if (klen > 6 && memcmp(key, "param_", 6) == 0) { |
|
|
|
0
|
|
|
|
|
|
|
2211
|
|
|
|
|
|
|
/* strip param_ prefix: native protocol uses bare names */ |
|
2212
|
0
|
|
|
|
|
|
nbuf_varuint(&b, (uint64_t)(klen - 6)); |
|
2213
|
0
|
|
|
|
|
|
nbuf_append(&b, key + 6, (size_t)(klen - 6)); |
|
2214
|
0
|
|
|
|
|
|
nbuf_varuint(&b, 2); /* flags: CUSTOM = 0x02 */ |
|
2215
|
|
|
|
|
|
|
/* value: wrap in single quotes for Field::dump format */ |
|
2216
|
0
|
|
|
|
|
|
nbuf_varuint(&b, (uint64_t)(vlen + 2)); |
|
2217
|
0
|
|
|
|
|
|
nbuf_append(&b, "'", 1); |
|
2218
|
0
|
|
|
|
|
|
nbuf_append(&b, val, vlen); |
|
2219
|
0
|
|
|
|
|
|
nbuf_append(&b, "'", 1); |
|
2220
|
|
|
|
|
|
|
} |
|
2221
|
|
|
|
|
|
|
} |
|
2222
|
|
|
|
|
|
|
} |
|
2223
|
0
|
0
|
|
|
|
|
if (defaults) { |
|
2224
|
|
|
|
|
|
|
HE *pe; |
|
2225
|
0
|
|
|
|
|
|
hv_iterinit(defaults); |
|
2226
|
0
|
0
|
|
|
|
|
while ((pe = hv_iternext(defaults))) { |
|
2227
|
|
|
|
|
|
|
I32 klen; |
|
2228
|
0
|
|
|
|
|
|
char *key = hv_iterkey(pe, &klen); |
|
2229
|
0
|
0
|
|
|
|
|
if (klen > 6 && memcmp(key, "param_", 6) == 0) { |
|
|
|
0
|
|
|
|
|
|
|
2230
|
0
|
0
|
|
|
|
|
if (overrides && hv_exists(overrides, key, klen)) |
|
|
|
0
|
|
|
|
|
|
|
2231
|
0
|
|
|
|
|
|
continue; |
|
2232
|
|
|
|
|
|
|
STRLEN vlen; |
|
2233
|
0
|
|
|
|
|
|
char *val = SvPV(hv_iterval(defaults, pe), vlen); |
|
2234
|
|
|
|
|
|
|
/* strip param_ prefix: native protocol uses bare names */ |
|
2235
|
0
|
|
|
|
|
|
nbuf_varuint(&b, (uint64_t)(klen - 6)); |
|
2236
|
0
|
|
|
|
|
|
nbuf_append(&b, key + 6, (size_t)(klen - 6)); |
|
2237
|
0
|
|
|
|
|
|
nbuf_varuint(&b, 2); /* flags: CUSTOM = 0x02 */ |
|
2238
|
0
|
|
|
|
|
|
nbuf_varuint(&b, (uint64_t)(vlen + 2)); |
|
2239
|
0
|
|
|
|
|
|
nbuf_append(&b, "'", 1); |
|
2240
|
0
|
|
|
|
|
|
nbuf_append(&b, val, vlen); |
|
2241
|
0
|
|
|
|
|
|
nbuf_append(&b, "'", 1); |
|
2242
|
|
|
|
|
|
|
} |
|
2243
|
|
|
|
|
|
|
} |
|
2244
|
|
|
|
|
|
|
} |
|
2245
|
0
|
|
|
|
|
|
nbuf_cstring(&b, ""); /* empty name = end of parameters */ |
|
2246
|
|
|
|
|
|
|
|
|
2247
|
|
|
|
|
|
|
/* Append empty Data block */ |
|
2248
|
0
|
|
|
|
|
|
nbuf_empty_data_block(&b, self->compress); |
|
2249
|
|
|
|
|
|
|
|
|
2250
|
0
|
|
|
|
|
|
*out_len = b.len; |
|
2251
|
0
|
|
|
|
|
|
return b.data; |
|
2252
|
|
|
|
|
|
|
} |
|
2253
|
|
|
|
|
|
|
|
|
2254
|
|
|
|
|
|
|
/* --- Native protocol column decoder --- */ |
|
2255
|
|
|
|
|
|
|
|
|
2256
|
|
|
|
|
|
|
/* Column type codes for decoding. */ |
|
2257
|
|
|
|
|
|
|
enum { |
|
2258
|
|
|
|
|
|
|
CT_INT8, CT_INT16, CT_INT32, CT_INT64, |
|
2259
|
|
|
|
|
|
|
CT_UINT8, CT_UINT16, CT_UINT32, CT_UINT64, |
|
2260
|
|
|
|
|
|
|
CT_FLOAT32, CT_FLOAT64, |
|
2261
|
|
|
|
|
|
|
CT_STRING, CT_FIXEDSTRING, |
|
2262
|
|
|
|
|
|
|
CT_ARRAY, CT_NULLABLE, |
|
2263
|
|
|
|
|
|
|
CT_DATE, CT_DATE32, CT_DATETIME, CT_DATETIME64, |
|
2264
|
|
|
|
|
|
|
CT_UUID, CT_ENUM8, CT_ENUM16, |
|
2265
|
|
|
|
|
|
|
CT_DECIMAL32, CT_DECIMAL64, CT_DECIMAL128, |
|
2266
|
|
|
|
|
|
|
CT_LOWCARDINALITY, CT_NOTHING, |
|
2267
|
|
|
|
|
|
|
CT_BOOL, CT_IPV4, CT_IPV6, |
|
2268
|
|
|
|
|
|
|
CT_INT128, CT_UINT128, |
|
2269
|
|
|
|
|
|
|
CT_INT256, CT_UINT256, |
|
2270
|
|
|
|
|
|
|
CT_TUPLE, CT_MAP, |
|
2271
|
|
|
|
|
|
|
CT_UNKNOWN |
|
2272
|
|
|
|
|
|
|
}; |
|
2273
|
|
|
|
|
|
|
|
|
2274
|
|
|
|
|
|
|
typedef struct col_type_s col_type_t; |
|
2275
|
|
|
|
|
|
|
struct col_type_s { |
|
2276
|
|
|
|
|
|
|
int code; |
|
2277
|
|
|
|
|
|
|
int param; /* FixedString(N), DateTime64 precision, Decimal scale */ |
|
2278
|
|
|
|
|
|
|
col_type_t *inner; /* Nullable, Array, LowCardinality */ |
|
2279
|
|
|
|
|
|
|
col_type_t **inners; /* Tuple elements, Map key+value */ |
|
2280
|
|
|
|
|
|
|
int num_inners; |
|
2281
|
|
|
|
|
|
|
char *type_str; /* full type string (for Enum label lookup) */ |
|
2282
|
|
|
|
|
|
|
size_t type_str_len; |
|
2283
|
|
|
|
|
|
|
char *tz; /* timezone for DateTime/DateTime64 (NULL = UTC) */ |
|
2284
|
|
|
|
|
|
|
}; |
|
2285
|
|
|
|
|
|
|
|
|
2286
|
0
|
|
|
|
|
|
static void free_col_type(col_type_t *t) { |
|
2287
|
|
|
|
|
|
|
int i; |
|
2288
|
0
|
0
|
|
|
|
|
if (!t) return; |
|
2289
|
0
|
0
|
|
|
|
|
if (t->inner) free_col_type(t->inner); |
|
2290
|
0
|
0
|
|
|
|
|
if (t->inners) { |
|
2291
|
0
|
0
|
|
|
|
|
for (i = 0; i < t->num_inners; i++) |
|
2292
|
0
|
|
|
|
|
|
free_col_type(t->inners[i]); |
|
2293
|
0
|
|
|
|
|
|
Safefree(t->inners); |
|
2294
|
|
|
|
|
|
|
} |
|
2295
|
0
|
0
|
|
|
|
|
if (t->type_str) Safefree(t->type_str); |
|
2296
|
0
|
0
|
|
|
|
|
if (t->tz) Safefree(t->tz); |
|
2297
|
0
|
|
|
|
|
|
Safefree(t); |
|
2298
|
|
|
|
|
|
|
} |
|
2299
|
|
|
|
|
|
|
|
|
2300
|
|
|
|
|
|
|
/* Forward declaration */ |
|
2301
|
|
|
|
|
|
|
static col_type_t* parse_col_type(const char *type, size_t len); |
|
2302
|
|
|
|
|
|
|
|
|
2303
|
|
|
|
|
|
|
/* |
|
2304
|
|
|
|
|
|
|
* Parse comma-separated type list inside Tuple(...) or Map(...). |
|
2305
|
|
|
|
|
|
|
* Handles nested parentheses correctly. |
|
2306
|
|
|
|
|
|
|
* Sets t->inners and t->num_inners. |
|
2307
|
|
|
|
|
|
|
*/ |
|
2308
|
0
|
|
|
|
|
|
static void parse_type_list(col_type_t *t, const char *inner, size_t inner_len) { |
|
2309
|
0
|
|
|
|
|
|
int depth = 0, count = 0; |
|
2310
|
0
|
|
|
|
|
|
size_t i, start = 0; |
|
2311
|
|
|
|
|
|
|
|
|
2312
|
|
|
|
|
|
|
/* Count elements */ |
|
2313
|
0
|
0
|
|
|
|
|
for (i = 0; i <= inner_len; i++) { |
|
2314
|
0
|
0
|
|
|
|
|
if (i < inner_len && inner[i] == '(') depth++; |
|
|
|
0
|
|
|
|
|
|
|
2315
|
0
|
0
|
|
|
|
|
else if (i < inner_len && inner[i] == ')') depth--; |
|
|
|
0
|
|
|
|
|
|
|
2316
|
0
|
0
|
|
|
|
|
else if (i == inner_len || (inner[i] == ',' && depth == 0)) |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
2317
|
0
|
|
|
|
|
|
count++; |
|
2318
|
|
|
|
|
|
|
} |
|
2319
|
|
|
|
|
|
|
|
|
2320
|
0
|
|
|
|
|
|
Newxz(t->inners, count, col_type_t*); |
|
2321
|
0
|
|
|
|
|
|
t->num_inners = count; |
|
2322
|
|
|
|
|
|
|
|
|
2323
|
|
|
|
|
|
|
/* Parse each element */ |
|
2324
|
0
|
|
|
|
|
|
count = 0; |
|
2325
|
0
|
|
|
|
|
|
depth = 0; |
|
2326
|
0
|
|
|
|
|
|
start = 0; |
|
2327
|
0
|
0
|
|
|
|
|
for (i = 0; i <= inner_len; i++) { |
|
2328
|
0
|
0
|
|
|
|
|
if (i < inner_len && inner[i] == '(') depth++; |
|
|
|
0
|
|
|
|
|
|
|
2329
|
0
|
0
|
|
|
|
|
else if (i < inner_len && inner[i] == ')') depth--; |
|
|
|
0
|
|
|
|
|
|
|
2330
|
0
|
0
|
|
|
|
|
else if (i == inner_len || (inner[i] == ',' && depth == 0)) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
2331
|
0
|
|
|
|
|
|
size_t s = start, e = i; |
|
2332
|
0
|
0
|
|
|
|
|
while (s < e && inner[s] == ' ') s++; |
|
|
|
0
|
|
|
|
|
|
|
2333
|
0
|
0
|
|
|
|
|
while (e > s && inner[e-1] == ' ') e--; |
|
|
|
0
|
|
|
|
|
|
|
2334
|
|
|
|
|
|
|
/* Strip named tuple field prefix: "name Type" -> "Type" */ |
|
2335
|
|
|
|
|
|
|
{ |
|
2336
|
|
|
|
|
|
|
size_t sp; |
|
2337
|
0
|
0
|
|
|
|
|
for (sp = s; sp < e; sp++) { |
|
2338
|
0
|
0
|
|
|
|
|
if (inner[sp] == '(') break; /* type with parens, stop */ |
|
2339
|
0
|
0
|
|
|
|
|
if (inner[sp] == ' ') { s = sp + 1; break; } |
|
2340
|
|
|
|
|
|
|
} |
|
2341
|
|
|
|
|
|
|
} |
|
2342
|
0
|
|
|
|
|
|
t->inners[count++] = parse_col_type(inner + s, e - s); |
|
2343
|
0
|
|
|
|
|
|
start = i + 1; |
|
2344
|
|
|
|
|
|
|
} |
|
2345
|
|
|
|
|
|
|
} |
|
2346
|
0
|
|
|
|
|
|
} |
|
2347
|
|
|
|
|
|
|
|
|
2348
|
0
|
|
|
|
|
|
static col_type_t* parse_col_type(const char *type, size_t len) { |
|
2349
|
|
|
|
|
|
|
col_type_t *t; |
|
2350
|
0
|
|
|
|
|
|
Newxz(t, 1, col_type_t); |
|
2351
|
|
|
|
|
|
|
|
|
2352
|
0
|
0
|
|
|
|
|
if (len == 4 && memcmp(type, "Int8", 4) == 0) t->code = CT_INT8; |
|
|
|
0
|
|
|
|
|
|
|
2353
|
0
|
0
|
|
|
|
|
else if (len == 5 && memcmp(type, "Int16", 5) == 0) t->code = CT_INT16; |
|
|
|
0
|
|
|
|
|
|
|
2354
|
0
|
0
|
|
|
|
|
else if (len == 5 && memcmp(type, "Int32", 5) == 0) t->code = CT_INT32; |
|
|
|
0
|
|
|
|
|
|
|
2355
|
0
|
0
|
|
|
|
|
else if (len == 5 && memcmp(type, "Int64", 5) == 0) t->code = CT_INT64; |
|
|
|
0
|
|
|
|
|
|
|
2356
|
0
|
0
|
|
|
|
|
else if (len == 5 && memcmp(type, "UInt8", 5) == 0) t->code = CT_UINT8; |
|
|
|
0
|
|
|
|
|
|
|
2357
|
0
|
0
|
|
|
|
|
else if (len == 6 && memcmp(type, "UInt16", 6) == 0) t->code = CT_UINT16; |
|
|
|
0
|
|
|
|
|
|
|
2358
|
0
|
0
|
|
|
|
|
else if (len == 6 && memcmp(type, "UInt32", 6) == 0) t->code = CT_UINT32; |
|
|
|
0
|
|
|
|
|
|
|
2359
|
0
|
0
|
|
|
|
|
else if (len == 6 && memcmp(type, "UInt64", 6) == 0) t->code = CT_UINT64; |
|
|
|
0
|
|
|
|
|
|
|
2360
|
0
|
0
|
|
|
|
|
else if (len == 7 && memcmp(type, "Float32", 7) == 0) t->code = CT_FLOAT32; |
|
|
|
0
|
|
|
|
|
|
|
2361
|
0
|
0
|
|
|
|
|
else if (len == 7 && memcmp(type, "Float64", 7) == 0) t->code = CT_FLOAT64; |
|
|
|
0
|
|
|
|
|
|
|
2362
|
0
|
0
|
|
|
|
|
else if (len == 6 && memcmp(type, "String", 6) == 0) t->code = CT_STRING; |
|
|
|
0
|
|
|
|
|
|
|
2363
|
0
|
0
|
|
|
|
|
else if (len > 12 && memcmp(type, "FixedString(", 12) == 0) { |
|
|
|
0
|
|
|
|
|
|
|
2364
|
0
|
|
|
|
|
|
t->code = CT_FIXEDSTRING; |
|
2365
|
0
|
|
|
|
|
|
t->param = atoi(type + 12); |
|
2366
|
|
|
|
|
|
|
} |
|
2367
|
0
|
0
|
|
|
|
|
else if (len > 6 && memcmp(type, "Array(", 6) == 0) { |
|
|
|
0
|
|
|
|
|
|
|
2368
|
0
|
|
|
|
|
|
t->code = CT_ARRAY; |
|
2369
|
0
|
|
|
|
|
|
t->inner = parse_col_type(type + 6, len - 7); |
|
2370
|
|
|
|
|
|
|
} |
|
2371
|
0
|
0
|
|
|
|
|
else if (len > 9 && memcmp(type, "Nullable(", 9) == 0) { |
|
|
|
0
|
|
|
|
|
|
|
2372
|
0
|
|
|
|
|
|
t->code = CT_NULLABLE; |
|
2373
|
0
|
|
|
|
|
|
t->inner = parse_col_type(type + 9, len - 10); |
|
2374
|
|
|
|
|
|
|
} |
|
2375
|
0
|
0
|
|
|
|
|
else if (len > 15 && memcmp(type, "LowCardinality(", 15) == 0) { |
|
|
|
0
|
|
|
|
|
|
|
2376
|
0
|
|
|
|
|
|
t->code = CT_LOWCARDINALITY; |
|
2377
|
0
|
|
|
|
|
|
t->inner = parse_col_type(type + 15, len - 16); |
|
2378
|
|
|
|
|
|
|
} |
|
2379
|
0
|
0
|
|
|
|
|
else if (len == 4 && memcmp(type, "Date", 4) == 0) t->code = CT_DATE; |
|
|
|
0
|
|
|
|
|
|
|
2380
|
0
|
0
|
|
|
|
|
else if (len == 6 && memcmp(type, "Date32", 6) == 0) t->code = CT_DATE32; |
|
|
|
0
|
|
|
|
|
|
|
2381
|
0
|
0
|
|
|
|
|
else if (len == 8 && memcmp(type, "DateTime", 8) == 0) t->code = CT_DATETIME; |
|
|
|
0
|
|
|
|
|
|
|
2382
|
0
|
0
|
|
|
|
|
else if (len > 9 && memcmp(type, "DateTime(", 9) == 0) { |
|
|
|
0
|
|
|
|
|
|
|
2383
|
0
|
|
|
|
|
|
t->code = CT_DATETIME; |
|
2384
|
|
|
|
|
|
|
/* DateTime('timezone') — extract timezone */ |
|
2385
|
0
|
|
|
|
|
|
{ |
|
2386
|
0
|
|
|
|
|
|
const char *q = memchr(type + 9, '\'', len - 9); |
|
2387
|
0
|
0
|
|
|
|
|
if (q) { |
|
2388
|
0
|
|
|
|
|
|
const char *qe = memchr(q + 1, '\'', type + len - q - 1); |
|
2389
|
0
|
0
|
|
|
|
|
if (qe && qe > q + 1) { |
|
|
|
0
|
|
|
|
|
|
|
2390
|
0
|
|
|
|
|
|
size_t tzlen = qe - q - 1; |
|
2391
|
0
|
|
|
|
|
|
Newx(t->tz, tzlen + 1, char); |
|
2392
|
0
|
|
|
|
|
|
Copy(q + 1, t->tz, tzlen, char); |
|
2393
|
0
|
|
|
|
|
|
t->tz[tzlen] = '\0'; |
|
2394
|
|
|
|
|
|
|
} |
|
2395
|
|
|
|
|
|
|
} |
|
2396
|
|
|
|
|
|
|
} |
|
2397
|
|
|
|
|
|
|
} |
|
2398
|
0
|
0
|
|
|
|
|
else if (len > 11 && memcmp(type, "DateTime64(", 11) == 0) { |
|
|
|
0
|
|
|
|
|
|
|
2399
|
0
|
|
|
|
|
|
t->code = CT_DATETIME64; |
|
2400
|
0
|
|
|
|
|
|
t->param = atoi(type + 11); |
|
2401
|
|
|
|
|
|
|
/* DateTime64(N, 'timezone') — extract timezone */ |
|
2402
|
0
|
|
|
|
|
|
{ |
|
2403
|
0
|
|
|
|
|
|
const char *comma = memchr(type + 11, ',', len - 11); |
|
2404
|
0
|
0
|
|
|
|
|
if (comma) { |
|
2405
|
0
|
|
|
|
|
|
const char *q = memchr(comma, '\'', type + len - comma); |
|
2406
|
0
|
0
|
|
|
|
|
if (q) { |
|
2407
|
0
|
|
|
|
|
|
const char *qe = memchr(q + 1, '\'', type + len - q - 1); |
|
2408
|
0
|
0
|
|
|
|
|
if (qe && qe > q + 1) { |
|
|
|
0
|
|
|
|
|
|
|
2409
|
0
|
|
|
|
|
|
size_t tzlen = qe - q - 1; |
|
2410
|
0
|
|
|
|
|
|
Newx(t->tz, tzlen + 1, char); |
|
2411
|
0
|
|
|
|
|
|
Copy(q + 1, t->tz, tzlen, char); |
|
2412
|
0
|
|
|
|
|
|
t->tz[tzlen] = '\0'; |
|
2413
|
|
|
|
|
|
|
} |
|
2414
|
|
|
|
|
|
|
} |
|
2415
|
|
|
|
|
|
|
} |
|
2416
|
|
|
|
|
|
|
} |
|
2417
|
|
|
|
|
|
|
} |
|
2418
|
0
|
0
|
|
|
|
|
else if (len == 4 && memcmp(type, "UUID", 4) == 0) t->code = CT_UUID; |
|
|
|
0
|
|
|
|
|
|
|
2419
|
0
|
0
|
|
|
|
|
else if (len > 6 && memcmp(type, "Enum8(", 6) == 0) { |
|
|
|
0
|
|
|
|
|
|
|
2420
|
0
|
|
|
|
|
|
t->code = CT_ENUM8; |
|
2421
|
0
|
|
|
|
|
|
Newx(t->type_str, len + 1, char); |
|
2422
|
0
|
|
|
|
|
|
Copy(type, t->type_str, len, char); |
|
2423
|
0
|
|
|
|
|
|
t->type_str[len] = '\0'; |
|
2424
|
0
|
|
|
|
|
|
t->type_str_len = len; |
|
2425
|
|
|
|
|
|
|
} |
|
2426
|
0
|
0
|
|
|
|
|
else if (len > 7 && memcmp(type, "Enum16(", 7) == 0) { |
|
|
|
0
|
|
|
|
|
|
|
2427
|
0
|
|
|
|
|
|
t->code = CT_ENUM16; |
|
2428
|
0
|
|
|
|
|
|
Newx(t->type_str, len + 1, char); |
|
2429
|
0
|
|
|
|
|
|
Copy(type, t->type_str, len, char); |
|
2430
|
0
|
|
|
|
|
|
t->type_str[len] = '\0'; |
|
2431
|
0
|
|
|
|
|
|
t->type_str_len = len; |
|
2432
|
|
|
|
|
|
|
} |
|
2433
|
0
|
0
|
|
|
|
|
else if (len > 10 && memcmp(type, "Decimal32(", 10) == 0) { |
|
|
|
0
|
|
|
|
|
|
|
2434
|
0
|
|
|
|
|
|
t->code = CT_DECIMAL32; |
|
2435
|
0
|
|
|
|
|
|
t->param = atoi(type + 10); |
|
2436
|
|
|
|
|
|
|
} |
|
2437
|
0
|
0
|
|
|
|
|
else if (len > 10 && memcmp(type, "Decimal64(", 10) == 0) { |
|
|
|
0
|
|
|
|
|
|
|
2438
|
0
|
|
|
|
|
|
t->code = CT_DECIMAL64; |
|
2439
|
0
|
|
|
|
|
|
t->param = atoi(type + 10); |
|
2440
|
|
|
|
|
|
|
} |
|
2441
|
0
|
0
|
|
|
|
|
else if (len > 11 && memcmp(type, "Decimal128(", 11) == 0) { |
|
|
|
0
|
|
|
|
|
|
|
2442
|
0
|
|
|
|
|
|
t->code = CT_DECIMAL128; |
|
2443
|
0
|
|
|
|
|
|
t->param = atoi(type + 11); |
|
2444
|
|
|
|
|
|
|
} |
|
2445
|
0
|
0
|
|
|
|
|
else if (len > 8 && memcmp(type, "Decimal(", 8) == 0) { |
|
|
|
0
|
|
|
|
|
|
|
2446
|
0
|
|
|
|
|
|
int precision = atoi(type + 8); |
|
2447
|
0
|
|
|
|
|
|
const char *comma = memchr(type + 8, ',', len - 8); |
|
2448
|
0
|
0
|
|
|
|
|
t->param = comma ? atoi(comma + 1) : 0; |
|
2449
|
0
|
0
|
|
|
|
|
if (precision <= 9) t->code = CT_DECIMAL32; |
|
2450
|
0
|
0
|
|
|
|
|
else if (precision <= 18) t->code = CT_DECIMAL64; |
|
2451
|
0
|
|
|
|
|
|
else t->code = CT_DECIMAL128; |
|
2452
|
|
|
|
|
|
|
} |
|
2453
|
0
|
0
|
|
|
|
|
else if (len == 7 && memcmp(type, "Nothing", 7) == 0) t->code = CT_NOTHING; |
|
|
|
0
|
|
|
|
|
|
|
2454
|
0
|
0
|
|
|
|
|
else if (len == 4 && memcmp(type, "Bool", 4) == 0) t->code = CT_BOOL; |
|
|
|
0
|
|
|
|
|
|
|
2455
|
0
|
0
|
|
|
|
|
else if (len == 4 && memcmp(type, "IPv4", 4) == 0) t->code = CT_IPV4; |
|
|
|
0
|
|
|
|
|
|
|
2456
|
0
|
0
|
|
|
|
|
else if (len == 4 && memcmp(type, "IPv6", 4) == 0) t->code = CT_IPV6; |
|
|
|
0
|
|
|
|
|
|
|
2457
|
0
|
0
|
|
|
|
|
else if (len == 6 && memcmp(type, "Int128", 6) == 0) t->code = CT_INT128; |
|
|
|
0
|
|
|
|
|
|
|
2458
|
0
|
0
|
|
|
|
|
else if (len == 7 && memcmp(type, "UInt128", 7) == 0) t->code = CT_UINT128; |
|
|
|
0
|
|
|
|
|
|
|
2459
|
0
|
0
|
|
|
|
|
else if (len == 6 && memcmp(type, "Int256", 6) == 0) t->code = CT_INT256; |
|
|
|
0
|
|
|
|
|
|
|
2460
|
0
|
0
|
|
|
|
|
else if (len == 7 && memcmp(type, "UInt256", 7) == 0) t->code = CT_UINT256; |
|
|
|
0
|
|
|
|
|
|
|
2461
|
0
|
0
|
|
|
|
|
else if (len > 6 && memcmp(type, "Tuple(", 6) == 0) { |
|
|
|
0
|
|
|
|
|
|
|
2462
|
0
|
|
|
|
|
|
t->code = CT_TUPLE; |
|
2463
|
0
|
|
|
|
|
|
parse_type_list(t, type + 6, len - 7); |
|
2464
|
|
|
|
|
|
|
} |
|
2465
|
0
|
0
|
|
|
|
|
else if (len > 4 && memcmp(type, "Map(", 4) == 0) { |
|
|
|
0
|
|
|
|
|
|
|
2466
|
0
|
|
|
|
|
|
t->code = CT_MAP; |
|
2467
|
0
|
|
|
|
|
|
parse_type_list(t, type + 4, len - 5); |
|
2468
|
|
|
|
|
|
|
} |
|
2469
|
0
|
0
|
|
|
|
|
else if (len > 7 && memcmp(type, "Nested(", 7) == 0) { |
|
|
|
0
|
|
|
|
|
|
|
2470
|
|
|
|
|
|
|
/* Nested(name1 Type1, name2 Type2) = Array(Tuple(Type1, Type2)) */ |
|
2471
|
|
|
|
|
|
|
col_type_t *tuple; |
|
2472
|
0
|
|
|
|
|
|
Newxz(tuple, 1, col_type_t); |
|
2473
|
0
|
|
|
|
|
|
tuple->code = CT_TUPLE; |
|
2474
|
0
|
|
|
|
|
|
parse_type_list(tuple, type + 7, len - 8); |
|
2475
|
0
|
|
|
|
|
|
t->code = CT_ARRAY; |
|
2476
|
0
|
|
|
|
|
|
t->inner = tuple; |
|
2477
|
|
|
|
|
|
|
} |
|
2478
|
0
|
0
|
|
|
|
|
else if (len > 25 && memcmp(type, "SimpleAggregateFunction(", 24) == 0) { |
|
|
|
0
|
|
|
|
|
|
|
2479
|
|
|
|
|
|
|
/* SimpleAggregateFunction(func, Type...) — skip func, parse inner type(s) */ |
|
2480
|
0
|
|
|
|
|
|
const char *inner = type + 24; |
|
2481
|
0
|
|
|
|
|
|
size_t inner_len = len - 25; |
|
2482
|
|
|
|
|
|
|
/* Find first comma at depth 0 — that separates func from type */ |
|
2483
|
|
|
|
|
|
|
size_t ci; |
|
2484
|
0
|
|
|
|
|
|
int depth = 0; |
|
2485
|
0
|
0
|
|
|
|
|
for (ci = 0; ci < inner_len; ci++) { |
|
2486
|
0
|
0
|
|
|
|
|
if (inner[ci] == '(') depth++; |
|
2487
|
0
|
0
|
|
|
|
|
else if (inner[ci] == ')') depth--; |
|
2488
|
0
|
0
|
|
|
|
|
else if (inner[ci] == ',' && depth == 0) break; |
|
|
|
0
|
|
|
|
|
|
|
2489
|
|
|
|
|
|
|
} |
|
2490
|
0
|
0
|
|
|
|
|
if (ci < inner_len) { |
|
2491
|
|
|
|
|
|
|
/* Skip comma and whitespace */ |
|
2492
|
0
|
|
|
|
|
|
ci++; |
|
2493
|
0
|
0
|
|
|
|
|
while (ci < inner_len && inner[ci] == ' ') ci++; |
|
|
|
0
|
|
|
|
|
|
|
2494
|
0
|
|
|
|
|
|
Safefree(t); |
|
2495
|
0
|
|
|
|
|
|
t = parse_col_type(inner + ci, inner_len - ci); |
|
2496
|
|
|
|
|
|
|
} else { |
|
2497
|
0
|
|
|
|
|
|
t->code = CT_UNKNOWN; |
|
2498
|
|
|
|
|
|
|
} |
|
2499
|
|
|
|
|
|
|
} |
|
2500
|
|
|
|
|
|
|
else { |
|
2501
|
|
|
|
|
|
|
/* Unknown type — treat as String (read raw bytes) */ |
|
2502
|
0
|
|
|
|
|
|
t->code = CT_UNKNOWN; |
|
2503
|
|
|
|
|
|
|
} |
|
2504
|
|
|
|
|
|
|
|
|
2505
|
0
|
|
|
|
|
|
return t; |
|
2506
|
|
|
|
|
|
|
} |
|
2507
|
|
|
|
|
|
|
|
|
2508
|
|
|
|
|
|
|
/* Size in bytes for fixed-width types. Returns 0 for variable-width. */ |
|
2509
|
0
|
|
|
|
|
|
static size_t col_type_fixed_size(col_type_t *t) { |
|
2510
|
0
|
|
|
|
|
|
switch (t->code) { |
|
2511
|
0
|
|
|
|
|
|
case CT_INT8: case CT_UINT8: case CT_ENUM8: case CT_BOOL: return 1; |
|
2512
|
0
|
|
|
|
|
|
case CT_INT16: case CT_UINT16: case CT_ENUM16: case CT_DATE: return 2; |
|
2513
|
0
|
|
|
|
|
|
case CT_INT32: case CT_UINT32: case CT_FLOAT32: |
|
2514
|
|
|
|
|
|
|
case CT_DECIMAL32: case CT_DATE32: case CT_DATETIME: |
|
2515
|
0
|
|
|
|
|
|
case CT_IPV4: return 4; |
|
2516
|
0
|
|
|
|
|
|
case CT_INT64: case CT_UINT64: case CT_FLOAT64: |
|
2517
|
0
|
|
|
|
|
|
case CT_DECIMAL64: case CT_DATETIME64: return 8; |
|
2518
|
0
|
|
|
|
|
|
case CT_UUID: case CT_DECIMAL128: |
|
2519
|
0
|
|
|
|
|
|
case CT_INT128: case CT_UINT128: case CT_IPV6: return 16; |
|
2520
|
0
|
|
|
|
|
|
case CT_INT256: case CT_UINT256: return 32; |
|
2521
|
0
|
|
|
|
|
|
case CT_FIXEDSTRING: return (size_t)t->param; |
|
2522
|
0
|
|
|
|
|
|
default: return 0; |
|
2523
|
|
|
|
|
|
|
} |
|
2524
|
|
|
|
|
|
|
} |
|
2525
|
|
|
|
|
|
|
|
|
2526
|
|
|
|
|
|
|
/* --- Decode helper functions for opt-in type formatting --- */ |
|
2527
|
|
|
|
|
|
|
|
|
2528
|
|
|
|
|
|
|
/* Convert days since Unix epoch to "YYYY-MM-DD" */ |
|
2529
|
0
|
|
|
|
|
|
static SV* days_to_date_sv(int32_t days) { |
|
2530
|
0
|
|
|
|
|
|
time_t t = (time_t)days * 86400; |
|
2531
|
|
|
|
|
|
|
struct tm tm; |
|
2532
|
|
|
|
|
|
|
char buf[11]; |
|
2533
|
0
|
0
|
|
|
|
|
if (!gmtime_r(&t, &tm)) return newSVpvn("0000-00-00", 10); |
|
2534
|
0
|
|
|
|
|
|
snprintf(buf, sizeof(buf), "%04d-%02d-%02d", |
|
2535
|
0
|
|
|
|
|
|
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); |
|
2536
|
0
|
|
|
|
|
|
return newSVpvn(buf, 10); |
|
2537
|
|
|
|
|
|
|
} |
|
2538
|
|
|
|
|
|
|
|
|
2539
|
|
|
|
|
|
|
/* Convert epoch seconds to "YYYY-MM-DD HH:MM:SS", tz-aware */ |
|
2540
|
0
|
|
|
|
|
|
static SV* epoch_to_datetime_sv(uint32_t epoch) { |
|
2541
|
0
|
|
|
|
|
|
time_t t = (time_t)epoch; |
|
2542
|
|
|
|
|
|
|
struct tm tm; |
|
2543
|
|
|
|
|
|
|
char buf[20]; |
|
2544
|
0
|
0
|
|
|
|
|
if (!gmtime_r(&t, &tm)) return newSVpvn("0000-00-00 00:00:00", 19); |
|
2545
|
0
|
|
|
|
|
|
snprintf(buf, sizeof(buf), "%04d-%02d-%02d %02d:%02d:%02d", |
|
2546
|
0
|
|
|
|
|
|
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, |
|
2547
|
|
|
|
|
|
|
tm.tm_hour, tm.tm_min, tm.tm_sec); |
|
2548
|
0
|
|
|
|
|
|
return newSVpvn(buf, 19); |
|
2549
|
|
|
|
|
|
|
} |
|
2550
|
|
|
|
|
|
|
|
|
2551
|
|
|
|
|
|
|
/* Like epoch_to_datetime_sv but uses localtime_r (TZ must already be set by caller) */ |
|
2552
|
0
|
|
|
|
|
|
static SV* epoch_to_datetime_sv_local(uint32_t epoch) { |
|
2553
|
0
|
|
|
|
|
|
time_t t = (time_t)epoch; |
|
2554
|
|
|
|
|
|
|
struct tm tm; |
|
2555
|
|
|
|
|
|
|
char buf[20]; |
|
2556
|
0
|
0
|
|
|
|
|
if (!localtime_r(&t, &tm)) return newSVpvn("0000-00-00 00:00:00", 19); |
|
2557
|
0
|
|
|
|
|
|
snprintf(buf, sizeof(buf), "%04d-%02d-%02d %02d:%02d:%02d", |
|
2558
|
0
|
|
|
|
|
|
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, |
|
2559
|
|
|
|
|
|
|
tm.tm_hour, tm.tm_min, tm.tm_sec); |
|
2560
|
0
|
|
|
|
|
|
return newSVpvn(buf, 19); |
|
2561
|
|
|
|
|
|
|
} |
|
2562
|
|
|
|
|
|
|
|
|
2563
|
|
|
|
|
|
|
/* Convert DateTime64 to "YYYY-MM-DD HH:MM:SS.fff...", use_local=1 for localtime */ |
|
2564
|
0
|
|
|
|
|
|
static SV* dt64_to_datetime_sv_ex(int64_t val, int precision, int use_local) { |
|
2565
|
0
|
|
|
|
|
|
int64_t scale = 1; |
|
2566
|
|
|
|
|
|
|
int p; |
|
2567
|
|
|
|
|
|
|
int64_t epoch, frac; |
|
2568
|
|
|
|
|
|
|
time_t t; |
|
2569
|
|
|
|
|
|
|
struct tm tm; |
|
2570
|
|
|
|
|
|
|
char buf[32]; |
|
2571
|
|
|
|
|
|
|
int n; |
|
2572
|
|
|
|
|
|
|
|
|
2573
|
0
|
0
|
|
|
|
|
for (p = 0; p < precision; p++) scale *= 10; |
|
2574
|
0
|
|
|
|
|
|
epoch = val / scale; |
|
2575
|
0
|
|
|
|
|
|
frac = val % scale; |
|
2576
|
0
|
0
|
|
|
|
|
if (frac < 0) { epoch--; frac += scale; } |
|
2577
|
|
|
|
|
|
|
|
|
2578
|
0
|
|
|
|
|
|
t = (time_t)epoch; |
|
2579
|
0
|
0
|
|
|
|
|
if (use_local) { |
|
2580
|
0
|
0
|
|
|
|
|
if (!localtime_r(&t, &tm)) return newSVpvn("0000-00-00 00:00:00", 19); |
|
2581
|
|
|
|
|
|
|
} else { |
|
2582
|
0
|
0
|
|
|
|
|
if (!gmtime_r(&t, &tm)) return newSVpvn("0000-00-00 00:00:00", 19); |
|
2583
|
|
|
|
|
|
|
} |
|
2584
|
0
|
|
|
|
|
|
n = snprintf(buf, sizeof(buf), "%04d-%02d-%02d %02d:%02d:%02d", |
|
2585
|
0
|
|
|
|
|
|
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, |
|
2586
|
|
|
|
|
|
|
tm.tm_hour, tm.tm_min, tm.tm_sec); |
|
2587
|
0
|
0
|
|
|
|
|
if (precision > 0 && n < 30) { |
|
|
|
0
|
|
|
|
|
|
|
2588
|
|
|
|
|
|
|
char fracbuf[16]; |
|
2589
|
|
|
|
|
|
|
int fi; |
|
2590
|
0
|
|
|
|
|
|
snprintf(fracbuf, sizeof(fracbuf), "%0*lld", precision, (long long)frac); |
|
2591
|
0
|
|
|
|
|
|
buf[n++] = '.'; |
|
2592
|
0
|
0
|
|
|
|
|
for (fi = 0; fi < precision && n < 31; fi++) |
|
|
|
0
|
|
|
|
|
|
|
2593
|
0
|
|
|
|
|
|
buf[n++] = fracbuf[fi]; |
|
2594
|
|
|
|
|
|
|
} |
|
2595
|
0
|
|
|
|
|
|
return newSVpvn(buf, n); |
|
2596
|
|
|
|
|
|
|
} |
|
2597
|
|
|
|
|
|
|
|
|
2598
|
|
|
|
|
|
|
|
|
2599
|
|
|
|
|
|
|
/* Set TZ environment variable and call tzset(); returns saved old TZ (caller must free) */ |
|
2600
|
0
|
|
|
|
|
|
static char* set_tz(const char *tz) { |
|
2601
|
0
|
|
|
|
|
|
char *old_tz = getenv("TZ"); |
|
2602
|
0
|
|
|
|
|
|
char *saved = NULL; |
|
2603
|
0
|
0
|
|
|
|
|
if (old_tz) { |
|
2604
|
0
|
|
|
|
|
|
size_t l = strlen(old_tz); |
|
2605
|
0
|
|
|
|
|
|
Newx(saved, l + 1, char); |
|
2606
|
0
|
|
|
|
|
|
Copy(old_tz, saved, l + 1, char); |
|
2607
|
|
|
|
|
|
|
} |
|
2608
|
0
|
|
|
|
|
|
setenv("TZ", tz, 1); |
|
2609
|
0
|
|
|
|
|
|
tzset(); |
|
2610
|
0
|
|
|
|
|
|
return saved; |
|
2611
|
|
|
|
|
|
|
} |
|
2612
|
|
|
|
|
|
|
|
|
2613
|
|
|
|
|
|
|
/* Restore TZ from saved value (which may be NULL), then free saved */ |
|
2614
|
0
|
|
|
|
|
|
static void restore_tz(char *saved) { |
|
2615
|
0
|
0
|
|
|
|
|
if (saved) { |
|
2616
|
0
|
|
|
|
|
|
setenv("TZ", saved, 1); |
|
2617
|
0
|
|
|
|
|
|
Safefree(saved); |
|
2618
|
|
|
|
|
|
|
} else { |
|
2619
|
0
|
|
|
|
|
|
unsetenv("TZ"); |
|
2620
|
|
|
|
|
|
|
} |
|
2621
|
0
|
|
|
|
|
|
tzset(); |
|
2622
|
0
|
|
|
|
|
|
} |
|
2623
|
|
|
|
|
|
|
|
|
2624
|
|
|
|
|
|
|
/* Compute 10^n as double */ |
|
2625
|
0
|
|
|
|
|
|
static double pow10_int(int n) { |
|
2626
|
0
|
|
|
|
|
|
double r = 1.0; |
|
2627
|
|
|
|
|
|
|
int i; |
|
2628
|
0
|
0
|
|
|
|
|
for (i = 0; i < n; i++) r *= 10.0; |
|
2629
|
0
|
|
|
|
|
|
return r; |
|
2630
|
|
|
|
|
|
|
} |
|
2631
|
|
|
|
|
|
|
|
|
2632
|
|
|
|
|
|
|
/* Parse enum label for a given code from type string like "Enum8('a'=1,'b'=2)" */ |
|
2633
|
0
|
|
|
|
|
|
static SV* enum_label_for_code(const char *type_str, size_t type_str_len, int code) { |
|
2634
|
|
|
|
|
|
|
/* Find the opening '(' */ |
|
2635
|
0
|
|
|
|
|
|
const char *p = memchr(type_str, '(', type_str_len); |
|
2636
|
|
|
|
|
|
|
const char *end; |
|
2637
|
0
|
0
|
|
|
|
|
if (!p) return newSViv(code); |
|
2638
|
0
|
|
|
|
|
|
p++; |
|
2639
|
0
|
|
|
|
|
|
end = type_str + type_str_len - 1; /* skip closing ')' */ |
|
2640
|
|
|
|
|
|
|
|
|
2641
|
0
|
0
|
|
|
|
|
while (p < end) { |
|
2642
|
|
|
|
|
|
|
/* Skip whitespace */ |
|
2643
|
0
|
0
|
|
|
|
|
while (p < end && *p == ' ') p++; |
|
|
|
0
|
|
|
|
|
|
|
2644
|
0
|
0
|
|
|
|
|
if (p >= end || *p != '\'') break; |
|
|
|
0
|
|
|
|
|
|
|
2645
|
0
|
|
|
|
|
|
p++; /* skip opening quote */ |
|
2646
|
|
|
|
|
|
|
|
|
2647
|
|
|
|
|
|
|
/* Read label (handle escaped quotes) */ |
|
2648
|
|
|
|
|
|
|
{ |
|
2649
|
0
|
|
|
|
|
|
const char *label_start = p; |
|
2650
|
|
|
|
|
|
|
size_t label_len; |
|
2651
|
|
|
|
|
|
|
int val; |
|
2652
|
|
|
|
|
|
|
|
|
2653
|
0
|
0
|
|
|
|
|
while (p < end && !(*p == '\'' && (p + 1 >= end || *(p+1) != '\''))) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
2654
|
0
|
0
|
|
|
|
|
if (*p == '\'' && p + 1 < end && *(p+1) == '\'') { p += 2; continue; } |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
2655
|
0
|
|
|
|
|
|
p++; |
|
2656
|
|
|
|
|
|
|
} |
|
2657
|
0
|
|
|
|
|
|
label_len = p - label_start; |
|
2658
|
0
|
0
|
|
|
|
|
if (p < end) p++; /* skip closing quote */ |
|
2659
|
|
|
|
|
|
|
|
|
2660
|
|
|
|
|
|
|
/* Skip ' = ' */ |
|
2661
|
0
|
0
|
|
|
|
|
while (p < end && (*p == ' ' || *p == '=')) p++; |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
2662
|
|
|
|
|
|
|
|
|
2663
|
|
|
|
|
|
|
/* Read integer value */ |
|
2664
|
0
|
|
|
|
|
|
val = (int)strtol(p, NULL, 10); |
|
2665
|
|
|
|
|
|
|
|
|
2666
|
0
|
0
|
|
|
|
|
if (val == code) return newSVpvn(label_start, label_len); |
|
2667
|
|
|
|
|
|
|
|
|
2668
|
|
|
|
|
|
|
/* Skip to next entry */ |
|
2669
|
0
|
0
|
|
|
|
|
while (p < end && *p != ',') p++; |
|
|
|
0
|
|
|
|
|
|
|
2670
|
0
|
0
|
|
|
|
|
if (p < end) p++; /* skip comma */ |
|
2671
|
|
|
|
|
|
|
} |
|
2672
|
|
|
|
|
|
|
} |
|
2673
|
|
|
|
|
|
|
/* Not found — return numeric code */ |
|
2674
|
0
|
|
|
|
|
|
return newSViv(code); |
|
2675
|
|
|
|
|
|
|
} |
|
2676
|
|
|
|
|
|
|
|
|
2677
|
|
|
|
|
|
|
/* |
|
2678
|
|
|
|
|
|
|
* Decode a column of `nrows` values from the native binary format. |
|
2679
|
|
|
|
|
|
|
* Returns an array of SVs (one per row). Returns NULL on failure. |
|
2680
|
|
|
|
|
|
|
* Sets *decode_err=1 on definitive errors (vs needing more data). |
|
2681
|
|
|
|
|
|
|
* Advances *pos past the consumed bytes. |
|
2682
|
|
|
|
|
|
|
*/ |
|
2683
|
|
|
|
|
|
|
|
|
2684
|
|
|
|
|
|
|
#ifdef __SIZEOF_INT128__ |
|
2685
|
0
|
|
|
|
|
|
static SV* int128_to_sv(const char *p, int is_signed) { |
|
2686
|
|
|
|
|
|
|
unsigned __int128 uv; |
|
2687
|
|
|
|
|
|
|
char dbuf[42]; |
|
2688
|
0
|
|
|
|
|
|
int dlen = 0, neg = 0, k; |
|
2689
|
0
|
0
|
|
|
|
|
if (is_signed) { |
|
2690
|
|
|
|
|
|
|
__int128 sv; |
|
2691
|
0
|
|
|
|
|
|
memcpy(&sv, p, 16); |
|
2692
|
0
|
|
|
|
|
|
neg = sv < 0; |
|
2693
|
0
|
0
|
|
|
|
|
uv = neg ? -(unsigned __int128)sv : (unsigned __int128)sv; |
|
2694
|
|
|
|
|
|
|
} else { |
|
2695
|
0
|
|
|
|
|
|
memcpy(&uv, p, 16); |
|
2696
|
|
|
|
|
|
|
} |
|
2697
|
|
|
|
|
|
|
do { |
|
2698
|
0
|
|
|
|
|
|
dbuf[dlen++] = '0' + (int)(uv % 10); |
|
2699
|
0
|
|
|
|
|
|
uv /= 10; |
|
2700
|
0
|
0
|
|
|
|
|
} while (uv); |
|
2701
|
0
|
0
|
|
|
|
|
if (neg) dbuf[dlen++] = '-'; |
|
2702
|
0
|
0
|
|
|
|
|
for (k = 0; k < dlen/2; k++) { |
|
2703
|
0
|
|
|
|
|
|
char tmp = dbuf[k]; dbuf[k] = dbuf[dlen-1-k]; dbuf[dlen-1-k] = tmp; |
|
2704
|
|
|
|
|
|
|
} |
|
2705
|
0
|
|
|
|
|
|
return newSVpvn(dbuf, dlen); |
|
2706
|
|
|
|
|
|
|
} |
|
2707
|
|
|
|
|
|
|
#endif |
|
2708
|
|
|
|
|
|
|
|
|
2709
|
|
|
|
|
|
|
/* Convert a 256-bit LE unsigned integer (as 4 x uint64_t) to decimal string. |
|
2710
|
|
|
|
|
|
|
* Works on all platforms (no __int128 required). */ |
|
2711
|
0
|
|
|
|
|
|
static SV* uint256_to_sv(const char *p) { |
|
2712
|
|
|
|
|
|
|
/* Copy into 4 x uint64_t LE limbs: v[0] = lowest */ |
|
2713
|
|
|
|
|
|
|
uint64_t v[4]; |
|
2714
|
|
|
|
|
|
|
char dbuf[80]; |
|
2715
|
0
|
|
|
|
|
|
int dlen = 0, k; |
|
2716
|
|
|
|
|
|
|
|
|
2717
|
0
|
|
|
|
|
|
memcpy(v, p, 32); |
|
2718
|
|
|
|
|
|
|
|
|
2719
|
|
|
|
|
|
|
/* Handle zero */ |
|
2720
|
0
|
0
|
|
|
|
|
if (v[0] == 0 && v[1] == 0 && v[2] == 0 && v[3] == 0) |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
2721
|
0
|
|
|
|
|
|
return newSVpvn("0", 1); |
|
2722
|
|
|
|
|
|
|
|
|
2723
|
|
|
|
|
|
|
/* Repeatedly divide by 10, collecting remainders */ |
|
2724
|
0
|
0
|
|
|
|
|
while (v[0] || v[1] || v[2] || v[3]) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
2725
|
0
|
|
|
|
|
|
uint64_t rem = 0; |
|
2726
|
|
|
|
|
|
|
int i; |
|
2727
|
0
|
0
|
|
|
|
|
for (i = 3; i >= 0; i--) { |
|
2728
|
|
|
|
|
|
|
#ifdef __SIZEOF_INT128__ |
|
2729
|
0
|
|
|
|
|
|
unsigned __int128 cur = ((unsigned __int128)rem << 64) | v[i]; |
|
2730
|
0
|
|
|
|
|
|
v[i] = (uint64_t)(cur / 10); |
|
2731
|
0
|
|
|
|
|
|
rem = (uint64_t)(cur % 10); |
|
2732
|
|
|
|
|
|
|
#else |
|
2733
|
|
|
|
|
|
|
/* Without 128-bit: split each 64-bit limb into hi32:lo32 */ |
|
2734
|
|
|
|
|
|
|
uint64_t hi = (rem << 32) | (v[i] >> 32); |
|
2735
|
|
|
|
|
|
|
uint64_t q_hi = hi / 10; |
|
2736
|
|
|
|
|
|
|
uint64_t r_hi = hi % 10; |
|
2737
|
|
|
|
|
|
|
uint64_t lo = (r_hi << 32) | (v[i] & 0xFFFFFFFFULL); |
|
2738
|
|
|
|
|
|
|
uint64_t q_lo = lo / 10; |
|
2739
|
|
|
|
|
|
|
rem = lo % 10; |
|
2740
|
|
|
|
|
|
|
v[i] = (q_hi << 32) | q_lo; |
|
2741
|
|
|
|
|
|
|
#endif |
|
2742
|
|
|
|
|
|
|
} |
|
2743
|
0
|
|
|
|
|
|
dbuf[dlen++] = '0' + (int)rem; |
|
2744
|
|
|
|
|
|
|
} |
|
2745
|
0
|
0
|
|
|
|
|
for (k = 0; k < dlen/2; k++) { |
|
2746
|
0
|
|
|
|
|
|
char tmp = dbuf[k]; dbuf[k] = dbuf[dlen-1-k]; dbuf[dlen-1-k] = tmp; |
|
2747
|
|
|
|
|
|
|
} |
|
2748
|
0
|
|
|
|
|
|
return newSVpvn(dbuf, dlen); |
|
2749
|
|
|
|
|
|
|
} |
|
2750
|
|
|
|
|
|
|
|
|
2751
|
0
|
|
|
|
|
|
static SV* int256_to_sv(const char *p, int is_signed) { |
|
2752
|
0
|
0
|
|
|
|
|
if (is_signed && ((unsigned char)p[31] & 0x80)) { |
|
|
|
0
|
|
|
|
|
|
|
2753
|
|
|
|
|
|
|
/* Negative: two's complement negate, format, prepend '-' */ |
|
2754
|
|
|
|
|
|
|
unsigned char neg[32]; |
|
2755
|
0
|
|
|
|
|
|
int i, carry = 1; |
|
2756
|
|
|
|
|
|
|
SV *sv; |
|
2757
|
|
|
|
|
|
|
STRLEN svlen; |
|
2758
|
|
|
|
|
|
|
char *s; |
|
2759
|
0
|
0
|
|
|
|
|
for (i = 0; i < 32; i++) { |
|
2760
|
0
|
|
|
|
|
|
int b = (unsigned char)(~((unsigned char)p[i])) + carry; |
|
2761
|
0
|
|
|
|
|
|
neg[i] = (unsigned char)(b & 0xFF); |
|
2762
|
0
|
|
|
|
|
|
carry = b >> 8; |
|
2763
|
|
|
|
|
|
|
} |
|
2764
|
0
|
|
|
|
|
|
sv = uint256_to_sv((const char *)neg); |
|
2765
|
|
|
|
|
|
|
/* Prepend '-' */ |
|
2766
|
0
|
|
|
|
|
|
s = SvPV(sv, svlen); |
|
2767
|
|
|
|
|
|
|
{ |
|
2768
|
0
|
|
|
|
|
|
SV *result = newSV(svlen + 1); |
|
2769
|
0
|
|
|
|
|
|
SvPOK_on(result); |
|
2770
|
0
|
|
|
|
|
|
SvCUR_set(result, svlen + 1); |
|
2771
|
0
|
|
|
|
|
|
*SvPVX(result) = '-'; |
|
2772
|
0
|
|
|
|
|
|
Copy(s, SvPVX(result) + 1, svlen, char); |
|
2773
|
0
|
|
|
|
|
|
SvPVX(result)[svlen + 1] = '\0'; |
|
2774
|
0
|
|
|
|
|
|
SvREFCNT_dec(sv); |
|
2775
|
0
|
|
|
|
|
|
return result; |
|
2776
|
|
|
|
|
|
|
} |
|
2777
|
|
|
|
|
|
|
} |
|
2778
|
0
|
|
|
|
|
|
return uint256_to_sv(p); |
|
2779
|
|
|
|
|
|
|
} |
|
2780
|
|
|
|
|
|
|
|
|
2781
|
|
|
|
|
|
|
static SV** decode_column_ex(const char *buf, size_t len, size_t *pos, |
|
2782
|
|
|
|
|
|
|
uint64_t nrows, col_type_t *ct, int *decode_err, |
|
2783
|
|
|
|
|
|
|
uint32_t decode_flags, ev_clickhouse_t *lc_self, |
|
2784
|
|
|
|
|
|
|
int lc_col_idx); |
|
2785
|
|
|
|
|
|
|
|
|
2786
|
0
|
|
|
|
|
|
static SV** decode_column(const char *buf, size_t len, size_t *pos, |
|
2787
|
|
|
|
|
|
|
uint64_t nrows, col_type_t *ct, int *decode_err, |
|
2788
|
|
|
|
|
|
|
uint32_t decode_flags) { |
|
2789
|
0
|
|
|
|
|
|
return decode_column_ex(buf, len, pos, nrows, ct, decode_err, decode_flags, NULL, -1); |
|
2790
|
|
|
|
|
|
|
} |
|
2791
|
|
|
|
|
|
|
|
|
2792
|
0
|
|
|
|
|
|
static SV** decode_column_ex(const char *buf, size_t len, size_t *pos, |
|
2793
|
|
|
|
|
|
|
uint64_t nrows, col_type_t *ct, int *decode_err, |
|
2794
|
|
|
|
|
|
|
uint32_t decode_flags, ev_clickhouse_t *lc_self, |
|
2795
|
|
|
|
|
|
|
int lc_col_idx) { |
|
2796
|
|
|
|
|
|
|
SV **out; |
|
2797
|
|
|
|
|
|
|
uint64_t i; |
|
2798
|
|
|
|
|
|
|
size_t fsz; |
|
2799
|
|
|
|
|
|
|
|
|
2800
|
0
|
0
|
|
|
|
|
Newxz(out, nrows ? nrows : 1, SV*); |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
2801
|
|
|
|
|
|
|
|
|
2802
|
0
|
0
|
|
|
|
|
if (ct->code == CT_NOTHING) { |
|
2803
|
|
|
|
|
|
|
/* Nothing type: 1 placeholder byte ('0') per row */ |
|
2804
|
0
|
0
|
|
|
|
|
if (*pos > len || nrows > len - *pos) goto fail; |
|
|
|
0
|
|
|
|
|
|
|
2805
|
0
|
|
|
|
|
|
*pos += nrows; |
|
2806
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) |
|
2807
|
0
|
|
|
|
|
|
out[i] = newSV(0); |
|
2808
|
0
|
|
|
|
|
|
return out; |
|
2809
|
|
|
|
|
|
|
} |
|
2810
|
|
|
|
|
|
|
|
|
2811
|
0
|
0
|
|
|
|
|
if (ct->code == CT_NULLABLE) { |
|
2812
|
|
|
|
|
|
|
/* null bitmap: nrows bytes of UInt8 */ |
|
2813
|
|
|
|
|
|
|
uint8_t *nulls; |
|
2814
|
|
|
|
|
|
|
SV **inner; |
|
2815
|
0
|
0
|
|
|
|
|
if (*pos > len || nrows > len - *pos) goto fail; |
|
|
|
0
|
|
|
|
|
|
|
2816
|
0
|
|
|
|
|
|
Newx(nulls, nrows, uint8_t); |
|
2817
|
0
|
|
|
|
|
|
Copy(buf + *pos, nulls, nrows, uint8_t); |
|
2818
|
0
|
|
|
|
|
|
*pos += nrows; |
|
2819
|
|
|
|
|
|
|
|
|
2820
|
|
|
|
|
|
|
/* decode inner column */ |
|
2821
|
0
|
|
|
|
|
|
inner = decode_column(buf, len, pos, nrows, ct->inner, decode_err, decode_flags); |
|
2822
|
0
|
0
|
|
|
|
|
if (!inner) { Safefree(nulls); goto fail; } |
|
2823
|
|
|
|
|
|
|
|
|
2824
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
2825
|
0
|
0
|
|
|
|
|
if (nulls[i]) { |
|
2826
|
0
|
|
|
|
|
|
SvREFCNT_dec(inner[i]); |
|
2827
|
0
|
|
|
|
|
|
out[i] = newSV(0); /* undef */ |
|
2828
|
|
|
|
|
|
|
} else { |
|
2829
|
0
|
|
|
|
|
|
out[i] = inner[i]; |
|
2830
|
|
|
|
|
|
|
} |
|
2831
|
|
|
|
|
|
|
} |
|
2832
|
0
|
|
|
|
|
|
Safefree(nulls); |
|
2833
|
0
|
|
|
|
|
|
Safefree(inner); |
|
2834
|
0
|
|
|
|
|
|
return out; |
|
2835
|
|
|
|
|
|
|
} |
|
2836
|
|
|
|
|
|
|
|
|
2837
|
0
|
0
|
|
|
|
|
if (ct->code == CT_LOWCARDINALITY) { |
|
2838
|
|
|
|
|
|
|
/* |
|
2839
|
|
|
|
|
|
|
* LowCardinality wire format (all multi-byte integers are UInt64 LE): |
|
2840
|
|
|
|
|
|
|
* PREFIX: UInt64 key_version (1=SharedDicts, 2=SingleDict) |
|
2841
|
|
|
|
|
|
|
* DATA: UInt64 serialization_type (bits 0-7: index type, |
|
2842
|
|
|
|
|
|
|
* bit 8: NeedGlobalDictionary, bit 9: HasAdditionalKeys, |
|
2843
|
|
|
|
|
|
|
* bit 10: NeedUpdateDictionary) |
|
2844
|
|
|
|
|
|
|
* if NeedUpdateDictionary: UInt64 num_keys + dictionary data |
|
2845
|
|
|
|
|
|
|
* UInt64 num_indices + index data |
|
2846
|
|
|
|
|
|
|
*/ |
|
2847
|
|
|
|
|
|
|
uint64_t version, ser_type, num_keys, num_indices; |
|
2848
|
0
|
|
|
|
|
|
size_t saved = *pos; |
|
2849
|
|
|
|
|
|
|
int key_type; |
|
2850
|
|
|
|
|
|
|
size_t idx_size; |
|
2851
|
0
|
|
|
|
|
|
SV **dict = NULL; |
|
2852
|
0
|
|
|
|
|
|
int dict_borrowed = 0; /* 1 if dict points to lc_self storage */ |
|
2853
|
|
|
|
|
|
|
|
|
2854
|
|
|
|
|
|
|
/* key_version: UInt64 (from serializeBinaryBulkStatePrefix) */ |
|
2855
|
0
|
0
|
|
|
|
|
if (*pos + 8 > len) goto fail; |
|
2856
|
0
|
|
|
|
|
|
memcpy(&version, buf + *pos, 8); *pos += 8; |
|
2857
|
|
|
|
|
|
|
|
|
2858
|
|
|
|
|
|
|
/* serialization_type: UInt64 */ |
|
2859
|
0
|
0
|
|
|
|
|
if (*pos + 8 > len) { *pos = saved; goto fail; } |
|
2860
|
0
|
|
|
|
|
|
memcpy(&ser_type, buf + *pos, 8); *pos += 8; |
|
2861
|
|
|
|
|
|
|
|
|
2862
|
0
|
|
|
|
|
|
key_type = (int)(ser_type & 0xFF); |
|
2863
|
|
|
|
|
|
|
/* key_type: 0=UInt8, 1=UInt16, 2=UInt32, 3=UInt64 */ |
|
2864
|
|
|
|
|
|
|
|
|
2865
|
|
|
|
|
|
|
/* Read dictionary if NeedUpdateDictionary (bit 10) */ |
|
2866
|
0
|
0
|
|
|
|
|
if (ser_type & (1ULL << 10)) { |
|
2867
|
0
|
0
|
|
|
|
|
if (*pos + 8 > len) { *pos = saved; goto fail; } |
|
2868
|
0
|
|
|
|
|
|
memcpy(&num_keys, buf + *pos, 8); *pos += 8; |
|
2869
|
|
|
|
|
|
|
|
|
2870
|
0
|
|
|
|
|
|
dict = decode_column(buf, len, pos, num_keys, ct->inner, decode_err, decode_flags); |
|
2871
|
0
|
0
|
|
|
|
|
if (!dict) { *pos = saved; goto fail; } |
|
2872
|
|
|
|
|
|
|
} else { |
|
2873
|
|
|
|
|
|
|
/* NeedUpdateDictionary=0: reuse dictionary from prior block */ |
|
2874
|
0
|
0
|
|
|
|
|
if (lc_self && lc_col_idx >= 0 && lc_col_idx < lc_self->lc_num_cols |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
2875
|
0
|
0
|
|
|
|
|
&& lc_self->lc_dicts[lc_col_idx]) { |
|
2876
|
0
|
|
|
|
|
|
dict = lc_self->lc_dicts[lc_col_idx]; |
|
2877
|
0
|
|
|
|
|
|
num_keys = lc_self->lc_dict_sizes[lc_col_idx]; |
|
2878
|
0
|
|
|
|
|
|
dict_borrowed = 1; |
|
2879
|
|
|
|
|
|
|
} else { |
|
2880
|
0
|
0
|
|
|
|
|
if (decode_err) *decode_err = 1; |
|
2881
|
0
|
|
|
|
|
|
*pos = saved; |
|
2882
|
0
|
|
|
|
|
|
goto fail; |
|
2883
|
|
|
|
|
|
|
} |
|
2884
|
|
|
|
|
|
|
} |
|
2885
|
|
|
|
|
|
|
|
|
2886
|
|
|
|
|
|
|
/* Read indices: UInt64 num_indices + index data */ |
|
2887
|
0
|
0
|
|
|
|
|
if (*pos + 8 > len) { |
|
2888
|
0
|
0
|
|
|
|
|
if (dict && !dict_borrowed) { for (i = 0; i < num_keys; i++) SvREFCNT_dec(dict[i]); Safefree(dict); } |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
2889
|
0
|
|
|
|
|
|
*pos = saved; goto fail; |
|
2890
|
|
|
|
|
|
|
} |
|
2891
|
0
|
|
|
|
|
|
memcpy(&num_indices, buf + *pos, 8); *pos += 8; |
|
2892
|
|
|
|
|
|
|
|
|
2893
|
0
|
0
|
|
|
|
|
idx_size = (key_type == 0) ? 1 : (key_type == 1) ? 2 : |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
2894
|
|
|
|
|
|
|
(key_type == 2) ? 4 : 8; |
|
2895
|
0
|
0
|
|
|
|
|
if (num_indices != nrows) { |
|
2896
|
0
|
0
|
|
|
|
|
if (dict && !dict_borrowed) { for (i = 0; i < num_keys; i++) SvREFCNT_dec(dict[i]); Safefree(dict); } |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
2897
|
0
|
0
|
|
|
|
|
*pos = saved; if (decode_err) *decode_err = 1; goto fail; |
|
2898
|
|
|
|
|
|
|
} |
|
2899
|
0
|
0
|
|
|
|
|
if (*pos > len || num_indices > (len - *pos) / idx_size) { |
|
|
|
0
|
|
|
|
|
|
|
2900
|
0
|
0
|
|
|
|
|
if (dict && !dict_borrowed) { for (i = 0; i < num_keys; i++) SvREFCNT_dec(dict[i]); Safefree(dict); } |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
2901
|
0
|
|
|
|
|
|
*pos = saved; goto fail; |
|
2902
|
|
|
|
|
|
|
} |
|
2903
|
|
|
|
|
|
|
|
|
2904
|
|
|
|
|
|
|
/* Store new dictionary for cross-block reuse (after validation) */ |
|
2905
|
0
|
0
|
|
|
|
|
if (!dict_borrowed && lc_self && lc_col_idx >= 0 && lc_col_idx < lc_self->lc_num_cols) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
2906
|
0
|
0
|
|
|
|
|
if (lc_self->lc_dicts[lc_col_idx]) { |
|
2907
|
|
|
|
|
|
|
uint64_t di; |
|
2908
|
0
|
0
|
|
|
|
|
for (di = 0; di < lc_self->lc_dict_sizes[lc_col_idx]; di++) |
|
2909
|
0
|
|
|
|
|
|
SvREFCNT_dec(lc_self->lc_dicts[lc_col_idx][di]); |
|
2910
|
0
|
|
|
|
|
|
Safefree(lc_self->lc_dicts[lc_col_idx]); |
|
2911
|
|
|
|
|
|
|
} |
|
2912
|
|
|
|
|
|
|
SV **dcopy; |
|
2913
|
0
|
0
|
|
|
|
|
Newx(dcopy, num_keys > 0 ? num_keys : 1, SV*); |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
2914
|
0
|
0
|
|
|
|
|
for (i = 0; i < num_keys; i++) |
|
2915
|
0
|
|
|
|
|
|
dcopy[i] = SvREFCNT_inc(dict[i]); |
|
2916
|
0
|
|
|
|
|
|
lc_self->lc_dicts[lc_col_idx] = dcopy; |
|
2917
|
0
|
|
|
|
|
|
lc_self->lc_dict_sizes[lc_col_idx] = num_keys; |
|
2918
|
|
|
|
|
|
|
} |
|
2919
|
|
|
|
|
|
|
|
|
2920
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
2921
|
0
|
|
|
|
|
|
uint64_t idx = 0; |
|
2922
|
0
|
|
|
|
|
|
memcpy(&idx, buf + *pos + i * idx_size, idx_size); |
|
2923
|
0
|
0
|
|
|
|
|
if (dict && idx < num_keys) { |
|
|
|
0
|
|
|
|
|
|
|
2924
|
0
|
|
|
|
|
|
out[i] = SvREFCNT_inc(dict[idx]); |
|
2925
|
|
|
|
|
|
|
} else { |
|
2926
|
0
|
|
|
|
|
|
out[i] = newSV(0); /* undef for missing dict entry */ |
|
2927
|
|
|
|
|
|
|
} |
|
2928
|
|
|
|
|
|
|
} |
|
2929
|
0
|
|
|
|
|
|
*pos += num_indices * idx_size; |
|
2930
|
|
|
|
|
|
|
|
|
2931
|
0
|
0
|
|
|
|
|
if (dict && !dict_borrowed) { |
|
|
|
0
|
|
|
|
|
|
|
2932
|
0
|
0
|
|
|
|
|
for (i = 0; i < num_keys; i++) SvREFCNT_dec(dict[i]); |
|
2933
|
0
|
|
|
|
|
|
Safefree(dict); |
|
2934
|
|
|
|
|
|
|
} |
|
2935
|
0
|
|
|
|
|
|
return out; |
|
2936
|
|
|
|
|
|
|
} |
|
2937
|
|
|
|
|
|
|
|
|
2938
|
0
|
0
|
|
|
|
|
if (ct->code == CT_STRING) { |
|
2939
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
2940
|
|
|
|
|
|
|
const char *s; |
|
2941
|
|
|
|
|
|
|
size_t slen; |
|
2942
|
0
|
0
|
|
|
|
|
if (read_native_string_ref(buf, len, pos, &s, &slen) <= 0) { |
|
2943
|
|
|
|
|
|
|
/* clean up already-created SVs */ |
|
2944
|
|
|
|
|
|
|
uint64_t j; |
|
2945
|
0
|
0
|
|
|
|
|
for (j = 0; j < i; j++) SvREFCNT_dec(out[j]); |
|
2946
|
0
|
|
|
|
|
|
goto fail; |
|
2947
|
|
|
|
|
|
|
} |
|
2948
|
0
|
|
|
|
|
|
out[i] = newSVpvn(s, slen); |
|
2949
|
|
|
|
|
|
|
} |
|
2950
|
0
|
|
|
|
|
|
return out; |
|
2951
|
|
|
|
|
|
|
} |
|
2952
|
|
|
|
|
|
|
|
|
2953
|
0
|
0
|
|
|
|
|
if (ct->code == CT_ARRAY) { |
|
2954
|
|
|
|
|
|
|
/* offsets: nrows x UInt64 */ |
|
2955
|
|
|
|
|
|
|
uint64_t *offsets; |
|
2956
|
|
|
|
|
|
|
SV **elems; |
|
2957
|
|
|
|
|
|
|
uint64_t total, prev; |
|
2958
|
|
|
|
|
|
|
|
|
2959
|
0
|
0
|
|
|
|
|
if (*pos > len || nrows > (len - *pos) / 8) goto fail; |
|
|
|
0
|
|
|
|
|
|
|
2960
|
0
|
0
|
|
|
|
|
Newx(offsets, nrows, uint64_t); |
|
2961
|
0
|
0
|
|
|
|
|
Copy(buf + *pos, offsets, nrows, uint64_t); |
|
2962
|
0
|
|
|
|
|
|
*pos += nrows * 8; |
|
2963
|
|
|
|
|
|
|
|
|
2964
|
|
|
|
|
|
|
/* validate offset monotonicity */ |
|
2965
|
0
|
|
|
|
|
|
prev = 0; |
|
2966
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
2967
|
0
|
0
|
|
|
|
|
if (offsets[i] < prev) { Safefree(offsets); goto fail; } |
|
2968
|
0
|
|
|
|
|
|
prev = offsets[i]; |
|
2969
|
|
|
|
|
|
|
} |
|
2970
|
|
|
|
|
|
|
|
|
2971
|
0
|
0
|
|
|
|
|
total = nrows > 0 ? offsets[nrows - 1] : 0; |
|
2972
|
|
|
|
|
|
|
|
|
2973
|
|
|
|
|
|
|
/* decode all inner elements */ |
|
2974
|
0
|
|
|
|
|
|
elems = decode_column(buf, len, pos, total, ct->inner, decode_err, decode_flags); |
|
2975
|
0
|
0
|
|
|
|
|
if (!elems) { Safefree(offsets); goto fail; } |
|
2976
|
|
|
|
|
|
|
|
|
2977
|
|
|
|
|
|
|
/* build AV for each row */ |
|
2978
|
0
|
|
|
|
|
|
prev = 0; |
|
2979
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
2980
|
0
|
|
|
|
|
|
uint64_t count = offsets[i] - prev; |
|
2981
|
0
|
|
|
|
|
|
AV *av = newAV(); |
|
2982
|
|
|
|
|
|
|
uint64_t j; |
|
2983
|
0
|
0
|
|
|
|
|
if (count > 0) av_extend(av, count - 1); |
|
2984
|
0
|
0
|
|
|
|
|
for (j = 0; j < count; j++) { |
|
2985
|
0
|
|
|
|
|
|
av_push(av, elems[prev + j]); |
|
2986
|
|
|
|
|
|
|
} |
|
2987
|
0
|
|
|
|
|
|
out[i] = newRV_noinc((SV*)av); |
|
2988
|
0
|
|
|
|
|
|
prev = offsets[i]; |
|
2989
|
|
|
|
|
|
|
} |
|
2990
|
|
|
|
|
|
|
|
|
2991
|
0
|
|
|
|
|
|
Safefree(offsets); |
|
2992
|
0
|
|
|
|
|
|
Safefree(elems); |
|
2993
|
0
|
|
|
|
|
|
return out; |
|
2994
|
|
|
|
|
|
|
} |
|
2995
|
|
|
|
|
|
|
|
|
2996
|
0
|
0
|
|
|
|
|
if (ct->code == CT_TUPLE) { |
|
2997
|
|
|
|
|
|
|
/* Tuple: each element is a separate column, transpose to row arrays */ |
|
2998
|
|
|
|
|
|
|
SV ***cols; |
|
2999
|
|
|
|
|
|
|
int j; |
|
3000
|
|
|
|
|
|
|
|
|
3001
|
0
|
|
|
|
|
|
Newxz(cols, ct->num_inners, SV**); |
|
3002
|
0
|
0
|
|
|
|
|
for (j = 0; j < ct->num_inners; j++) { |
|
3003
|
0
|
|
|
|
|
|
cols[j] = decode_column(buf, len, pos, nrows, ct->inners[j], decode_err, decode_flags); |
|
3004
|
0
|
0
|
|
|
|
|
if (!cols[j]) { |
|
3005
|
|
|
|
|
|
|
int k; |
|
3006
|
0
|
0
|
|
|
|
|
for (k = 0; k < j; k++) { |
|
3007
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) SvREFCNT_dec(cols[k][i]); |
|
3008
|
0
|
|
|
|
|
|
Safefree(cols[k]); |
|
3009
|
|
|
|
|
|
|
} |
|
3010
|
0
|
|
|
|
|
|
Safefree(cols); |
|
3011
|
0
|
|
|
|
|
|
goto fail; |
|
3012
|
|
|
|
|
|
|
} |
|
3013
|
|
|
|
|
|
|
} |
|
3014
|
|
|
|
|
|
|
|
|
3015
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3016
|
0
|
|
|
|
|
|
AV *av = newAV(); |
|
3017
|
0
|
|
|
|
|
|
av_extend(av, ct->num_inners - 1); |
|
3018
|
0
|
0
|
|
|
|
|
for (j = 0; j < ct->num_inners; j++) |
|
3019
|
0
|
|
|
|
|
|
av_push(av, cols[j][i]); |
|
3020
|
0
|
|
|
|
|
|
out[i] = newRV_noinc((SV*)av); |
|
3021
|
|
|
|
|
|
|
} |
|
3022
|
|
|
|
|
|
|
|
|
3023
|
0
|
0
|
|
|
|
|
for (j = 0; j < ct->num_inners; j++) Safefree(cols[j]); |
|
3024
|
0
|
|
|
|
|
|
Safefree(cols); |
|
3025
|
0
|
|
|
|
|
|
return out; |
|
3026
|
|
|
|
|
|
|
} |
|
3027
|
|
|
|
|
|
|
|
|
3028
|
0
|
0
|
|
|
|
|
if (ct->code == CT_MAP) { |
|
3029
|
0
|
0
|
|
|
|
|
if (ct->num_inners != 2) { if (decode_err) *decode_err = 1; goto fail; } |
|
|
|
0
|
|
|
|
|
|
|
3030
|
|
|
|
|
|
|
/* Map(K,V): wire format same as Array — offsets + keys column + values column */ |
|
3031
|
|
|
|
|
|
|
uint64_t *offsets, total, prev; |
|
3032
|
|
|
|
|
|
|
SV **keys_col, **vals_col; |
|
3033
|
|
|
|
|
|
|
|
|
3034
|
0
|
0
|
|
|
|
|
if (*pos > len || nrows > (len - *pos) / 8) goto fail; |
|
|
|
0
|
|
|
|
|
|
|
3035
|
0
|
0
|
|
|
|
|
Newx(offsets, nrows, uint64_t); |
|
3036
|
0
|
0
|
|
|
|
|
Copy(buf + *pos, offsets, nrows, uint64_t); |
|
3037
|
0
|
|
|
|
|
|
*pos += nrows * 8; |
|
3038
|
|
|
|
|
|
|
|
|
3039
|
|
|
|
|
|
|
/* validate offset monotonicity */ |
|
3040
|
0
|
|
|
|
|
|
prev = 0; |
|
3041
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3042
|
0
|
0
|
|
|
|
|
if (offsets[i] < prev) { Safefree(offsets); goto fail; } |
|
3043
|
0
|
|
|
|
|
|
prev = offsets[i]; |
|
3044
|
|
|
|
|
|
|
} |
|
3045
|
|
|
|
|
|
|
|
|
3046
|
0
|
0
|
|
|
|
|
total = nrows > 0 ? offsets[nrows - 1] : 0; |
|
3047
|
|
|
|
|
|
|
|
|
3048
|
0
|
|
|
|
|
|
keys_col = decode_column(buf, len, pos, total, ct->inners[0], decode_err, decode_flags); |
|
3049
|
0
|
0
|
|
|
|
|
if (!keys_col) { Safefree(offsets); goto fail; } |
|
3050
|
|
|
|
|
|
|
|
|
3051
|
0
|
|
|
|
|
|
vals_col = decode_column(buf, len, pos, total, ct->inners[1], decode_err, decode_flags); |
|
3052
|
0
|
0
|
|
|
|
|
if (!vals_col) { |
|
3053
|
0
|
0
|
|
|
|
|
for (i = 0; i < total; i++) SvREFCNT_dec(keys_col[i]); |
|
3054
|
0
|
|
|
|
|
|
Safefree(keys_col); |
|
3055
|
0
|
|
|
|
|
|
Safefree(offsets); |
|
3056
|
0
|
|
|
|
|
|
goto fail; |
|
3057
|
|
|
|
|
|
|
} |
|
3058
|
|
|
|
|
|
|
|
|
3059
|
0
|
|
|
|
|
|
prev = 0; |
|
3060
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3061
|
0
|
|
|
|
|
|
uint64_t count = offsets[i] - prev; |
|
3062
|
0
|
|
|
|
|
|
HV *hv = newHV(); |
|
3063
|
|
|
|
|
|
|
uint64_t j; |
|
3064
|
0
|
0
|
|
|
|
|
for (j = 0; j < count; j++) { |
|
3065
|
|
|
|
|
|
|
STRLEN klen; |
|
3066
|
0
|
|
|
|
|
|
const char *kstr = SvPV(keys_col[prev + j], klen); |
|
3067
|
|
|
|
|
|
|
{ |
|
3068
|
0
|
|
|
|
|
|
SV *val_sv = SvREFCNT_inc(vals_col[prev + j]); |
|
3069
|
0
|
0
|
|
|
|
|
if (!hv_store(hv, kstr, klen, val_sv, 0)) |
|
3070
|
0
|
|
|
|
|
|
SvREFCNT_dec(val_sv); |
|
3071
|
|
|
|
|
|
|
} |
|
3072
|
|
|
|
|
|
|
} |
|
3073
|
0
|
|
|
|
|
|
out[i] = newRV_noinc((SV*)hv); |
|
3074
|
0
|
|
|
|
|
|
prev = offsets[i]; |
|
3075
|
|
|
|
|
|
|
} |
|
3076
|
|
|
|
|
|
|
|
|
3077
|
0
|
0
|
|
|
|
|
for (i = 0; i < total; i++) { |
|
3078
|
0
|
|
|
|
|
|
SvREFCNT_dec(keys_col[i]); |
|
3079
|
0
|
|
|
|
|
|
SvREFCNT_dec(vals_col[i]); |
|
3080
|
|
|
|
|
|
|
} |
|
3081
|
0
|
|
|
|
|
|
Safefree(keys_col); |
|
3082
|
0
|
|
|
|
|
|
Safefree(vals_col); |
|
3083
|
0
|
|
|
|
|
|
Safefree(offsets); |
|
3084
|
0
|
|
|
|
|
|
return out; |
|
3085
|
|
|
|
|
|
|
} |
|
3086
|
|
|
|
|
|
|
|
|
3087
|
|
|
|
|
|
|
/* Fixed-width types */ |
|
3088
|
0
|
|
|
|
|
|
fsz = col_type_fixed_size(ct); |
|
3089
|
0
|
0
|
|
|
|
|
if (ct->code == CT_FIXEDSTRING && fsz == 0) { |
|
|
|
0
|
|
|
|
|
|
|
3090
|
|
|
|
|
|
|
/* FixedString(0): 0 bytes per row, produce empty strings */ |
|
3091
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) |
|
3092
|
0
|
|
|
|
|
|
out[i] = newSVpvn("", 0); |
|
3093
|
0
|
|
|
|
|
|
return out; |
|
3094
|
|
|
|
|
|
|
} |
|
3095
|
0
|
0
|
|
|
|
|
if (fsz > 0) { |
|
3096
|
0
|
|
|
|
|
|
char *saved_tz = NULL; |
|
3097
|
0
|
|
|
|
|
|
int tz_set = 0; |
|
3098
|
|
|
|
|
|
|
|
|
3099
|
0
|
0
|
|
|
|
|
if (*pos > len || nrows > (len - *pos) / fsz) goto fail; |
|
|
|
0
|
|
|
|
|
|
|
3100
|
|
|
|
|
|
|
|
|
3101
|
|
|
|
|
|
|
/* Set timezone for DateTime/DateTime64 columns with explicit tz */ |
|
3102
|
0
|
0
|
|
|
|
|
if (ct->tz && (decode_flags & DECODE_DT_STR) && |
|
|
|
0
|
|
|
|
|
|
|
3103
|
0
|
0
|
|
|
|
|
(ct->code == CT_DATETIME || ct->code == CT_DATETIME64)) { |
|
|
|
0
|
|
|
|
|
|
|
3104
|
0
|
|
|
|
|
|
saved_tz = set_tz(ct->tz); |
|
3105
|
0
|
|
|
|
|
|
tz_set = 1; |
|
3106
|
|
|
|
|
|
|
} |
|
3107
|
|
|
|
|
|
|
|
|
3108
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3109
|
0
|
|
|
|
|
|
const char *p = buf + *pos + i * fsz; |
|
3110
|
0
|
|
|
|
|
|
switch (ct->code) { |
|
3111
|
0
|
|
|
|
|
|
case CT_INT8: out[i] = newSViv(*(int8_t*)p); break; |
|
3112
|
0
|
|
|
|
|
|
case CT_INT16: { int16_t v; memcpy(&v, p, 2); out[i] = newSViv(v); break; } |
|
3113
|
0
|
|
|
|
|
|
case CT_INT32: { int32_t v; memcpy(&v, p, 4); out[i] = newSViv(v); break; } |
|
3114
|
0
|
|
|
|
|
|
case CT_INT64: { int64_t v; memcpy(&v, p, 8); out[i] = newSViv((IV)v); break; } |
|
3115
|
0
|
|
|
|
|
|
case CT_UINT8: case CT_BOOL: |
|
3116
|
0
|
|
|
|
|
|
out[i] = newSVuv(*(uint8_t*)p); break; |
|
3117
|
0
|
|
|
|
|
|
case CT_UINT16: { uint16_t v; memcpy(&v, p, 2); out[i] = newSVuv(v); break; } |
|
3118
|
0
|
|
|
|
|
|
case CT_UINT32: { uint32_t v; memcpy(&v, p, 4); out[i] = newSVuv(v); break; } |
|
3119
|
0
|
|
|
|
|
|
case CT_UINT64: { uint64_t v; memcpy(&v, p, 8); out[i] = newSVuv((UV)v); break; } |
|
3120
|
0
|
|
|
|
|
|
case CT_FLOAT32: { float v; memcpy(&v, p, 4); out[i] = newSVnv(v); break; } |
|
3121
|
0
|
|
|
|
|
|
case CT_FLOAT64: { double v; memcpy(&v, p, 8); out[i] = newSVnv(v); break; } |
|
3122
|
0
|
|
|
|
|
|
case CT_ENUM8: |
|
3123
|
0
|
0
|
|
|
|
|
if (decode_flags & DECODE_ENUM_STR) |
|
3124
|
0
|
|
|
|
|
|
out[i] = enum_label_for_code(ct->type_str, ct->type_str_len, *(int8_t*)p); |
|
3125
|
|
|
|
|
|
|
else |
|
3126
|
0
|
|
|
|
|
|
out[i] = newSViv(*(int8_t*)p); |
|
3127
|
0
|
|
|
|
|
|
break; |
|
3128
|
0
|
|
|
|
|
|
case CT_ENUM16: { |
|
3129
|
0
|
|
|
|
|
|
int16_t v; memcpy(&v, p, 2); |
|
3130
|
0
|
0
|
|
|
|
|
if (decode_flags & DECODE_ENUM_STR) |
|
3131
|
0
|
|
|
|
|
|
out[i] = enum_label_for_code(ct->type_str, ct->type_str_len, v); |
|
3132
|
|
|
|
|
|
|
else |
|
3133
|
0
|
|
|
|
|
|
out[i] = newSViv(v); |
|
3134
|
0
|
|
|
|
|
|
break; |
|
3135
|
|
|
|
|
|
|
} |
|
3136
|
0
|
|
|
|
|
|
case CT_DATE: { |
|
3137
|
0
|
|
|
|
|
|
uint16_t v; memcpy(&v, p, 2); |
|
3138
|
0
|
0
|
|
|
|
|
if (decode_flags & DECODE_DT_STR) |
|
3139
|
0
|
|
|
|
|
|
out[i] = days_to_date_sv((int32_t)v); |
|
3140
|
|
|
|
|
|
|
else |
|
3141
|
0
|
|
|
|
|
|
out[i] = newSVuv(v); |
|
3142
|
0
|
|
|
|
|
|
break; |
|
3143
|
|
|
|
|
|
|
} |
|
3144
|
0
|
|
|
|
|
|
case CT_DATE32: { |
|
3145
|
0
|
|
|
|
|
|
int32_t v; memcpy(&v, p, 4); |
|
3146
|
0
|
0
|
|
|
|
|
if (decode_flags & DECODE_DT_STR) |
|
3147
|
0
|
|
|
|
|
|
out[i] = days_to_date_sv(v); |
|
3148
|
|
|
|
|
|
|
else |
|
3149
|
0
|
|
|
|
|
|
out[i] = newSViv(v); |
|
3150
|
0
|
|
|
|
|
|
break; |
|
3151
|
|
|
|
|
|
|
} |
|
3152
|
0
|
|
|
|
|
|
case CT_DATETIME: { |
|
3153
|
0
|
|
|
|
|
|
uint32_t v; memcpy(&v, p, 4); |
|
3154
|
0
|
0
|
|
|
|
|
if (decode_flags & DECODE_DT_STR) |
|
3155
|
0
|
|
|
|
|
|
out[i] = tz_set ? epoch_to_datetime_sv_local(v) |
|
3156
|
0
|
0
|
|
|
|
|
: epoch_to_datetime_sv(v); |
|
3157
|
|
|
|
|
|
|
else |
|
3158
|
0
|
|
|
|
|
|
out[i] = newSVuv(v); |
|
3159
|
0
|
|
|
|
|
|
break; |
|
3160
|
|
|
|
|
|
|
} |
|
3161
|
0
|
|
|
|
|
|
case CT_DATETIME64: { |
|
3162
|
0
|
|
|
|
|
|
int64_t v; memcpy(&v, p, 8); |
|
3163
|
0
|
0
|
|
|
|
|
if (decode_flags & DECODE_DT_STR) |
|
3164
|
0
|
|
|
|
|
|
out[i] = dt64_to_datetime_sv_ex(v, ct->param, tz_set); |
|
3165
|
|
|
|
|
|
|
else |
|
3166
|
0
|
|
|
|
|
|
out[i] = newSViv((IV)v); |
|
3167
|
0
|
|
|
|
|
|
break; |
|
3168
|
|
|
|
|
|
|
} |
|
3169
|
0
|
|
|
|
|
|
case CT_DECIMAL32: { |
|
3170
|
0
|
|
|
|
|
|
int32_t v; memcpy(&v, p, 4); |
|
3171
|
0
|
0
|
|
|
|
|
if (decode_flags & DECODE_DEC_SCALE) |
|
3172
|
0
|
|
|
|
|
|
out[i] = newSVnv((double)v / pow10_int(ct->param)); |
|
3173
|
|
|
|
|
|
|
else |
|
3174
|
0
|
|
|
|
|
|
out[i] = newSViv(v); |
|
3175
|
0
|
|
|
|
|
|
break; |
|
3176
|
|
|
|
|
|
|
} |
|
3177
|
0
|
|
|
|
|
|
case CT_DECIMAL64: { |
|
3178
|
0
|
|
|
|
|
|
int64_t v; memcpy(&v, p, 8); |
|
3179
|
0
|
0
|
|
|
|
|
if (decode_flags & DECODE_DEC_SCALE) |
|
3180
|
0
|
|
|
|
|
|
out[i] = newSVnv((double)v / pow10_int(ct->param)); |
|
3181
|
|
|
|
|
|
|
else |
|
3182
|
0
|
|
|
|
|
|
out[i] = newSViv((IV)v); |
|
3183
|
0
|
|
|
|
|
|
break; |
|
3184
|
|
|
|
|
|
|
} |
|
3185
|
0
|
|
|
|
|
|
case CT_DECIMAL128: { |
|
3186
|
|
|
|
|
|
|
#ifdef __SIZEOF_INT128__ |
|
3187
|
0
|
0
|
|
|
|
|
if (decode_flags & DECODE_DEC_SCALE) { |
|
3188
|
|
|
|
|
|
|
__int128 sv128; |
|
3189
|
0
|
|
|
|
|
|
memcpy(&sv128, p, 16); |
|
3190
|
|
|
|
|
|
|
/* Use long double for Decimal128 to preserve more precision */ |
|
3191
|
0
|
|
|
|
|
|
out[i] = newSVnv((NV)((long double)sv128 / (long double)pow10_int(ct->param))); |
|
3192
|
|
|
|
|
|
|
} else { |
|
3193
|
0
|
|
|
|
|
|
out[i] = int128_to_sv(p, 1); |
|
3194
|
|
|
|
|
|
|
} |
|
3195
|
|
|
|
|
|
|
#else |
|
3196
|
|
|
|
|
|
|
out[i] = newSVpvn(p, 16); |
|
3197
|
|
|
|
|
|
|
#endif |
|
3198
|
0
|
|
|
|
|
|
break; |
|
3199
|
|
|
|
|
|
|
} |
|
3200
|
0
|
|
|
|
|
|
case CT_UUID: { |
|
3201
|
|
|
|
|
|
|
/* UUID: two LE UInt64 halves, each reversed for display */ |
|
3202
|
|
|
|
|
|
|
char ustr[37]; |
|
3203
|
0
|
|
|
|
|
|
const unsigned char *u = (const unsigned char *)p; |
|
3204
|
0
|
|
|
|
|
|
snprintf(ustr, sizeof(ustr), |
|
3205
|
|
|
|
|
|
|
"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", |
|
3206
|
0
|
|
|
|
|
|
u[7],u[6],u[5],u[4],u[3],u[2],u[1],u[0], |
|
3207
|
0
|
|
|
|
|
|
u[15],u[14],u[13],u[12],u[11],u[10],u[9],u[8]); |
|
3208
|
0
|
|
|
|
|
|
out[i] = newSVpvn(ustr, 36); |
|
3209
|
0
|
|
|
|
|
|
break; |
|
3210
|
|
|
|
|
|
|
} |
|
3211
|
0
|
|
|
|
|
|
case CT_IPV4: { |
|
3212
|
|
|
|
|
|
|
/* UInt32 LE, MSB is first octet */ |
|
3213
|
|
|
|
|
|
|
uint32_t v; |
|
3214
|
|
|
|
|
|
|
struct in_addr addr; |
|
3215
|
|
|
|
|
|
|
char abuf[INET_ADDRSTRLEN]; |
|
3216
|
0
|
|
|
|
|
|
memcpy(&v, p, 4); |
|
3217
|
0
|
|
|
|
|
|
addr.s_addr = htonl(v); |
|
3218
|
0
|
|
|
|
|
|
inet_ntop(AF_INET, &addr, abuf, sizeof(abuf)); |
|
3219
|
0
|
|
|
|
|
|
out[i] = newSVpv(abuf, 0); |
|
3220
|
0
|
|
|
|
|
|
break; |
|
3221
|
|
|
|
|
|
|
} |
|
3222
|
0
|
|
|
|
|
|
case CT_IPV6: { |
|
3223
|
|
|
|
|
|
|
/* 16 bytes in network byte order */ |
|
3224
|
|
|
|
|
|
|
char abuf[INET6_ADDRSTRLEN]; |
|
3225
|
0
|
|
|
|
|
|
inet_ntop(AF_INET6, p, abuf, sizeof(abuf)); |
|
3226
|
0
|
|
|
|
|
|
out[i] = newSVpv(abuf, 0); |
|
3227
|
0
|
|
|
|
|
|
break; |
|
3228
|
|
|
|
|
|
|
} |
|
3229
|
0
|
|
|
|
|
|
case CT_INT128: { |
|
3230
|
|
|
|
|
|
|
#ifdef __SIZEOF_INT128__ |
|
3231
|
0
|
|
|
|
|
|
out[i] = int128_to_sv(p, 1); |
|
3232
|
|
|
|
|
|
|
#else |
|
3233
|
|
|
|
|
|
|
out[i] = newSVpvn(p, 16); |
|
3234
|
|
|
|
|
|
|
#endif |
|
3235
|
0
|
|
|
|
|
|
break; |
|
3236
|
|
|
|
|
|
|
} |
|
3237
|
0
|
|
|
|
|
|
case CT_UINT128: { |
|
3238
|
|
|
|
|
|
|
#ifdef __SIZEOF_INT128__ |
|
3239
|
0
|
|
|
|
|
|
out[i] = int128_to_sv(p, 0); |
|
3240
|
|
|
|
|
|
|
#else |
|
3241
|
|
|
|
|
|
|
out[i] = newSVpvn(p, 16); |
|
3242
|
|
|
|
|
|
|
#endif |
|
3243
|
0
|
|
|
|
|
|
break; |
|
3244
|
|
|
|
|
|
|
} |
|
3245
|
0
|
|
|
|
|
|
case CT_INT256: |
|
3246
|
0
|
|
|
|
|
|
out[i] = int256_to_sv(p, 1); break; |
|
3247
|
0
|
|
|
|
|
|
case CT_UINT256: |
|
3248
|
0
|
|
|
|
|
|
out[i] = int256_to_sv(p, 0); break; |
|
3249
|
0
|
|
|
|
|
|
case CT_FIXEDSTRING: default: |
|
3250
|
0
|
|
|
|
|
|
out[i] = newSVpvn(p, fsz); break; |
|
3251
|
|
|
|
|
|
|
} |
|
3252
|
|
|
|
|
|
|
} |
|
3253
|
0
|
0
|
|
|
|
|
if (tz_set) restore_tz(saved_tz); |
|
3254
|
0
|
|
|
|
|
|
*pos += nrows * fsz; |
|
3255
|
0
|
|
|
|
|
|
return out; |
|
3256
|
|
|
|
|
|
|
} |
|
3257
|
|
|
|
|
|
|
|
|
3258
|
|
|
|
|
|
|
/* CT_UNKNOWN: try reading as String */ |
|
3259
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3260
|
|
|
|
|
|
|
const char *s; |
|
3261
|
|
|
|
|
|
|
size_t slen; |
|
3262
|
0
|
0
|
|
|
|
|
if (read_native_string_ref(buf, len, pos, &s, &slen) <= 0) { |
|
3263
|
|
|
|
|
|
|
uint64_t j; |
|
3264
|
0
|
0
|
|
|
|
|
for (j = 0; j < i; j++) SvREFCNT_dec(out[j]); |
|
3265
|
0
|
|
|
|
|
|
goto fail; |
|
3266
|
|
|
|
|
|
|
} |
|
3267
|
0
|
|
|
|
|
|
out[i] = newSVpvn(s, slen); |
|
3268
|
|
|
|
|
|
|
} |
|
3269
|
0
|
|
|
|
|
|
return out; |
|
3270
|
|
|
|
|
|
|
|
|
3271
|
0
|
|
|
|
|
|
fail: |
|
3272
|
0
|
|
|
|
|
|
Safefree(out); |
|
3273
|
0
|
|
|
|
|
|
return NULL; |
|
3274
|
|
|
|
|
|
|
} |
|
3275
|
|
|
|
|
|
|
|
|
3276
|
|
|
|
|
|
|
/* --- Native protocol column encoder (for INSERT) --- */ |
|
3277
|
|
|
|
|
|
|
|
|
3278
|
|
|
|
|
|
|
/* TSV unescape: \\ → \, \n → newline, \t → tab, \0 → null byte */ |
|
3279
|
0
|
|
|
|
|
|
static size_t tsv_unescape(const char *src, size_t src_len, char *dst) { |
|
3280
|
0
|
|
|
|
|
|
size_t i, j = 0; |
|
3281
|
0
|
0
|
|
|
|
|
for (i = 0; i < src_len; i++) { |
|
3282
|
0
|
0
|
|
|
|
|
if (src[i] == '\\' && i + 1 < src_len) { |
|
|
|
0
|
|
|
|
|
|
|
3283
|
0
|
|
|
|
|
|
switch (src[i+1]) { |
|
3284
|
0
|
|
|
|
|
|
case '\\': dst[j++] = '\\'; i++; break; |
|
3285
|
0
|
|
|
|
|
|
case 'n': dst[j++] = '\n'; i++; break; |
|
3286
|
0
|
|
|
|
|
|
case 't': dst[j++] = '\t'; i++; break; |
|
3287
|
0
|
|
|
|
|
|
case '0': dst[j++] = '\0'; i++; break; |
|
3288
|
0
|
|
|
|
|
|
case '\'': dst[j++] = '\''; i++; break; |
|
3289
|
0
|
|
|
|
|
|
case 'b': dst[j++] = '\b'; i++; break; |
|
3290
|
0
|
|
|
|
|
|
case 'r': dst[j++] = '\r'; i++; break; |
|
3291
|
0
|
|
|
|
|
|
case 'a': dst[j++] = '\a'; i++; break; |
|
3292
|
0
|
|
|
|
|
|
case 'f': dst[j++] = '\f'; i++; break; |
|
3293
|
0
|
|
|
|
|
|
default: dst[j++] = src[i]; break; |
|
3294
|
|
|
|
|
|
|
} |
|
3295
|
|
|
|
|
|
|
} else { |
|
3296
|
0
|
|
|
|
|
|
dst[j++] = src[i]; |
|
3297
|
|
|
|
|
|
|
} |
|
3298
|
|
|
|
|
|
|
} |
|
3299
|
0
|
|
|
|
|
|
return j; |
|
3300
|
|
|
|
|
|
|
} |
|
3301
|
|
|
|
|
|
|
|
|
3302
|
0
|
|
|
|
|
|
static int is_tsv_null(const char *s, size_t len) { |
|
3303
|
0
|
0
|
|
|
|
|
return len == 2 && s[0] == '\\' && s[1] == 'N'; |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
3304
|
|
|
|
|
|
|
} |
|
3305
|
|
|
|
|
|
|
|
|
3306
|
|
|
|
|
|
|
/* TSV escape: inverse of tsv_unescape — appends escaped bytes to buffer */ |
|
3307
|
0
|
|
|
|
|
|
static void tsv_escape(native_buf_t *b, const char *s, size_t len) { |
|
3308
|
0
|
|
|
|
|
|
size_t i, start = 0; |
|
3309
|
0
|
0
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
3310
|
0
|
|
|
|
|
|
char esc = 0; |
|
3311
|
0
|
|
|
|
|
|
switch (s[i]) { |
|
3312
|
0
|
|
|
|
|
|
case '\\': esc = '\\'; break; |
|
3313
|
0
|
|
|
|
|
|
case '\t': esc = 't'; break; |
|
3314
|
0
|
|
|
|
|
|
case '\n': esc = 'n'; break; |
|
3315
|
0
|
|
|
|
|
|
case '\0': esc = '0'; break; |
|
3316
|
0
|
|
|
|
|
|
case '\b': esc = 'b'; break; |
|
3317
|
0
|
|
|
|
|
|
case '\r': esc = 'r'; break; |
|
3318
|
0
|
|
|
|
|
|
case '\a': esc = 'a'; break; |
|
3319
|
0
|
|
|
|
|
|
case '\f': esc = 'f'; break; |
|
3320
|
|
|
|
|
|
|
} |
|
3321
|
0
|
0
|
|
|
|
|
if (esc) { |
|
3322
|
0
|
0
|
|
|
|
|
if (i > start) |
|
3323
|
0
|
|
|
|
|
|
nbuf_append(b, s + start, i - start); |
|
3324
|
0
|
|
|
|
|
|
nbuf_grow(b, 2); |
|
3325
|
0
|
|
|
|
|
|
b->data[b->len++] = '\\'; |
|
3326
|
0
|
|
|
|
|
|
b->data[b->len++] = esc; |
|
3327
|
0
|
|
|
|
|
|
start = i + 1; |
|
3328
|
|
|
|
|
|
|
} |
|
3329
|
|
|
|
|
|
|
} |
|
3330
|
0
|
0
|
|
|
|
|
if (start < len) |
|
3331
|
0
|
|
|
|
|
|
nbuf_append(b, s + start, len - start); |
|
3332
|
0
|
|
|
|
|
|
} |
|
3333
|
|
|
|
|
|
|
|
|
3334
|
|
|
|
|
|
|
/* Serialize an AV of AVs to TabSeparated format for HTTP INSERT. |
|
3335
|
|
|
|
|
|
|
* Returns malloc'd buffer; caller must Safefree(). */ |
|
3336
|
0
|
|
|
|
|
|
static char* serialize_av_to_tsv(pTHX_ AV *rows, size_t *out_len) { |
|
3337
|
|
|
|
|
|
|
native_buf_t b; |
|
3338
|
0
|
|
|
|
|
|
SSize_t nrows = av_len(rows) + 1; |
|
3339
|
|
|
|
|
|
|
SSize_t r; |
|
3340
|
|
|
|
|
|
|
|
|
3341
|
0
|
|
|
|
|
|
nbuf_init(&b); |
|
3342
|
|
|
|
|
|
|
|
|
3343
|
0
|
0
|
|
|
|
|
for (r = 0; r < nrows; r++) { |
|
3344
|
0
|
|
|
|
|
|
SV **row_svp = av_fetch(rows, r, 0); |
|
3345
|
|
|
|
|
|
|
AV *row; |
|
3346
|
|
|
|
|
|
|
SSize_t ncols, c; |
|
3347
|
|
|
|
|
|
|
|
|
3348
|
0
|
0
|
|
|
|
|
if (!row_svp || !SvROK(*row_svp) || |
|
|
|
0
|
|
|
|
|
|
|
3349
|
0
|
0
|
|
|
|
|
SvTYPE(SvRV(*row_svp)) != SVt_PVAV) { |
|
3350
|
0
|
|
|
|
|
|
Safefree(b.data); |
|
3351
|
0
|
|
|
|
|
|
croak("insert data: row %" IVdf " is not an ARRAY ref", (IV)r); |
|
3352
|
|
|
|
|
|
|
} |
|
3353
|
0
|
|
|
|
|
|
row = (AV *)SvRV(*row_svp); |
|
3354
|
0
|
|
|
|
|
|
ncols = av_len(row) + 1; |
|
3355
|
|
|
|
|
|
|
|
|
3356
|
0
|
0
|
|
|
|
|
for (c = 0; c < ncols; c++) { |
|
3357
|
0
|
|
|
|
|
|
SV **val_svp = av_fetch(row, c, 0); |
|
3358
|
0
|
0
|
|
|
|
|
if (c > 0) |
|
3359
|
0
|
|
|
|
|
|
nbuf_u8(&b, '\t'); |
|
3360
|
|
|
|
|
|
|
|
|
3361
|
0
|
0
|
|
|
|
|
if (!val_svp || !SvOK(*val_svp)) { |
|
|
|
0
|
|
|
|
|
|
|
3362
|
0
|
|
|
|
|
|
nbuf_append(&b, "\\N", 2); |
|
3363
|
|
|
|
|
|
|
} else { |
|
3364
|
|
|
|
|
|
|
STRLEN vlen; |
|
3365
|
0
|
|
|
|
|
|
const char *v = SvPV(*val_svp, vlen); |
|
3366
|
0
|
|
|
|
|
|
tsv_escape(&b, v, vlen); |
|
3367
|
|
|
|
|
|
|
} |
|
3368
|
|
|
|
|
|
|
} |
|
3369
|
0
|
|
|
|
|
|
nbuf_u8(&b, '\n'); |
|
3370
|
|
|
|
|
|
|
} |
|
3371
|
|
|
|
|
|
|
|
|
3372
|
0
|
|
|
|
|
|
*out_len = b.len; |
|
3373
|
0
|
|
|
|
|
|
return b.data; |
|
3374
|
|
|
|
|
|
|
} |
|
3375
|
|
|
|
|
|
|
|
|
3376
|
|
|
|
|
|
|
/* |
|
3377
|
|
|
|
|
|
|
* Encode a column of text values into native binary format. |
|
3378
|
|
|
|
|
|
|
* Returns 1 on success, 0 if type is unsupported (caller falls back to inline SQL). |
|
3379
|
|
|
|
|
|
|
*/ |
|
3380
|
0
|
|
|
|
|
|
static int encode_column_text(native_buf_t *b, |
|
3381
|
|
|
|
|
|
|
const char **values, size_t *value_lens, |
|
3382
|
|
|
|
|
|
|
uint64_t nrows, col_type_t *ct) { |
|
3383
|
|
|
|
|
|
|
uint64_t i; |
|
3384
|
|
|
|
|
|
|
|
|
3385
|
0
|
|
|
|
|
|
switch (ct->code) { |
|
3386
|
0
|
|
|
|
|
|
case CT_INT8: case CT_ENUM8: { |
|
3387
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3388
|
0
|
|
|
|
|
|
int8_t v = (int8_t)strtol(values[i], NULL, 10); |
|
3389
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 1); |
|
3390
|
|
|
|
|
|
|
} |
|
3391
|
0
|
|
|
|
|
|
return 1; |
|
3392
|
|
|
|
|
|
|
} |
|
3393
|
0
|
|
|
|
|
|
case CT_INT16: case CT_ENUM16: { |
|
3394
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3395
|
0
|
|
|
|
|
|
int16_t v = (int16_t)strtol(values[i], NULL, 10); |
|
3396
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 2); |
|
3397
|
|
|
|
|
|
|
} |
|
3398
|
0
|
|
|
|
|
|
return 1; |
|
3399
|
|
|
|
|
|
|
} |
|
3400
|
0
|
|
|
|
|
|
case CT_INT32: case CT_DATE32: { |
|
3401
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3402
|
|
|
|
|
|
|
int32_t v; |
|
3403
|
0
|
0
|
|
|
|
|
if (ct->code == CT_DATE32 && value_lens[i] >= 10 |
|
|
|
0
|
|
|
|
|
|
|
3404
|
0
|
0
|
|
|
|
|
&& values[i][4] == '-') |
|
3405
|
0
|
|
|
|
|
|
v = date_string_to_days(values[i], value_lens[i]); |
|
3406
|
|
|
|
|
|
|
else |
|
3407
|
0
|
|
|
|
|
|
v = (int32_t)strtol(values[i], NULL, 10); |
|
3408
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 4); |
|
3409
|
|
|
|
|
|
|
} |
|
3410
|
0
|
|
|
|
|
|
return 1; |
|
3411
|
|
|
|
|
|
|
} |
|
3412
|
0
|
|
|
|
|
|
case CT_INT64: { |
|
3413
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3414
|
0
|
|
|
|
|
|
int64_t v = (int64_t)strtoll(values[i], NULL, 10); |
|
3415
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 8); |
|
3416
|
|
|
|
|
|
|
} |
|
3417
|
0
|
|
|
|
|
|
return 1; |
|
3418
|
|
|
|
|
|
|
} |
|
3419
|
0
|
|
|
|
|
|
case CT_UINT8: case CT_BOOL: { |
|
3420
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3421
|
0
|
|
|
|
|
|
uint8_t v = (uint8_t)strtoul(values[i], NULL, 10); |
|
3422
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 1); |
|
3423
|
|
|
|
|
|
|
} |
|
3424
|
0
|
|
|
|
|
|
return 1; |
|
3425
|
|
|
|
|
|
|
} |
|
3426
|
0
|
|
|
|
|
|
case CT_UINT16: case CT_DATE: { |
|
3427
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3428
|
|
|
|
|
|
|
uint16_t v; |
|
3429
|
0
|
0
|
|
|
|
|
if (ct->code == CT_DATE && value_lens[i] >= 10 |
|
|
|
0
|
|
|
|
|
|
|
3430
|
0
|
0
|
|
|
|
|
&& values[i][4] == '-') |
|
3431
|
0
|
|
|
|
|
|
v = (uint16_t)date_string_to_days(values[i], value_lens[i]); |
|
3432
|
|
|
|
|
|
|
else |
|
3433
|
0
|
|
|
|
|
|
v = (uint16_t)strtoul(values[i], NULL, 10); |
|
3434
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 2); |
|
3435
|
|
|
|
|
|
|
} |
|
3436
|
0
|
|
|
|
|
|
return 1; |
|
3437
|
|
|
|
|
|
|
} |
|
3438
|
0
|
|
|
|
|
|
case CT_UINT32: case CT_DATETIME: { |
|
3439
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3440
|
|
|
|
|
|
|
uint32_t v; |
|
3441
|
0
|
0
|
|
|
|
|
if (ct->code == CT_DATETIME && value_lens[i] >= 10 |
|
|
|
0
|
|
|
|
|
|
|
3442
|
0
|
0
|
|
|
|
|
&& values[i][4] == '-') |
|
3443
|
0
|
|
|
|
|
|
v = datetime_string_to_epoch(values[i], value_lens[i]); |
|
3444
|
|
|
|
|
|
|
else |
|
3445
|
0
|
|
|
|
|
|
v = (uint32_t)strtoul(values[i], NULL, 10); |
|
3446
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 4); |
|
3447
|
|
|
|
|
|
|
} |
|
3448
|
0
|
|
|
|
|
|
return 1; |
|
3449
|
|
|
|
|
|
|
} |
|
3450
|
0
|
|
|
|
|
|
case CT_UINT64: { |
|
3451
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3452
|
0
|
|
|
|
|
|
uint64_t v = (uint64_t)strtoull(values[i], NULL, 10); |
|
3453
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 8); |
|
3454
|
|
|
|
|
|
|
} |
|
3455
|
0
|
|
|
|
|
|
return 1; |
|
3456
|
|
|
|
|
|
|
} |
|
3457
|
0
|
|
|
|
|
|
case CT_FLOAT32: { |
|
3458
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3459
|
0
|
|
|
|
|
|
float v = strtof(values[i], NULL); |
|
3460
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 4); |
|
3461
|
|
|
|
|
|
|
} |
|
3462
|
0
|
|
|
|
|
|
return 1; |
|
3463
|
|
|
|
|
|
|
} |
|
3464
|
0
|
|
|
|
|
|
case CT_FLOAT64: { |
|
3465
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3466
|
0
|
|
|
|
|
|
double v = strtod(values[i], NULL); |
|
3467
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 8); |
|
3468
|
|
|
|
|
|
|
} |
|
3469
|
0
|
|
|
|
|
|
return 1; |
|
3470
|
|
|
|
|
|
|
} |
|
3471
|
0
|
|
|
|
|
|
case CT_DATETIME64: { |
|
3472
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3473
|
|
|
|
|
|
|
int64_t v; |
|
3474
|
0
|
0
|
|
|
|
|
if (value_lens[i] >= 10 && values[i][4] == '-') { |
|
|
|
0
|
|
|
|
|
|
|
3475
|
0
|
|
|
|
|
|
uint32_t epoch = datetime_string_to_epoch(values[i], value_lens[i]); |
|
3476
|
|
|
|
|
|
|
int s; |
|
3477
|
0
|
|
|
|
|
|
v = (int64_t)epoch; |
|
3478
|
0
|
0
|
|
|
|
|
for (s = 0; s < ct->param; s++) v *= 10; |
|
3479
|
|
|
|
|
|
|
/* parse fractional seconds if present (e.g. ".123") */ |
|
3480
|
0
|
0
|
|
|
|
|
if (value_lens[i] >= 20 && values[i][19] == '.') { |
|
|
|
0
|
|
|
|
|
|
|
3481
|
0
|
|
|
|
|
|
const char *fp = values[i] + 20; |
|
3482
|
0
|
|
|
|
|
|
const char *fe = values[i] + value_lens[i]; |
|
3483
|
0
|
|
|
|
|
|
int64_t frac = 0; |
|
3484
|
0
|
|
|
|
|
|
int digits = 0, prec = ct->param; |
|
3485
|
0
|
0
|
|
|
|
|
while (fp < fe && digits < prec) { |
|
|
|
0
|
|
|
|
|
|
|
3486
|
0
|
|
|
|
|
|
frac = frac * 10 + (*fp - '0'); |
|
3487
|
0
|
|
|
|
|
|
fp++; |
|
3488
|
0
|
|
|
|
|
|
digits++; |
|
3489
|
|
|
|
|
|
|
} |
|
3490
|
0
|
0
|
|
|
|
|
while (digits < prec) { frac *= 10; digits++; } |
|
3491
|
0
|
|
|
|
|
|
v += frac; |
|
3492
|
|
|
|
|
|
|
} |
|
3493
|
|
|
|
|
|
|
} else { |
|
3494
|
0
|
|
|
|
|
|
v = (int64_t)strtoll(values[i], NULL, 10); |
|
3495
|
|
|
|
|
|
|
} |
|
3496
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 8); |
|
3497
|
|
|
|
|
|
|
} |
|
3498
|
0
|
|
|
|
|
|
return 1; |
|
3499
|
|
|
|
|
|
|
} |
|
3500
|
0
|
|
|
|
|
|
case CT_DECIMAL32: { |
|
3501
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3502
|
0
|
|
|
|
|
|
const char *p = values[i]; |
|
3503
|
0
|
|
|
|
|
|
int neg = 0; |
|
3504
|
0
|
|
|
|
|
|
int64_t integer_part = 0, frac_part = 0; |
|
3505
|
0
|
|
|
|
|
|
int frac_digits = 0, scale = ct->param, s; |
|
3506
|
0
|
0
|
|
|
|
|
if (*p == '-') { neg = 1; p++; } |
|
3507
|
0
|
0
|
|
|
|
|
else if (*p == '+') p++; |
|
3508
|
0
|
0
|
|
|
|
|
while (*p >= '0' && *p <= '9') { integer_part = integer_part * 10 + (*p - '0'); p++; } |
|
|
|
0
|
|
|
|
|
|
|
3509
|
0
|
0
|
|
|
|
|
if (*p == '.') { |
|
3510
|
0
|
|
|
|
|
|
p++; |
|
3511
|
0
|
0
|
|
|
|
|
while (*p >= '0' && *p <= '9' && frac_digits < scale) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
3512
|
0
|
|
|
|
|
|
frac_part = frac_part * 10 + (*p - '0'); |
|
3513
|
0
|
|
|
|
|
|
p++; |
|
3514
|
0
|
|
|
|
|
|
frac_digits++; |
|
3515
|
|
|
|
|
|
|
} |
|
3516
|
|
|
|
|
|
|
} |
|
3517
|
0
|
0
|
|
|
|
|
for (s = frac_digits; s < scale; s++) frac_part *= 10; |
|
3518
|
0
|
0
|
|
|
|
|
for (s = 0; s < scale; s++) integer_part *= 10; |
|
3519
|
|
|
|
|
|
|
{ |
|
3520
|
0
|
|
|
|
|
|
int64_t raw = integer_part + frac_part; |
|
3521
|
0
|
0
|
|
|
|
|
if (neg) raw = -raw; |
|
3522
|
0
|
|
|
|
|
|
int32_t v = (int32_t)raw; |
|
3523
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 4); |
|
3524
|
|
|
|
|
|
|
} |
|
3525
|
|
|
|
|
|
|
} |
|
3526
|
0
|
|
|
|
|
|
return 1; |
|
3527
|
|
|
|
|
|
|
} |
|
3528
|
0
|
|
|
|
|
|
case CT_DECIMAL64: { |
|
3529
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3530
|
0
|
|
|
|
|
|
const char *p = values[i]; |
|
3531
|
0
|
|
|
|
|
|
int neg = 0; |
|
3532
|
0
|
|
|
|
|
|
int64_t integer_part = 0, frac_part = 0; |
|
3533
|
0
|
|
|
|
|
|
int frac_digits = 0, scale = ct->param, s; |
|
3534
|
0
|
0
|
|
|
|
|
if (*p == '-') { neg = 1; p++; } |
|
3535
|
0
|
0
|
|
|
|
|
else if (*p == '+') p++; |
|
3536
|
0
|
0
|
|
|
|
|
while (*p >= '0' && *p <= '9') { integer_part = integer_part * 10 + (*p - '0'); p++; } |
|
|
|
0
|
|
|
|
|
|
|
3537
|
0
|
0
|
|
|
|
|
if (*p == '.') { |
|
3538
|
0
|
|
|
|
|
|
p++; |
|
3539
|
0
|
0
|
|
|
|
|
while (*p >= '0' && *p <= '9' && frac_digits < scale) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
3540
|
0
|
|
|
|
|
|
frac_part = frac_part * 10 + (*p - '0'); |
|
3541
|
0
|
|
|
|
|
|
p++; |
|
3542
|
0
|
|
|
|
|
|
frac_digits++; |
|
3543
|
|
|
|
|
|
|
} |
|
3544
|
|
|
|
|
|
|
} |
|
3545
|
0
|
0
|
|
|
|
|
for (s = frac_digits; s < scale; s++) frac_part *= 10; |
|
3546
|
0
|
0
|
|
|
|
|
for (s = 0; s < scale; s++) integer_part *= 10; |
|
3547
|
|
|
|
|
|
|
{ |
|
3548
|
0
|
|
|
|
|
|
int64_t v = integer_part + frac_part; |
|
3549
|
0
|
0
|
|
|
|
|
if (neg) v = -v; |
|
3550
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 8); |
|
3551
|
|
|
|
|
|
|
} |
|
3552
|
|
|
|
|
|
|
} |
|
3553
|
0
|
|
|
|
|
|
return 1; |
|
3554
|
|
|
|
|
|
|
} |
|
3555
|
0
|
|
|
|
|
|
case CT_STRING: { |
|
3556
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3557
|
0
|
0
|
|
|
|
|
if (memchr(values[i], '\\', value_lens[i])) { |
|
3558
|
|
|
|
|
|
|
char *tmp; |
|
3559
|
|
|
|
|
|
|
size_t ulen; |
|
3560
|
0
|
|
|
|
|
|
Newx(tmp, value_lens[i], char); |
|
3561
|
0
|
|
|
|
|
|
ulen = tsv_unescape(values[i], value_lens[i], tmp); |
|
3562
|
0
|
|
|
|
|
|
nbuf_string(b, tmp, ulen); |
|
3563
|
0
|
|
|
|
|
|
Safefree(tmp); |
|
3564
|
|
|
|
|
|
|
} else { |
|
3565
|
0
|
|
|
|
|
|
nbuf_string(b, values[i], value_lens[i]); |
|
3566
|
|
|
|
|
|
|
} |
|
3567
|
|
|
|
|
|
|
} |
|
3568
|
0
|
|
|
|
|
|
return 1; |
|
3569
|
|
|
|
|
|
|
} |
|
3570
|
0
|
|
|
|
|
|
case CT_FIXEDSTRING: { |
|
3571
|
0
|
|
|
|
|
|
size_t fsz = (size_t)ct->param; |
|
3572
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3573
|
0
|
0
|
|
|
|
|
if (memchr(values[i], '\\', value_lens[i])) { |
|
3574
|
|
|
|
|
|
|
char *tmp; |
|
3575
|
|
|
|
|
|
|
size_t ulen; |
|
3576
|
0
|
|
|
|
|
|
size_t tmp_sz = value_lens[i] > fsz ? value_lens[i] : fsz; |
|
3577
|
0
|
|
|
|
|
|
Newxz(tmp, tmp_sz, char); |
|
3578
|
0
|
|
|
|
|
|
ulen = tsv_unescape(values[i], value_lens[i], tmp); |
|
3579
|
|
|
|
|
|
|
(void)ulen; |
|
3580
|
0
|
|
|
|
|
|
nbuf_append(b, tmp, fsz); |
|
3581
|
0
|
|
|
|
|
|
Safefree(tmp); |
|
3582
|
|
|
|
|
|
|
} else { |
|
3583
|
0
|
|
|
|
|
|
nbuf_grow(b, fsz); |
|
3584
|
|
|
|
|
|
|
{ |
|
3585
|
0
|
|
|
|
|
|
size_t cplen = value_lens[i] < fsz ? value_lens[i] : fsz; |
|
3586
|
0
|
|
|
|
|
|
memcpy(b->data + b->len, values[i], cplen); |
|
3587
|
0
|
0
|
|
|
|
|
if (cplen < fsz) |
|
3588
|
0
|
|
|
|
|
|
memset(b->data + b->len + cplen, 0, fsz - cplen); |
|
3589
|
|
|
|
|
|
|
} |
|
3590
|
0
|
|
|
|
|
|
b->len += fsz; |
|
3591
|
|
|
|
|
|
|
} |
|
3592
|
|
|
|
|
|
|
} |
|
3593
|
0
|
|
|
|
|
|
return 1; |
|
3594
|
|
|
|
|
|
|
} |
|
3595
|
0
|
|
|
|
|
|
case CT_UUID: { |
|
3596
|
|
|
|
|
|
|
/* Parse "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" → 16 bytes LE halves */ |
|
3597
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3598
|
|
|
|
|
|
|
unsigned char ubytes[16]; |
|
3599
|
0
|
|
|
|
|
|
const char *s = values[i]; |
|
3600
|
0
|
|
|
|
|
|
size_t slen = value_lens[i]; |
|
3601
|
0
|
0
|
|
|
|
|
if (slen >= 36) { |
|
3602
|
|
|
|
|
|
|
/* parse hex digits, skip dashes */ |
|
3603
|
|
|
|
|
|
|
unsigned char raw[16]; |
|
3604
|
0
|
|
|
|
|
|
int k = 0, j; |
|
3605
|
0
|
0
|
|
|
|
|
for (j = 0; j < (int)slen && k < 32; j++) { |
|
|
|
0
|
|
|
|
|
|
|
3606
|
0
|
|
|
|
|
|
char c = s[j]; |
|
3607
|
0
|
0
|
|
|
|
|
if (c == '-') continue; |
|
3608
|
|
|
|
|
|
|
{ |
|
3609
|
|
|
|
|
|
|
unsigned char nibble; |
|
3610
|
0
|
0
|
|
|
|
|
if (c >= '0' && c <= '9') nibble = c - '0'; |
|
|
|
0
|
|
|
|
|
|
|
3611
|
0
|
0
|
|
|
|
|
else if (c >= 'a' && c <= 'f') nibble = 10 + c - 'a'; |
|
|
|
0
|
|
|
|
|
|
|
3612
|
0
|
0
|
|
|
|
|
else if (c >= 'A' && c <= 'F') nibble = 10 + c - 'A'; |
|
|
|
0
|
|
|
|
|
|
|
3613
|
0
|
|
|
|
|
|
else nibble = 0; |
|
3614
|
0
|
0
|
|
|
|
|
if (k % 2 == 0) raw[k/2] = nibble << 4; |
|
3615
|
0
|
|
|
|
|
|
else raw[k/2] |= nibble; |
|
3616
|
|
|
|
|
|
|
} |
|
3617
|
0
|
|
|
|
|
|
k++; |
|
3618
|
|
|
|
|
|
|
} |
|
3619
|
|
|
|
|
|
|
/* Reverse each 8-byte half for LE storage */ |
|
3620
|
0
|
0
|
|
|
|
|
for (k = 0; k < 8; k++) ubytes[k] = raw[7 - k]; |
|
3621
|
0
|
0
|
|
|
|
|
for (k = 0; k < 8; k++) ubytes[8 + k] = raw[15 - k]; |
|
3622
|
|
|
|
|
|
|
} else { |
|
3623
|
0
|
|
|
|
|
|
memset(ubytes, 0, 16); |
|
3624
|
|
|
|
|
|
|
} |
|
3625
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)ubytes, 16); |
|
3626
|
|
|
|
|
|
|
} |
|
3627
|
0
|
|
|
|
|
|
return 1; |
|
3628
|
|
|
|
|
|
|
} |
|
3629
|
0
|
|
|
|
|
|
case CT_IPV4: { |
|
3630
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3631
|
|
|
|
|
|
|
struct in_addr addr; |
|
3632
|
0
|
|
|
|
|
|
uint32_t v = 0; |
|
3633
|
|
|
|
|
|
|
char tmp[64]; |
|
3634
|
0
|
|
|
|
|
|
size_t cplen = value_lens[i] < 63 ? value_lens[i] : 63; |
|
3635
|
0
|
|
|
|
|
|
memcpy(tmp, values[i], cplen); |
|
3636
|
0
|
|
|
|
|
|
tmp[cplen] = '\0'; |
|
3637
|
0
|
0
|
|
|
|
|
if (inet_pton(AF_INET, tmp, &addr) == 1) |
|
3638
|
0
|
|
|
|
|
|
v = ntohl(addr.s_addr); |
|
3639
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 4); |
|
3640
|
|
|
|
|
|
|
} |
|
3641
|
0
|
|
|
|
|
|
return 1; |
|
3642
|
|
|
|
|
|
|
} |
|
3643
|
0
|
|
|
|
|
|
case CT_IPV6: { |
|
3644
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3645
|
|
|
|
|
|
|
unsigned char addr[16]; |
|
3646
|
|
|
|
|
|
|
char tmp[64]; |
|
3647
|
0
|
|
|
|
|
|
size_t cplen = value_lens[i] < 63 ? value_lens[i] : 63; |
|
3648
|
0
|
|
|
|
|
|
memcpy(tmp, values[i], cplen); |
|
3649
|
0
|
|
|
|
|
|
tmp[cplen] = '\0'; |
|
3650
|
0
|
|
|
|
|
|
memset(addr, 0, 16); |
|
3651
|
0
|
|
|
|
|
|
inet_pton(AF_INET6, tmp, addr); |
|
3652
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)addr, 16); |
|
3653
|
|
|
|
|
|
|
} |
|
3654
|
0
|
|
|
|
|
|
return 1; |
|
3655
|
|
|
|
|
|
|
} |
|
3656
|
0
|
|
|
|
|
|
case CT_NULLABLE: { |
|
3657
|
|
|
|
|
|
|
/* null bitmap + inner column */ |
|
3658
|
|
|
|
|
|
|
uint8_t *nulls; |
|
3659
|
|
|
|
|
|
|
const char **inner_vals; |
|
3660
|
|
|
|
|
|
|
size_t *inner_lens; |
|
3661
|
|
|
|
|
|
|
static const char zero_str[] = "0"; |
|
3662
|
|
|
|
|
|
|
static const char empty_str[] = ""; |
|
3663
|
|
|
|
|
|
|
|
|
3664
|
0
|
|
|
|
|
|
Newx(nulls, nrows, uint8_t); |
|
3665
|
0
|
0
|
|
|
|
|
Newxz(inner_vals, nrows, const char *); |
|
3666
|
0
|
0
|
|
|
|
|
Newx(inner_lens, nrows, size_t); |
|
3667
|
|
|
|
|
|
|
|
|
3668
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3669
|
0
|
0
|
|
|
|
|
if (is_tsv_null(values[i], value_lens[i])) { |
|
3670
|
0
|
|
|
|
|
|
nulls[i] = 1; |
|
3671
|
|
|
|
|
|
|
/* placeholder for null — use zero/empty depending on inner type */ |
|
3672
|
0
|
0
|
|
|
|
|
if (ct->inner->code == CT_STRING || ct->inner->code == CT_FIXEDSTRING) { |
|
|
|
0
|
|
|
|
|
|
|
3673
|
0
|
|
|
|
|
|
inner_vals[i] = empty_str; |
|
3674
|
0
|
|
|
|
|
|
inner_lens[i] = 0; |
|
3675
|
|
|
|
|
|
|
} else { |
|
3676
|
0
|
|
|
|
|
|
inner_vals[i] = zero_str; |
|
3677
|
0
|
|
|
|
|
|
inner_lens[i] = 1; |
|
3678
|
|
|
|
|
|
|
} |
|
3679
|
|
|
|
|
|
|
} else { |
|
3680
|
0
|
|
|
|
|
|
nulls[i] = 0; |
|
3681
|
0
|
|
|
|
|
|
inner_vals[i] = values[i]; |
|
3682
|
0
|
|
|
|
|
|
inner_lens[i] = value_lens[i]; |
|
3683
|
|
|
|
|
|
|
} |
|
3684
|
|
|
|
|
|
|
} |
|
3685
|
|
|
|
|
|
|
|
|
3686
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)nulls, nrows); |
|
3687
|
|
|
|
|
|
|
{ |
|
3688
|
0
|
|
|
|
|
|
int rc = encode_column_text(b, inner_vals, inner_lens, nrows, ct->inner); |
|
3689
|
0
|
|
|
|
|
|
Safefree(nulls); |
|
3690
|
0
|
|
|
|
|
|
Safefree(inner_vals); |
|
3691
|
0
|
|
|
|
|
|
Safefree(inner_lens); |
|
3692
|
0
|
|
|
|
|
|
return rc; |
|
3693
|
|
|
|
|
|
|
} |
|
3694
|
|
|
|
|
|
|
} |
|
3695
|
0
|
|
|
|
|
|
case CT_LOWCARDINALITY: { |
|
3696
|
|
|
|
|
|
|
/* Trivial 1:1 dictionary: each value is its own dict entry. |
|
3697
|
|
|
|
|
|
|
* This is correct wire format, just not deduplicated. */ |
|
3698
|
|
|
|
|
|
|
int key_type; |
|
3699
|
|
|
|
|
|
|
size_t idx_size; |
|
3700
|
0
|
|
|
|
|
|
uint64_t ser_type, version = 1; |
|
3701
|
|
|
|
|
|
|
native_buf_t dict_buf; |
|
3702
|
|
|
|
|
|
|
int rc; |
|
3703
|
|
|
|
|
|
|
|
|
3704
|
0
|
0
|
|
|
|
|
if (nrows <= 0xFF) { key_type = 0; idx_size = 1; } |
|
3705
|
0
|
0
|
|
|
|
|
else if (nrows <= 0xFFFF) { key_type = 1; idx_size = 2; } |
|
3706
|
0
|
|
|
|
|
|
else { key_type = 2; idx_size = 4; } |
|
3707
|
|
|
|
|
|
|
|
|
3708
|
0
|
|
|
|
|
|
ser_type = (uint64_t)key_type | (1ULL << 9) | (1ULL << 10); |
|
3709
|
|
|
|
|
|
|
/* HasAdditionalKeys | NeedUpdateDictionary */ |
|
3710
|
|
|
|
|
|
|
|
|
3711
|
|
|
|
|
|
|
/* version (prefix) */ |
|
3712
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&version, 8); |
|
3713
|
|
|
|
|
|
|
/* serialization_type */ |
|
3714
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&ser_type, 8); |
|
3715
|
|
|
|
|
|
|
/* num_keys */ |
|
3716
|
|
|
|
|
|
|
{ |
|
3717
|
0
|
|
|
|
|
|
uint64_t nk = nrows; |
|
3718
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&nk, 8); |
|
3719
|
|
|
|
|
|
|
} |
|
3720
|
|
|
|
|
|
|
/* dictionary data: encode as inner type */ |
|
3721
|
0
|
|
|
|
|
|
nbuf_init(&dict_buf); |
|
3722
|
0
|
|
|
|
|
|
rc = encode_column_text(&dict_buf, values, value_lens, nrows, ct->inner); |
|
3723
|
0
|
0
|
|
|
|
|
if (!rc) { Safefree(dict_buf.data); return 0; } |
|
3724
|
0
|
|
|
|
|
|
nbuf_append(b, dict_buf.data, dict_buf.len); |
|
3725
|
0
|
|
|
|
|
|
Safefree(dict_buf.data); |
|
3726
|
|
|
|
|
|
|
/* num_indices */ |
|
3727
|
|
|
|
|
|
|
{ |
|
3728
|
0
|
|
|
|
|
|
uint64_t ni = nrows; |
|
3729
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&ni, 8); |
|
3730
|
|
|
|
|
|
|
} |
|
3731
|
|
|
|
|
|
|
/* indices: [0, 1, 2, ..., nrows-1] */ |
|
3732
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3733
|
0
|
0
|
|
|
|
|
if (idx_size == 1) { |
|
3734
|
0
|
|
|
|
|
|
uint8_t idx = (uint8_t)i; |
|
3735
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&idx, 1); |
|
3736
|
0
|
0
|
|
|
|
|
} else if (idx_size == 2) { |
|
3737
|
0
|
|
|
|
|
|
uint16_t idx = (uint16_t)i; |
|
3738
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&idx, 2); |
|
3739
|
|
|
|
|
|
|
} else { |
|
3740
|
0
|
|
|
|
|
|
uint32_t idx = (uint32_t)i; |
|
3741
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&idx, 4); |
|
3742
|
|
|
|
|
|
|
} |
|
3743
|
|
|
|
|
|
|
} |
|
3744
|
0
|
|
|
|
|
|
return 1; |
|
3745
|
|
|
|
|
|
|
} |
|
3746
|
0
|
|
|
|
|
|
default: |
|
3747
|
0
|
|
|
|
|
|
return 0; /* unsupported type — fall back to inline SQL */ |
|
3748
|
|
|
|
|
|
|
} |
|
3749
|
|
|
|
|
|
|
} |
|
3750
|
|
|
|
|
|
|
|
|
3751
|
|
|
|
|
|
|
/* |
|
3752
|
|
|
|
|
|
|
* Encode a column of Perl SV values into native binary format. |
|
3753
|
|
|
|
|
|
|
* Like encode_column_text() but takes SVs directly — no TSV parsing/unescaping. |
|
3754
|
|
|
|
|
|
|
* Returns 1 on success, 0 if type is unsupported. |
|
3755
|
|
|
|
|
|
|
*/ |
|
3756
|
0
|
|
|
|
|
|
static int encode_column_sv(pTHX_ native_buf_t *b, |
|
3757
|
|
|
|
|
|
|
SV **values, uint64_t nrows, |
|
3758
|
|
|
|
|
|
|
col_type_t *ct) { |
|
3759
|
|
|
|
|
|
|
uint64_t i; |
|
3760
|
|
|
|
|
|
|
|
|
3761
|
0
|
|
|
|
|
|
switch (ct->code) { |
|
3762
|
0
|
|
|
|
|
|
case CT_INT8: case CT_ENUM8: { |
|
3763
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3764
|
0
|
0
|
|
|
|
|
int8_t v = SvIOK(values[i]) ? (int8_t)SvIV(values[i]) |
|
3765
|
0
|
|
|
|
|
|
: (int8_t)strtol(SvPV_nolen(values[i]), NULL, 10); |
|
3766
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 1); |
|
3767
|
|
|
|
|
|
|
} |
|
3768
|
0
|
|
|
|
|
|
return 1; |
|
3769
|
|
|
|
|
|
|
} |
|
3770
|
0
|
|
|
|
|
|
case CT_INT16: case CT_ENUM16: { |
|
3771
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3772
|
0
|
0
|
|
|
|
|
int16_t v = SvIOK(values[i]) ? (int16_t)SvIV(values[i]) |
|
3773
|
0
|
|
|
|
|
|
: (int16_t)strtol(SvPV_nolen(values[i]), NULL, 10); |
|
3774
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 2); |
|
3775
|
|
|
|
|
|
|
} |
|
3776
|
0
|
|
|
|
|
|
return 1; |
|
3777
|
|
|
|
|
|
|
} |
|
3778
|
0
|
|
|
|
|
|
case CT_INT32: case CT_DATE32: { |
|
3779
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3780
|
|
|
|
|
|
|
int32_t v; |
|
3781
|
0
|
0
|
|
|
|
|
if (SvIOK(values[i])) { |
|
3782
|
0
|
|
|
|
|
|
v = (int32_t)SvIV(values[i]); |
|
3783
|
|
|
|
|
|
|
} else { |
|
3784
|
|
|
|
|
|
|
STRLEN vlen; |
|
3785
|
0
|
|
|
|
|
|
const char *s = SvPV(values[i], vlen); |
|
3786
|
0
|
0
|
|
|
|
|
if (ct->code == CT_DATE32 && vlen >= 10 && s[4] == '-') |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
3787
|
0
|
|
|
|
|
|
v = date_string_to_days(s, vlen); |
|
3788
|
|
|
|
|
|
|
else |
|
3789
|
0
|
|
|
|
|
|
v = (int32_t)strtol(s, NULL, 10); |
|
3790
|
|
|
|
|
|
|
} |
|
3791
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 4); |
|
3792
|
|
|
|
|
|
|
} |
|
3793
|
0
|
|
|
|
|
|
return 1; |
|
3794
|
|
|
|
|
|
|
} |
|
3795
|
0
|
|
|
|
|
|
case CT_INT64: { |
|
3796
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3797
|
0
|
|
|
|
|
|
int64_t v = SvIOK(values[i]) ? (int64_t)SvIV(values[i]) |
|
3798
|
0
|
0
|
|
|
|
|
: (int64_t)strtoll(SvPV_nolen(values[i]), NULL, 10); |
|
3799
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 8); |
|
3800
|
|
|
|
|
|
|
} |
|
3801
|
0
|
|
|
|
|
|
return 1; |
|
3802
|
|
|
|
|
|
|
} |
|
3803
|
0
|
|
|
|
|
|
case CT_UINT8: case CT_BOOL: { |
|
3804
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3805
|
0
|
0
|
|
|
|
|
uint8_t v = SvIOK(values[i]) ? (uint8_t)SvUV(values[i]) |
|
3806
|
0
|
|
|
|
|
|
: (uint8_t)strtoul(SvPV_nolen(values[i]), NULL, 10); |
|
3807
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 1); |
|
3808
|
|
|
|
|
|
|
} |
|
3809
|
0
|
|
|
|
|
|
return 1; |
|
3810
|
|
|
|
|
|
|
} |
|
3811
|
0
|
|
|
|
|
|
case CT_UINT16: case CT_DATE: { |
|
3812
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3813
|
|
|
|
|
|
|
uint16_t v; |
|
3814
|
0
|
0
|
|
|
|
|
if (SvIOK(values[i])) { |
|
3815
|
0
|
|
|
|
|
|
v = (uint16_t)SvUV(values[i]); |
|
3816
|
|
|
|
|
|
|
} else { |
|
3817
|
|
|
|
|
|
|
STRLEN vlen; |
|
3818
|
0
|
|
|
|
|
|
const char *s = SvPV(values[i], vlen); |
|
3819
|
0
|
0
|
|
|
|
|
if (ct->code == CT_DATE && vlen >= 10 && s[4] == '-') |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
3820
|
0
|
|
|
|
|
|
v = (uint16_t)date_string_to_days(s, vlen); |
|
3821
|
|
|
|
|
|
|
else |
|
3822
|
0
|
|
|
|
|
|
v = (uint16_t)strtoul(s, NULL, 10); |
|
3823
|
|
|
|
|
|
|
} |
|
3824
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 2); |
|
3825
|
|
|
|
|
|
|
} |
|
3826
|
0
|
|
|
|
|
|
return 1; |
|
3827
|
|
|
|
|
|
|
} |
|
3828
|
0
|
|
|
|
|
|
case CT_UINT32: case CT_DATETIME: { |
|
3829
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3830
|
|
|
|
|
|
|
uint32_t v; |
|
3831
|
0
|
0
|
|
|
|
|
if (SvIOK(values[i])) { |
|
3832
|
0
|
|
|
|
|
|
v = (uint32_t)SvUV(values[i]); |
|
3833
|
|
|
|
|
|
|
} else { |
|
3834
|
|
|
|
|
|
|
STRLEN vlen; |
|
3835
|
0
|
|
|
|
|
|
const char *s = SvPV(values[i], vlen); |
|
3836
|
0
|
0
|
|
|
|
|
if (ct->code == CT_DATETIME && vlen >= 10 && s[4] == '-') |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
3837
|
0
|
|
|
|
|
|
v = datetime_string_to_epoch(s, vlen); |
|
3838
|
|
|
|
|
|
|
else |
|
3839
|
0
|
|
|
|
|
|
v = (uint32_t)strtoul(s, NULL, 10); |
|
3840
|
|
|
|
|
|
|
} |
|
3841
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 4); |
|
3842
|
|
|
|
|
|
|
} |
|
3843
|
0
|
|
|
|
|
|
return 1; |
|
3844
|
|
|
|
|
|
|
} |
|
3845
|
0
|
|
|
|
|
|
case CT_UINT64: { |
|
3846
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3847
|
0
|
|
|
|
|
|
uint64_t v = SvIOK(values[i]) ? (uint64_t)SvUV(values[i]) |
|
3848
|
0
|
0
|
|
|
|
|
: (uint64_t)strtoull(SvPV_nolen(values[i]), NULL, 10); |
|
3849
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 8); |
|
3850
|
|
|
|
|
|
|
} |
|
3851
|
0
|
|
|
|
|
|
return 1; |
|
3852
|
|
|
|
|
|
|
} |
|
3853
|
0
|
|
|
|
|
|
case CT_FLOAT32: { |
|
3854
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3855
|
0
|
|
|
|
|
|
float v = SvNOK(values[i]) ? (float)SvNV(values[i]) |
|
3856
|
0
|
0
|
|
|
|
|
: strtof(SvPV_nolen(values[i]), NULL); |
|
3857
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 4); |
|
3858
|
|
|
|
|
|
|
} |
|
3859
|
0
|
|
|
|
|
|
return 1; |
|
3860
|
|
|
|
|
|
|
} |
|
3861
|
0
|
|
|
|
|
|
case CT_FLOAT64: { |
|
3862
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3863
|
0
|
|
|
|
|
|
double v = SvNOK(values[i]) ? SvNV(values[i]) |
|
3864
|
0
|
0
|
|
|
|
|
: strtod(SvPV_nolen(values[i]), NULL); |
|
3865
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 8); |
|
3866
|
|
|
|
|
|
|
} |
|
3867
|
0
|
|
|
|
|
|
return 1; |
|
3868
|
|
|
|
|
|
|
} |
|
3869
|
0
|
|
|
|
|
|
case CT_DATETIME64: { |
|
3870
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3871
|
|
|
|
|
|
|
int64_t v; |
|
3872
|
0
|
0
|
|
|
|
|
if (SvIOK(values[i])) { |
|
3873
|
0
|
|
|
|
|
|
v = (int64_t)SvIV(values[i]); |
|
3874
|
|
|
|
|
|
|
} else { |
|
3875
|
|
|
|
|
|
|
STRLEN vlen; |
|
3876
|
0
|
|
|
|
|
|
const char *s = SvPV(values[i], vlen); |
|
3877
|
0
|
0
|
|
|
|
|
if (vlen >= 10 && s[4] == '-') { |
|
|
|
0
|
|
|
|
|
|
|
3878
|
0
|
|
|
|
|
|
uint32_t epoch = datetime_string_to_epoch(s, vlen); |
|
3879
|
|
|
|
|
|
|
int sc; |
|
3880
|
0
|
|
|
|
|
|
v = (int64_t)epoch; |
|
3881
|
0
|
0
|
|
|
|
|
for (sc = 0; sc < ct->param; sc++) v *= 10; |
|
3882
|
0
|
0
|
|
|
|
|
if (vlen >= 20 && s[19] == '.') { |
|
|
|
0
|
|
|
|
|
|
|
3883
|
0
|
|
|
|
|
|
const char *fp = s + 20; |
|
3884
|
0
|
|
|
|
|
|
const char *fe = s + vlen; |
|
3885
|
0
|
|
|
|
|
|
int64_t frac = 0; |
|
3886
|
0
|
|
|
|
|
|
int digits = 0, prec = ct->param; |
|
3887
|
0
|
0
|
|
|
|
|
while (fp < fe && digits < prec) { |
|
|
|
0
|
|
|
|
|
|
|
3888
|
0
|
|
|
|
|
|
frac = frac * 10 + (*fp - '0'); |
|
3889
|
0
|
|
|
|
|
|
fp++; |
|
3890
|
0
|
|
|
|
|
|
digits++; |
|
3891
|
|
|
|
|
|
|
} |
|
3892
|
0
|
0
|
|
|
|
|
while (digits < prec) { frac *= 10; digits++; } |
|
3893
|
0
|
|
|
|
|
|
v += frac; |
|
3894
|
|
|
|
|
|
|
} |
|
3895
|
|
|
|
|
|
|
} else { |
|
3896
|
0
|
|
|
|
|
|
v = (int64_t)strtoll(s, NULL, 10); |
|
3897
|
|
|
|
|
|
|
} |
|
3898
|
|
|
|
|
|
|
} |
|
3899
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 8); |
|
3900
|
|
|
|
|
|
|
} |
|
3901
|
0
|
|
|
|
|
|
return 1; |
|
3902
|
|
|
|
|
|
|
} |
|
3903
|
0
|
|
|
|
|
|
case CT_DECIMAL32: { |
|
3904
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3905
|
|
|
|
|
|
|
STRLEN vlen; |
|
3906
|
0
|
|
|
|
|
|
const char *p = SvPV(values[i], vlen); |
|
3907
|
0
|
|
|
|
|
|
int neg = 0; |
|
3908
|
0
|
|
|
|
|
|
int64_t integer_part = 0, frac_part = 0; |
|
3909
|
0
|
|
|
|
|
|
int frac_digits = 0, scale = ct->param, s; |
|
3910
|
0
|
0
|
|
|
|
|
if (*p == '-') { neg = 1; p++; } |
|
3911
|
0
|
0
|
|
|
|
|
else if (*p == '+') p++; |
|
3912
|
0
|
0
|
|
|
|
|
while (*p >= '0' && *p <= '9') { integer_part = integer_part * 10 + (*p - '0'); p++; } |
|
|
|
0
|
|
|
|
|
|
|
3913
|
0
|
0
|
|
|
|
|
if (*p == '.') { |
|
3914
|
0
|
|
|
|
|
|
p++; |
|
3915
|
0
|
0
|
|
|
|
|
while (*p >= '0' && *p <= '9' && frac_digits < scale) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
3916
|
0
|
|
|
|
|
|
frac_part = frac_part * 10 + (*p - '0'); p++; frac_digits++; |
|
3917
|
|
|
|
|
|
|
} |
|
3918
|
|
|
|
|
|
|
} |
|
3919
|
0
|
0
|
|
|
|
|
for (s = frac_digits; s < scale; s++) frac_part *= 10; |
|
3920
|
0
|
0
|
|
|
|
|
for (s = 0; s < scale; s++) integer_part *= 10; |
|
3921
|
|
|
|
|
|
|
{ |
|
3922
|
0
|
|
|
|
|
|
int64_t raw = integer_part + frac_part; |
|
3923
|
0
|
0
|
|
|
|
|
if (neg) raw = -raw; |
|
3924
|
0
|
|
|
|
|
|
int32_t v = (int32_t)raw; |
|
3925
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 4); |
|
3926
|
|
|
|
|
|
|
} |
|
3927
|
|
|
|
|
|
|
} |
|
3928
|
0
|
|
|
|
|
|
return 1; |
|
3929
|
|
|
|
|
|
|
} |
|
3930
|
0
|
|
|
|
|
|
case CT_DECIMAL64: { |
|
3931
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3932
|
|
|
|
|
|
|
STRLEN vlen; |
|
3933
|
0
|
|
|
|
|
|
const char *p = SvPV(values[i], vlen); |
|
3934
|
0
|
|
|
|
|
|
int neg = 0; |
|
3935
|
0
|
|
|
|
|
|
int64_t integer_part = 0, frac_part = 0; |
|
3936
|
0
|
|
|
|
|
|
int frac_digits = 0, scale = ct->param, s; |
|
3937
|
0
|
0
|
|
|
|
|
if (*p == '-') { neg = 1; p++; } |
|
3938
|
0
|
0
|
|
|
|
|
else if (*p == '+') p++; |
|
3939
|
0
|
0
|
|
|
|
|
while (*p >= '0' && *p <= '9') { integer_part = integer_part * 10 + (*p - '0'); p++; } |
|
|
|
0
|
|
|
|
|
|
|
3940
|
0
|
0
|
|
|
|
|
if (*p == '.') { |
|
3941
|
0
|
|
|
|
|
|
p++; |
|
3942
|
0
|
0
|
|
|
|
|
while (*p >= '0' && *p <= '9' && frac_digits < scale) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
3943
|
0
|
|
|
|
|
|
frac_part = frac_part * 10 + (*p - '0'); p++; frac_digits++; |
|
3944
|
|
|
|
|
|
|
} |
|
3945
|
|
|
|
|
|
|
} |
|
3946
|
0
|
0
|
|
|
|
|
for (s = frac_digits; s < scale; s++) frac_part *= 10; |
|
3947
|
0
|
0
|
|
|
|
|
for (s = 0; s < scale; s++) integer_part *= 10; |
|
3948
|
|
|
|
|
|
|
{ |
|
3949
|
0
|
|
|
|
|
|
int64_t v = integer_part + frac_part; |
|
3950
|
0
|
0
|
|
|
|
|
if (neg) v = -v; |
|
3951
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 8); |
|
3952
|
|
|
|
|
|
|
} |
|
3953
|
|
|
|
|
|
|
} |
|
3954
|
0
|
|
|
|
|
|
return 1; |
|
3955
|
|
|
|
|
|
|
} |
|
3956
|
0
|
|
|
|
|
|
case CT_STRING: { |
|
3957
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3958
|
|
|
|
|
|
|
STRLEN vlen; |
|
3959
|
0
|
|
|
|
|
|
const char *v = SvPV(values[i], vlen); |
|
3960
|
0
|
|
|
|
|
|
nbuf_string(b, v, vlen); |
|
3961
|
|
|
|
|
|
|
} |
|
3962
|
0
|
|
|
|
|
|
return 1; |
|
3963
|
|
|
|
|
|
|
} |
|
3964
|
0
|
|
|
|
|
|
case CT_FIXEDSTRING: { |
|
3965
|
0
|
|
|
|
|
|
size_t fsz = (size_t)ct->param; |
|
3966
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3967
|
|
|
|
|
|
|
STRLEN vlen; |
|
3968
|
0
|
|
|
|
|
|
const char *v = SvPV(values[i], vlen); |
|
3969
|
0
|
|
|
|
|
|
size_t cplen = (size_t)vlen < fsz ? (size_t)vlen : fsz; |
|
3970
|
0
|
|
|
|
|
|
nbuf_grow(b, fsz); |
|
3971
|
0
|
|
|
|
|
|
memcpy(b->data + b->len, v, cplen); |
|
3972
|
0
|
0
|
|
|
|
|
if (cplen < fsz) |
|
3973
|
0
|
|
|
|
|
|
memset(b->data + b->len + cplen, 0, fsz - cplen); |
|
3974
|
0
|
|
|
|
|
|
b->len += fsz; |
|
3975
|
|
|
|
|
|
|
} |
|
3976
|
0
|
|
|
|
|
|
return 1; |
|
3977
|
|
|
|
|
|
|
} |
|
3978
|
0
|
|
|
|
|
|
case CT_UUID: { |
|
3979
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
3980
|
|
|
|
|
|
|
unsigned char ubytes[16]; |
|
3981
|
|
|
|
|
|
|
STRLEN slen; |
|
3982
|
0
|
|
|
|
|
|
const char *s = SvPV(values[i], slen); |
|
3983
|
0
|
0
|
|
|
|
|
if (slen >= 36) { |
|
3984
|
|
|
|
|
|
|
unsigned char raw[16]; |
|
3985
|
0
|
|
|
|
|
|
int k = 0, j; |
|
3986
|
0
|
0
|
|
|
|
|
for (j = 0; j < (int)slen && k < 32; j++) { |
|
|
|
0
|
|
|
|
|
|
|
3987
|
0
|
|
|
|
|
|
char c = s[j]; |
|
3988
|
0
|
0
|
|
|
|
|
if (c == '-') continue; |
|
3989
|
|
|
|
|
|
|
{ |
|
3990
|
|
|
|
|
|
|
unsigned char nibble; |
|
3991
|
0
|
0
|
|
|
|
|
if (c >= '0' && c <= '9') nibble = c - '0'; |
|
|
|
0
|
|
|
|
|
|
|
3992
|
0
|
0
|
|
|
|
|
else if (c >= 'a' && c <= 'f') nibble = 10 + c - 'a'; |
|
|
|
0
|
|
|
|
|
|
|
3993
|
0
|
0
|
|
|
|
|
else if (c >= 'A' && c <= 'F') nibble = 10 + c - 'A'; |
|
|
|
0
|
|
|
|
|
|
|
3994
|
0
|
|
|
|
|
|
else nibble = 0; |
|
3995
|
0
|
0
|
|
|
|
|
if (k % 2 == 0) raw[k/2] = nibble << 4; |
|
3996
|
0
|
|
|
|
|
|
else raw[k/2] |= nibble; |
|
3997
|
|
|
|
|
|
|
} |
|
3998
|
0
|
|
|
|
|
|
k++; |
|
3999
|
|
|
|
|
|
|
} |
|
4000
|
0
|
0
|
|
|
|
|
for (k = 0; k < 8; k++) ubytes[k] = raw[7 - k]; |
|
4001
|
0
|
0
|
|
|
|
|
for (k = 0; k < 8; k++) ubytes[8 + k] = raw[15 - k]; |
|
4002
|
|
|
|
|
|
|
} else { |
|
4003
|
0
|
|
|
|
|
|
memset(ubytes, 0, 16); |
|
4004
|
|
|
|
|
|
|
} |
|
4005
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)ubytes, 16); |
|
4006
|
|
|
|
|
|
|
} |
|
4007
|
0
|
|
|
|
|
|
return 1; |
|
4008
|
|
|
|
|
|
|
} |
|
4009
|
0
|
|
|
|
|
|
case CT_IPV4: { |
|
4010
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
4011
|
|
|
|
|
|
|
struct in_addr addr; |
|
4012
|
0
|
|
|
|
|
|
uint32_t v = 0; |
|
4013
|
|
|
|
|
|
|
char tmp[64]; |
|
4014
|
|
|
|
|
|
|
STRLEN vlen; |
|
4015
|
0
|
|
|
|
|
|
const char *s = SvPV(values[i], vlen); |
|
4016
|
0
|
|
|
|
|
|
size_t cplen = (size_t)vlen < 63 ? (size_t)vlen : 63; |
|
4017
|
0
|
|
|
|
|
|
memcpy(tmp, s, cplen); |
|
4018
|
0
|
|
|
|
|
|
tmp[cplen] = '\0'; |
|
4019
|
0
|
0
|
|
|
|
|
if (inet_pton(AF_INET, tmp, &addr) == 1) |
|
4020
|
0
|
|
|
|
|
|
v = ntohl(addr.s_addr); |
|
4021
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&v, 4); |
|
4022
|
|
|
|
|
|
|
} |
|
4023
|
0
|
|
|
|
|
|
return 1; |
|
4024
|
|
|
|
|
|
|
} |
|
4025
|
0
|
|
|
|
|
|
case CT_IPV6: { |
|
4026
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
4027
|
|
|
|
|
|
|
unsigned char addr[16]; |
|
4028
|
|
|
|
|
|
|
char tmp[64]; |
|
4029
|
|
|
|
|
|
|
STRLEN vlen; |
|
4030
|
0
|
|
|
|
|
|
const char *s = SvPV(values[i], vlen); |
|
4031
|
0
|
|
|
|
|
|
size_t cplen = (size_t)vlen < 63 ? (size_t)vlen : 63; |
|
4032
|
0
|
|
|
|
|
|
memcpy(tmp, s, cplen); |
|
4033
|
0
|
|
|
|
|
|
tmp[cplen] = '\0'; |
|
4034
|
0
|
|
|
|
|
|
memset(addr, 0, 16); |
|
4035
|
0
|
|
|
|
|
|
inet_pton(AF_INET6, tmp, addr); |
|
4036
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)addr, 16); |
|
4037
|
|
|
|
|
|
|
} |
|
4038
|
0
|
|
|
|
|
|
return 1; |
|
4039
|
|
|
|
|
|
|
} |
|
4040
|
0
|
|
|
|
|
|
case CT_NULLABLE: { |
|
4041
|
|
|
|
|
|
|
uint8_t *nulls; |
|
4042
|
|
|
|
|
|
|
SV **inner_vals; |
|
4043
|
|
|
|
|
|
|
SV *zero_sv; |
|
4044
|
|
|
|
|
|
|
|
|
4045
|
0
|
|
|
|
|
|
Newx(nulls, nrows, uint8_t); |
|
4046
|
0
|
0
|
|
|
|
|
Newx(inner_vals, nrows ? nrows : 1, SV *); |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
4047
|
0
|
|
|
|
|
|
zero_sv = newSViv(0); |
|
4048
|
|
|
|
|
|
|
|
|
4049
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
4050
|
0
|
0
|
|
|
|
|
if (!SvOK(values[i])) { |
|
4051
|
0
|
|
|
|
|
|
nulls[i] = 1; |
|
4052
|
0
|
|
|
|
|
|
inner_vals[i] = zero_sv; |
|
4053
|
|
|
|
|
|
|
} else { |
|
4054
|
0
|
|
|
|
|
|
nulls[i] = 0; |
|
4055
|
0
|
|
|
|
|
|
inner_vals[i] = values[i]; |
|
4056
|
|
|
|
|
|
|
} |
|
4057
|
|
|
|
|
|
|
} |
|
4058
|
|
|
|
|
|
|
|
|
4059
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)nulls, nrows); |
|
4060
|
|
|
|
|
|
|
{ |
|
4061
|
0
|
|
|
|
|
|
int rc = encode_column_sv(aTHX_ b, inner_vals, nrows, ct->inner); |
|
4062
|
0
|
|
|
|
|
|
Safefree(nulls); |
|
4063
|
0
|
|
|
|
|
|
Safefree(inner_vals); |
|
4064
|
0
|
|
|
|
|
|
SvREFCNT_dec(zero_sv); |
|
4065
|
0
|
|
|
|
|
|
return rc; |
|
4066
|
|
|
|
|
|
|
} |
|
4067
|
|
|
|
|
|
|
} |
|
4068
|
0
|
|
|
|
|
|
case CT_LOWCARDINALITY: { |
|
4069
|
|
|
|
|
|
|
int key_type; |
|
4070
|
|
|
|
|
|
|
size_t idx_size; |
|
4071
|
0
|
|
|
|
|
|
uint64_t ser_type, version = 1; |
|
4072
|
|
|
|
|
|
|
native_buf_t dict_buf; |
|
4073
|
|
|
|
|
|
|
int rc; |
|
4074
|
|
|
|
|
|
|
|
|
4075
|
0
|
0
|
|
|
|
|
if (nrows <= 0xFF) { key_type = 0; idx_size = 1; } |
|
4076
|
0
|
0
|
|
|
|
|
else if (nrows <= 0xFFFF) { key_type = 1; idx_size = 2; } |
|
4077
|
0
|
|
|
|
|
|
else { key_type = 2; idx_size = 4; } |
|
4078
|
|
|
|
|
|
|
|
|
4079
|
0
|
|
|
|
|
|
ser_type = (uint64_t)key_type | (1ULL << 9) | (1ULL << 10); |
|
4080
|
|
|
|
|
|
|
|
|
4081
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&version, 8); |
|
4082
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&ser_type, 8); |
|
4083
|
|
|
|
|
|
|
{ |
|
4084
|
0
|
|
|
|
|
|
uint64_t nk = nrows; |
|
4085
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&nk, 8); |
|
4086
|
|
|
|
|
|
|
} |
|
4087
|
0
|
|
|
|
|
|
nbuf_init(&dict_buf); |
|
4088
|
0
|
|
|
|
|
|
rc = encode_column_sv(aTHX_ &dict_buf, values, nrows, ct->inner); |
|
4089
|
0
|
0
|
|
|
|
|
if (!rc) { Safefree(dict_buf.data); return 0; } |
|
4090
|
0
|
|
|
|
|
|
nbuf_append(b, dict_buf.data, dict_buf.len); |
|
4091
|
0
|
|
|
|
|
|
Safefree(dict_buf.data); |
|
4092
|
|
|
|
|
|
|
{ |
|
4093
|
0
|
|
|
|
|
|
uint64_t ni = nrows; |
|
4094
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&ni, 8); |
|
4095
|
|
|
|
|
|
|
} |
|
4096
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
4097
|
0
|
0
|
|
|
|
|
if (idx_size == 1) { |
|
4098
|
0
|
|
|
|
|
|
uint8_t idx = (uint8_t)i; |
|
4099
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&idx, 1); |
|
4100
|
0
|
0
|
|
|
|
|
} else if (idx_size == 2) { |
|
4101
|
0
|
|
|
|
|
|
uint16_t idx = (uint16_t)i; |
|
4102
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&idx, 2); |
|
4103
|
|
|
|
|
|
|
} else { |
|
4104
|
0
|
|
|
|
|
|
uint32_t idx = (uint32_t)i; |
|
4105
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)&idx, 4); |
|
4106
|
|
|
|
|
|
|
} |
|
4107
|
|
|
|
|
|
|
} |
|
4108
|
0
|
|
|
|
|
|
return 1; |
|
4109
|
|
|
|
|
|
|
} |
|
4110
|
0
|
|
|
|
|
|
case CT_ARRAY: { |
|
4111
|
|
|
|
|
|
|
/* Each value must be an AV ref. Wire format: offsets + flat inner data */ |
|
4112
|
0
|
|
|
|
|
|
uint64_t total = 0; |
|
4113
|
|
|
|
|
|
|
uint64_t *offsets; |
|
4114
|
|
|
|
|
|
|
SV **all_elems; |
|
4115
|
0
|
|
|
|
|
|
uint64_t pos = 0; |
|
4116
|
|
|
|
|
|
|
int rc; |
|
4117
|
|
|
|
|
|
|
|
|
4118
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
4119
|
|
|
|
|
|
|
AV *av; |
|
4120
|
0
|
0
|
|
|
|
|
if (!SvROK(values[i]) || SvTYPE(SvRV(values[i])) != SVt_PVAV) |
|
|
|
0
|
|
|
|
|
|
|
4121
|
0
|
|
|
|
|
|
return 0; |
|
4122
|
0
|
|
|
|
|
|
av = (AV *)SvRV(values[i]); |
|
4123
|
0
|
0
|
|
|
|
|
{ SSize_t cnt = av_len(av) + 1; if (cnt > 0) total += (uint64_t)cnt; } |
|
4124
|
|
|
|
|
|
|
} |
|
4125
|
|
|
|
|
|
|
|
|
4126
|
0
|
0
|
|
|
|
|
Newx(offsets, nrows, uint64_t); |
|
4127
|
0
|
0
|
|
|
|
|
Newx(all_elems, total ? total : 1, SV *); |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
4128
|
|
|
|
|
|
|
|
|
4129
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
4130
|
0
|
|
|
|
|
|
AV *av = (AV *)SvRV(values[i]); |
|
4131
|
0
|
|
|
|
|
|
SSize_t n = av_len(av) + 1, j; |
|
4132
|
0
|
0
|
|
|
|
|
for (j = 0; j < n; j++) { |
|
4133
|
0
|
|
|
|
|
|
SV **ep = av_fetch(av, j, 0); |
|
4134
|
0
|
0
|
|
|
|
|
all_elems[pos++] = ep ? *ep : &PL_sv_undef; |
|
4135
|
|
|
|
|
|
|
} |
|
4136
|
0
|
|
|
|
|
|
offsets[i] = pos; |
|
4137
|
|
|
|
|
|
|
} |
|
4138
|
|
|
|
|
|
|
|
|
4139
|
|
|
|
|
|
|
/* write offsets as uint64 LE */ |
|
4140
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)offsets, nrows * 8); |
|
4141
|
0
|
|
|
|
|
|
rc = encode_column_sv(aTHX_ b, all_elems, total, ct->inner); |
|
4142
|
0
|
|
|
|
|
|
Safefree(offsets); |
|
4143
|
0
|
|
|
|
|
|
Safefree(all_elems); |
|
4144
|
0
|
|
|
|
|
|
return rc; |
|
4145
|
|
|
|
|
|
|
} |
|
4146
|
0
|
|
|
|
|
|
case CT_TUPLE: { |
|
4147
|
|
|
|
|
|
|
/* Each value must be an AV ref with num_inners elements */ |
|
4148
|
|
|
|
|
|
|
int j; |
|
4149
|
0
|
0
|
|
|
|
|
for (j = 0; j < ct->num_inners; j++) { |
|
4150
|
|
|
|
|
|
|
SV **col_vals; |
|
4151
|
|
|
|
|
|
|
int rc; |
|
4152
|
0
|
0
|
|
|
|
|
Newx(col_vals, nrows ? nrows : 1, SV *); |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
4153
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
4154
|
|
|
|
|
|
|
AV *av; |
|
4155
|
|
|
|
|
|
|
SV **ep; |
|
4156
|
0
|
0
|
|
|
|
|
if (!SvROK(values[i]) || SvTYPE(SvRV(values[i])) != SVt_PVAV) { |
|
|
|
0
|
|
|
|
|
|
|
4157
|
0
|
|
|
|
|
|
Safefree(col_vals); |
|
4158
|
0
|
|
|
|
|
|
return 0; |
|
4159
|
|
|
|
|
|
|
} |
|
4160
|
0
|
|
|
|
|
|
av = (AV *)SvRV(values[i]); |
|
4161
|
0
|
|
|
|
|
|
ep = av_fetch(av, j, 0); |
|
4162
|
0
|
0
|
|
|
|
|
col_vals[i] = ep ? *ep : &PL_sv_undef; |
|
4163
|
|
|
|
|
|
|
} |
|
4164
|
0
|
|
|
|
|
|
rc = encode_column_sv(aTHX_ b, col_vals, nrows, ct->inners[j]); |
|
4165
|
0
|
|
|
|
|
|
Safefree(col_vals); |
|
4166
|
0
|
0
|
|
|
|
|
if (!rc) return 0; |
|
4167
|
|
|
|
|
|
|
} |
|
4168
|
0
|
|
|
|
|
|
return 1; |
|
4169
|
|
|
|
|
|
|
} |
|
4170
|
0
|
|
|
|
|
|
case CT_MAP: { |
|
4171
|
|
|
|
|
|
|
/* Each value must be a hashref. Wire format: offsets + key column + value column */ |
|
4172
|
0
|
|
|
|
|
|
uint64_t total = 0; |
|
4173
|
|
|
|
|
|
|
uint64_t *offsets; |
|
4174
|
|
|
|
|
|
|
SV **all_keys, **all_vals; |
|
4175
|
0
|
|
|
|
|
|
uint64_t pos = 0; |
|
4176
|
|
|
|
|
|
|
int rc; |
|
4177
|
|
|
|
|
|
|
|
|
4178
|
0
|
0
|
|
|
|
|
if (ct->num_inners != 2) return 0; |
|
4179
|
|
|
|
|
|
|
|
|
4180
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
4181
|
|
|
|
|
|
|
HV *hv; |
|
4182
|
0
|
0
|
|
|
|
|
if (!SvROK(values[i]) || SvTYPE(SvRV(values[i])) != SVt_PVHV) |
|
|
|
0
|
|
|
|
|
|
|
4183
|
0
|
|
|
|
|
|
return 0; |
|
4184
|
0
|
|
|
|
|
|
hv = (HV *)SvRV(values[i]); |
|
4185
|
0
|
0
|
|
|
|
|
total += HvUSEDKEYS(hv); |
|
4186
|
|
|
|
|
|
|
} |
|
4187
|
|
|
|
|
|
|
|
|
4188
|
0
|
0
|
|
|
|
|
Newx(offsets, nrows, uint64_t); |
|
4189
|
0
|
0
|
|
|
|
|
Newx(all_keys, total ? total : 1, SV *); |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
4190
|
0
|
0
|
|
|
|
|
Newx(all_vals, total ? total : 1, SV *); |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
4191
|
|
|
|
|
|
|
|
|
4192
|
0
|
0
|
|
|
|
|
for (i = 0; i < nrows; i++) { |
|
4193
|
0
|
|
|
|
|
|
HV *hv = (HV *)SvRV(values[i]); |
|
4194
|
|
|
|
|
|
|
HE *he; |
|
4195
|
0
|
|
|
|
|
|
hv_iterinit(hv); |
|
4196
|
0
|
0
|
|
|
|
|
while ((he = hv_iternext(hv))) { |
|
4197
|
0
|
|
|
|
|
|
all_keys[pos] = hv_iterkeysv(he); |
|
4198
|
0
|
|
|
|
|
|
all_vals[pos] = hv_iterval(hv, he); |
|
4199
|
0
|
|
|
|
|
|
pos++; |
|
4200
|
|
|
|
|
|
|
} |
|
4201
|
0
|
|
|
|
|
|
offsets[i] = pos; |
|
4202
|
|
|
|
|
|
|
} |
|
4203
|
|
|
|
|
|
|
|
|
4204
|
0
|
|
|
|
|
|
nbuf_append(b, (const char *)offsets, nrows * 8); |
|
4205
|
0
|
|
|
|
|
|
rc = encode_column_sv(aTHX_ b, all_keys, total, ct->inners[0]); |
|
4206
|
0
|
0
|
|
|
|
|
if (rc) rc = encode_column_sv(aTHX_ b, all_vals, total, ct->inners[1]); |
|
4207
|
0
|
|
|
|
|
|
Safefree(offsets); |
|
4208
|
0
|
|
|
|
|
|
Safefree(all_keys); |
|
4209
|
0
|
|
|
|
|
|
Safefree(all_vals); |
|
4210
|
0
|
|
|
|
|
|
return rc; |
|
4211
|
|
|
|
|
|
|
} |
|
4212
|
0
|
|
|
|
|
|
default: |
|
4213
|
0
|
|
|
|
|
|
return 0; |
|
4214
|
|
|
|
|
|
|
} |
|
4215
|
|
|
|
|
|
|
} |
|
4216
|
|
|
|
|
|
|
|
|
4217
|
|
|
|
|
|
|
/* |
|
4218
|
|
|
|
|
|
|
* Wrap a filled Data block body into a CLIENT_DATA packet with optional LZ4 + empty trailing block. |
|
4219
|
|
|
|
|
|
|
* Consumes body->data (frees it). Returns malloc'd packet, or NULL on failure. |
|
4220
|
|
|
|
|
|
|
*/ |
|
4221
|
0
|
|
|
|
|
|
static char* wrap_data_block(ev_clickhouse_t *self, native_buf_t *body, size_t *out_len) { |
|
4222
|
|
|
|
|
|
|
native_buf_t pkt; |
|
4223
|
|
|
|
|
|
|
|
|
4224
|
0
|
|
|
|
|
|
nbuf_init(&pkt); |
|
4225
|
0
|
|
|
|
|
|
nbuf_varuint(&pkt, CLIENT_DATA); |
|
4226
|
0
|
|
|
|
|
|
nbuf_cstring(&pkt, ""); /* table name — outside compression */ |
|
4227
|
|
|
|
|
|
|
|
|
4228
|
|
|
|
|
|
|
#ifdef HAVE_LZ4 |
|
4229
|
|
|
|
|
|
|
if (self->compress) { |
|
4230
|
|
|
|
|
|
|
char *compressed; |
|
4231
|
|
|
|
|
|
|
size_t comp_len; |
|
4232
|
|
|
|
|
|
|
compressed = ch_lz4_compress(body->data, body->len, &comp_len); |
|
4233
|
|
|
|
|
|
|
Safefree(body->data); |
|
4234
|
|
|
|
|
|
|
body->data = NULL; |
|
4235
|
|
|
|
|
|
|
if (compressed) { |
|
4236
|
|
|
|
|
|
|
nbuf_append(&pkt, compressed, comp_len); |
|
4237
|
|
|
|
|
|
|
Safefree(compressed); |
|
4238
|
|
|
|
|
|
|
} else { |
|
4239
|
|
|
|
|
|
|
Safefree(pkt.data); |
|
4240
|
|
|
|
|
|
|
*out_len = 0; |
|
4241
|
|
|
|
|
|
|
return NULL; |
|
4242
|
|
|
|
|
|
|
} |
|
4243
|
|
|
|
|
|
|
} else |
|
4244
|
|
|
|
|
|
|
#endif |
|
4245
|
|
|
|
|
|
|
{ |
|
4246
|
0
|
|
|
|
|
|
nbuf_append(&pkt, body->data, body->len); |
|
4247
|
0
|
|
|
|
|
|
Safefree(body->data); |
|
4248
|
0
|
|
|
|
|
|
body->data = NULL; |
|
4249
|
|
|
|
|
|
|
} |
|
4250
|
|
|
|
|
|
|
|
|
4251
|
0
|
|
|
|
|
|
nbuf_empty_data_block(&pkt, self->compress); |
|
4252
|
|
|
|
|
|
|
|
|
4253
|
0
|
|
|
|
|
|
*out_len = pkt.len; |
|
4254
|
0
|
|
|
|
|
|
return pkt.data; |
|
4255
|
|
|
|
|
|
|
} |
|
4256
|
|
|
|
|
|
|
|
|
4257
|
|
|
|
|
|
|
/* |
|
4258
|
|
|
|
|
|
|
* Build a native protocol Data block from TabSeparated text data. |
|
4259
|
|
|
|
|
|
|
* col_names/col_types_str are string references into the sample block buffer. |
|
4260
|
|
|
|
|
|
|
* Returns malloc'd packet data (CLIENT_DATA + block), or NULL on failure. |
|
4261
|
|
|
|
|
|
|
*/ |
|
4262
|
0
|
|
|
|
|
|
static char* build_native_insert_data(ev_clickhouse_t *self, |
|
4263
|
|
|
|
|
|
|
const char *tsv_data, size_t tsv_len, |
|
4264
|
|
|
|
|
|
|
const char **col_names, size_t *col_name_lens, |
|
4265
|
|
|
|
|
|
|
const char **col_types_str, size_t *col_type_lens, |
|
4266
|
|
|
|
|
|
|
col_type_t **col_types, |
|
4267
|
|
|
|
|
|
|
int num_cols, |
|
4268
|
|
|
|
|
|
|
size_t *out_len) { |
|
4269
|
|
|
|
|
|
|
/* Parse TSV into rows and fields */ |
|
4270
|
0
|
|
|
|
|
|
int nrows = 0, max_rows = 64; |
|
4271
|
0
|
|
|
|
|
|
const char **fields = NULL; /* flat array: fields[row * num_cols + col] */ |
|
4272
|
0
|
|
|
|
|
|
size_t *field_lens = NULL; |
|
4273
|
0
|
|
|
|
|
|
const char *p = tsv_data; |
|
4274
|
0
|
|
|
|
|
|
const char *end = tsv_data + tsv_len; |
|
4275
|
|
|
|
|
|
|
|
|
4276
|
0
|
|
|
|
|
|
Newxz(fields, max_rows * num_cols, const char *); |
|
4277
|
0
|
|
|
|
|
|
Newx(field_lens, max_rows * num_cols, size_t); |
|
4278
|
|
|
|
|
|
|
|
|
4279
|
0
|
0
|
|
|
|
|
while (p < end) { |
|
4280
|
0
|
|
|
|
|
|
const char *line_end = memchr(p, '\n', end - p); |
|
4281
|
0
|
0
|
|
|
|
|
const char *line_limit = line_end ? line_end : end; |
|
4282
|
|
|
|
|
|
|
int col; |
|
4283
|
|
|
|
|
|
|
|
|
4284
|
|
|
|
|
|
|
/* skip empty trailing line */ |
|
4285
|
0
|
0
|
|
|
|
|
if (p == line_limit) { p = line_limit + 1; continue; } |
|
4286
|
|
|
|
|
|
|
|
|
4287
|
0
|
0
|
|
|
|
|
if (nrows >= max_rows) { |
|
4288
|
0
|
0
|
|
|
|
|
if (max_rows > INT_MAX / 2 || |
|
|
|
0
|
|
|
|
|
|
|
4289
|
0
|
0
|
|
|
|
|
(num_cols > 0 && max_rows * 2 > INT_MAX / num_cols)) { |
|
4290
|
0
|
|
|
|
|
|
Safefree(fields); |
|
4291
|
0
|
|
|
|
|
|
Safefree(field_lens); |
|
4292
|
0
|
|
|
|
|
|
*out_len = 0; |
|
4293
|
0
|
|
|
|
|
|
return NULL; |
|
4294
|
|
|
|
|
|
|
} |
|
4295
|
0
|
|
|
|
|
|
max_rows *= 2; |
|
4296
|
0
|
|
|
|
|
|
Renew(fields, max_rows * num_cols, const char *); |
|
4297
|
0
|
|
|
|
|
|
Renew(field_lens, max_rows * num_cols, size_t); |
|
4298
|
|
|
|
|
|
|
} |
|
4299
|
|
|
|
|
|
|
|
|
4300
|
|
|
|
|
|
|
/* split line by tabs */ |
|
4301
|
|
|
|
|
|
|
{ |
|
4302
|
0
|
|
|
|
|
|
const char *fp = p; |
|
4303
|
0
|
0
|
|
|
|
|
for (col = 0; col < num_cols; col++) { |
|
4304
|
|
|
|
|
|
|
const char *tab; |
|
4305
|
0
|
0
|
|
|
|
|
if (fp > line_limit) fp = line_limit; |
|
4306
|
0
|
0
|
|
|
|
|
if (col < num_cols - 1) { |
|
4307
|
0
|
|
|
|
|
|
tab = memchr(fp, '\t', line_limit - fp); |
|
4308
|
0
|
0
|
|
|
|
|
if (!tab) tab = line_limit; |
|
4309
|
|
|
|
|
|
|
} else { |
|
4310
|
0
|
|
|
|
|
|
tab = line_limit; |
|
4311
|
|
|
|
|
|
|
} |
|
4312
|
0
|
|
|
|
|
|
fields[nrows * num_cols + col] = fp; |
|
4313
|
0
|
|
|
|
|
|
field_lens[nrows * num_cols + col] = tab - fp; |
|
4314
|
0
|
|
|
|
|
|
fp = tab + 1; |
|
4315
|
|
|
|
|
|
|
} |
|
4316
|
|
|
|
|
|
|
} |
|
4317
|
0
|
|
|
|
|
|
nrows++; |
|
4318
|
0
|
|
|
|
|
|
p = line_limit + 1; |
|
4319
|
|
|
|
|
|
|
} |
|
4320
|
|
|
|
|
|
|
|
|
4321
|
0
|
0
|
|
|
|
|
if (nrows == 0) { |
|
4322
|
0
|
|
|
|
|
|
Safefree(fields); |
|
4323
|
0
|
|
|
|
|
|
Safefree(field_lens); |
|
4324
|
0
|
|
|
|
|
|
*out_len = 0; |
|
4325
|
0
|
|
|
|
|
|
return NULL; |
|
4326
|
|
|
|
|
|
|
} |
|
4327
|
|
|
|
|
|
|
|
|
4328
|
|
|
|
|
|
|
/* Build the Data block body: block info + num_cols + num_rows + columns */ |
|
4329
|
|
|
|
|
|
|
{ |
|
4330
|
|
|
|
|
|
|
native_buf_t body; |
|
4331
|
|
|
|
|
|
|
int col; |
|
4332
|
|
|
|
|
|
|
|
|
4333
|
0
|
|
|
|
|
|
nbuf_init(&body); |
|
4334
|
|
|
|
|
|
|
|
|
4335
|
|
|
|
|
|
|
/* block info */ |
|
4336
|
0
|
|
|
|
|
|
nbuf_varuint(&body, 1); /* field_num = 1 */ |
|
4337
|
0
|
|
|
|
|
|
nbuf_u8(&body, 0); /* is_overflows = false */ |
|
4338
|
0
|
|
|
|
|
|
nbuf_varuint(&body, 2); /* field_num = 2 */ |
|
4339
|
|
|
|
|
|
|
{ |
|
4340
|
0
|
|
|
|
|
|
int32_t bucket = -1; |
|
4341
|
0
|
|
|
|
|
|
nbuf_append(&body, (const char *)&bucket, 4); |
|
4342
|
|
|
|
|
|
|
} |
|
4343
|
0
|
|
|
|
|
|
nbuf_varuint(&body, 0); /* end of block info */ |
|
4344
|
0
|
|
|
|
|
|
nbuf_varuint(&body, (uint64_t)num_cols); |
|
4345
|
0
|
|
|
|
|
|
nbuf_varuint(&body, (uint64_t)nrows); |
|
4346
|
|
|
|
|
|
|
|
|
4347
|
|
|
|
|
|
|
/* encode each column */ |
|
4348
|
0
|
0
|
|
|
|
|
for (col = 0; col < num_cols; col++) { |
|
4349
|
|
|
|
|
|
|
const char **col_vals; |
|
4350
|
|
|
|
|
|
|
size_t *col_vlens; |
|
4351
|
|
|
|
|
|
|
int row; |
|
4352
|
|
|
|
|
|
|
|
|
4353
|
|
|
|
|
|
|
/* column name and type */ |
|
4354
|
0
|
|
|
|
|
|
nbuf_string(&body, col_names[col], col_name_lens[col]); |
|
4355
|
0
|
|
|
|
|
|
nbuf_string(&body, col_types_str[col], col_type_lens[col]); |
|
4356
|
0
|
|
|
|
|
|
nbuf_u8(&body, 0); /* has_custom_serialization = false */ |
|
4357
|
|
|
|
|
|
|
|
|
4358
|
|
|
|
|
|
|
/* gather column values from row-major fields */ |
|
4359
|
0
|
|
|
|
|
|
Newxz(col_vals, nrows, const char *); |
|
4360
|
0
|
|
|
|
|
|
Newx(col_vlens, nrows, size_t); |
|
4361
|
0
|
0
|
|
|
|
|
for (row = 0; row < nrows; row++) { |
|
4362
|
0
|
|
|
|
|
|
col_vals[row] = fields[row * num_cols + col]; |
|
4363
|
0
|
|
|
|
|
|
col_vlens[row] = field_lens[row * num_cols + col]; |
|
4364
|
|
|
|
|
|
|
} |
|
4365
|
|
|
|
|
|
|
|
|
4366
|
0
|
0
|
|
|
|
|
if (!encode_column_text(&body, col_vals, col_vlens, |
|
4367
|
0
|
|
|
|
|
|
(uint64_t)nrows, col_types[col])) { |
|
4368
|
0
|
|
|
|
|
|
Safefree(col_vals); |
|
4369
|
0
|
|
|
|
|
|
Safefree(col_vlens); |
|
4370
|
0
|
|
|
|
|
|
Safefree(body.data); |
|
4371
|
0
|
|
|
|
|
|
Safefree(fields); |
|
4372
|
0
|
|
|
|
|
|
Safefree(field_lens); |
|
4373
|
0
|
|
|
|
|
|
*out_len = (size_t)-1; /* sentinel: encode failure */ |
|
4374
|
0
|
|
|
|
|
|
return NULL; |
|
4375
|
|
|
|
|
|
|
} |
|
4376
|
0
|
|
|
|
|
|
Safefree(col_vals); |
|
4377
|
0
|
|
|
|
|
|
Safefree(col_vlens); |
|
4378
|
|
|
|
|
|
|
} |
|
4379
|
|
|
|
|
|
|
|
|
4380
|
0
|
|
|
|
|
|
Safefree(fields); |
|
4381
|
0
|
|
|
|
|
|
Safefree(field_lens); |
|
4382
|
|
|
|
|
|
|
|
|
4383
|
|
|
|
|
|
|
{ |
|
4384
|
0
|
|
|
|
|
|
char *result = wrap_data_block(self, &body, out_len); |
|
4385
|
0
|
0
|
|
|
|
|
if (!result) { *out_len = 0; return NULL; } |
|
4386
|
0
|
|
|
|
|
|
return result; |
|
4387
|
|
|
|
|
|
|
} |
|
4388
|
|
|
|
|
|
|
} |
|
4389
|
|
|
|
|
|
|
} |
|
4390
|
|
|
|
|
|
|
|
|
4391
|
|
|
|
|
|
|
/* |
|
4392
|
|
|
|
|
|
|
* Build a native protocol Data block from an AV of AV refs. |
|
4393
|
|
|
|
|
|
|
* Like build_native_insert_data() but encodes SVs directly via encode_column_sv(). |
|
4394
|
|
|
|
|
|
|
*/ |
|
4395
|
0
|
|
|
|
|
|
static char* build_native_insert_data_from_av(pTHX_ ev_clickhouse_t *self, |
|
4396
|
|
|
|
|
|
|
AV *rows, |
|
4397
|
|
|
|
|
|
|
const char **col_names, size_t *col_name_lens, |
|
4398
|
|
|
|
|
|
|
const char **col_types_str, size_t *col_type_lens, |
|
4399
|
|
|
|
|
|
|
col_type_t **col_types, |
|
4400
|
|
|
|
|
|
|
int num_cols, |
|
4401
|
|
|
|
|
|
|
size_t *out_len) { |
|
4402
|
0
|
|
|
|
|
|
SSize_t nrows = av_len(rows) + 1; |
|
4403
|
|
|
|
|
|
|
native_buf_t body; |
|
4404
|
|
|
|
|
|
|
int col; |
|
4405
|
|
|
|
|
|
|
|
|
4406
|
0
|
0
|
|
|
|
|
if (nrows <= 0) { |
|
4407
|
0
|
|
|
|
|
|
*out_len = 0; |
|
4408
|
0
|
|
|
|
|
|
return NULL; |
|
4409
|
|
|
|
|
|
|
} |
|
4410
|
|
|
|
|
|
|
|
|
4411
|
0
|
|
|
|
|
|
nbuf_init(&body); |
|
4412
|
|
|
|
|
|
|
|
|
4413
|
|
|
|
|
|
|
/* block info */ |
|
4414
|
0
|
|
|
|
|
|
nbuf_varuint(&body, 1); /* field_num = 1 */ |
|
4415
|
0
|
|
|
|
|
|
nbuf_u8(&body, 0); /* is_overflows = false */ |
|
4416
|
0
|
|
|
|
|
|
nbuf_varuint(&body, 2); /* field_num = 2 */ |
|
4417
|
|
|
|
|
|
|
{ |
|
4418
|
0
|
|
|
|
|
|
int32_t bucket = -1; |
|
4419
|
0
|
|
|
|
|
|
nbuf_append(&body, (const char *)&bucket, 4); |
|
4420
|
|
|
|
|
|
|
} |
|
4421
|
0
|
|
|
|
|
|
nbuf_varuint(&body, 0); /* end of block info */ |
|
4422
|
0
|
|
|
|
|
|
nbuf_varuint(&body, (uint64_t)num_cols); |
|
4423
|
0
|
|
|
|
|
|
nbuf_varuint(&body, (uint64_t)nrows); |
|
4424
|
|
|
|
|
|
|
|
|
4425
|
|
|
|
|
|
|
/* encode each column */ |
|
4426
|
0
|
0
|
|
|
|
|
for (col = 0; col < num_cols; col++) { |
|
4427
|
|
|
|
|
|
|
SV **col_vals; |
|
4428
|
|
|
|
|
|
|
SSize_t row; |
|
4429
|
|
|
|
|
|
|
|
|
4430
|
0
|
|
|
|
|
|
nbuf_string(&body, col_names[col], col_name_lens[col]); |
|
4431
|
0
|
|
|
|
|
|
nbuf_string(&body, col_types_str[col], col_type_lens[col]); |
|
4432
|
0
|
|
|
|
|
|
nbuf_u8(&body, 0); /* has_custom_serialization = false */ |
|
4433
|
|
|
|
|
|
|
|
|
4434
|
|
|
|
|
|
|
/* gather column values from row-major AV */ |
|
4435
|
0
|
0
|
|
|
|
|
Newx(col_vals, nrows, SV *); |
|
4436
|
0
|
0
|
|
|
|
|
for (row = 0; row < nrows; row++) { |
|
4437
|
0
|
|
|
|
|
|
SV **row_svp = av_fetch(rows, row, 0); |
|
4438
|
|
|
|
|
|
|
AV *row_av; |
|
4439
|
|
|
|
|
|
|
SV **val_svp; |
|
4440
|
|
|
|
|
|
|
|
|
4441
|
0
|
0
|
|
|
|
|
if (!row_svp || !SvROK(*row_svp) || SvTYPE(SvRV(*row_svp)) != SVt_PVAV) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
4442
|
0
|
|
|
|
|
|
Safefree(col_vals); |
|
4443
|
0
|
|
|
|
|
|
Safefree(body.data); |
|
4444
|
0
|
|
|
|
|
|
*out_len = (size_t)-1; /* sentinel: encode failure */ |
|
4445
|
0
|
|
|
|
|
|
return NULL; |
|
4446
|
|
|
|
|
|
|
} |
|
4447
|
0
|
|
|
|
|
|
row_av = (AV *)SvRV(*row_svp); |
|
4448
|
0
|
|
|
|
|
|
val_svp = av_fetch(row_av, col, 0); |
|
4449
|
0
|
0
|
|
|
|
|
col_vals[row] = (val_svp && *val_svp) ? *val_svp : &PL_sv_undef; |
|
|
|
0
|
|
|
|
|
|
|
4450
|
|
|
|
|
|
|
} |
|
4451
|
|
|
|
|
|
|
|
|
4452
|
0
|
0
|
|
|
|
|
if (!encode_column_sv(aTHX_ &body, col_vals, (uint64_t)nrows, col_types[col])) { |
|
4453
|
0
|
|
|
|
|
|
Safefree(col_vals); |
|
4454
|
0
|
|
|
|
|
|
Safefree(body.data); |
|
4455
|
0
|
|
|
|
|
|
*out_len = (size_t)-1; /* sentinel: encode failure */ |
|
4456
|
0
|
|
|
|
|
|
return NULL; |
|
4457
|
|
|
|
|
|
|
} |
|
4458
|
0
|
|
|
|
|
|
Safefree(col_vals); |
|
4459
|
|
|
|
|
|
|
} |
|
4460
|
|
|
|
|
|
|
|
|
4461
|
|
|
|
|
|
|
{ |
|
4462
|
0
|
|
|
|
|
|
char *result = wrap_data_block(self, &body, out_len); |
|
4463
|
0
|
0
|
|
|
|
|
if (!result) { *out_len = 0; return NULL; } |
|
4464
|
0
|
|
|
|
|
|
return result; |
|
4465
|
|
|
|
|
|
|
} |
|
4466
|
|
|
|
|
|
|
} |
|
4467
|
|
|
|
|
|
|
|
|
4468
|
|
|
|
|
|
|
/* --- Native protocol response parser --- */ |
|
4469
|
|
|
|
|
|
|
|
|
4470
|
|
|
|
|
|
|
/* |
|
4471
|
|
|
|
|
|
|
* Skip block info fields (revision >= DBMS_MIN_REVISION_WITH_BLOCK_INFO). |
|
4472
|
|
|
|
|
|
|
* Returns 1 on success, 0 if need more data. |
|
4473
|
|
|
|
|
|
|
*/ |
|
4474
|
0
|
|
|
|
|
|
static int skip_block_info(const char *buf, size_t len, size_t *pos) { |
|
4475
|
0
|
|
|
|
|
|
for (;;) { |
|
4476
|
|
|
|
|
|
|
uint64_t field_num; |
|
4477
|
0
|
|
|
|
|
|
int rc = read_varuint(buf, len, pos, &field_num); |
|
4478
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
4479
|
0
|
0
|
|
|
|
|
if (rc < 0) return -1; |
|
4480
|
0
|
0
|
|
|
|
|
if (field_num == 0) return 1; /* end marker */ |
|
4481
|
0
|
0
|
|
|
|
|
if (field_num == 1) { |
|
4482
|
|
|
|
|
|
|
/* is_overflows: UInt8 */ |
|
4483
|
|
|
|
|
|
|
uint8_t dummy; |
|
4484
|
0
|
|
|
|
|
|
rc = read_u8(buf, len, pos, &dummy); |
|
4485
|
0
|
0
|
|
|
|
|
if (rc <= 0) return rc; |
|
4486
|
0
|
0
|
|
|
|
|
} else if (field_num == 2) { |
|
4487
|
|
|
|
|
|
|
/* bucket_num: Int32 */ |
|
4488
|
|
|
|
|
|
|
int32_t dummy; |
|
4489
|
0
|
|
|
|
|
|
rc = read_i32(buf, len, pos, &dummy); |
|
4490
|
0
|
0
|
|
|
|
|
if (rc <= 0) return rc; |
|
4491
|
|
|
|
|
|
|
} else { |
|
4492
|
0
|
|
|
|
|
|
return -1; /* protocol error */ |
|
4493
|
|
|
|
|
|
|
} |
|
4494
|
|
|
|
|
|
|
} |
|
4495
|
|
|
|
|
|
|
} |
|
4496
|
|
|
|
|
|
|
|
|
4497
|
|
|
|
|
|
|
/* |
|
4498
|
|
|
|
|
|
|
* Try to parse one server packet from recv_buf. |
|
4499
|
|
|
|
|
|
|
* Returns: |
|
4500
|
|
|
|
|
|
|
* 1 = packet consumed, continue reading |
|
4501
|
|
|
|
|
|
|
* 0 = need more data |
|
4502
|
|
|
|
|
|
|
* -1 = error (message in *errmsg, caller must Safefree) |
|
4503
|
|
|
|
|
|
|
* 2 = EndOfStream |
|
4504
|
|
|
|
|
|
|
* 3 = Pong |
|
4505
|
|
|
|
|
|
|
* 4 = Hello parsed (self->server_* fields populated) |
|
4506
|
|
|
|
|
|
|
*/ |
|
4507
|
0
|
|
|
|
|
|
static int parse_native_packet(ev_clickhouse_t *self, char **errmsg) { |
|
4508
|
0
|
|
|
|
|
|
const char *buf = self->recv_buf; |
|
4509
|
0
|
|
|
|
|
|
size_t len = self->recv_len; |
|
4510
|
0
|
|
|
|
|
|
size_t pos = 0; |
|
4511
|
|
|
|
|
|
|
uint64_t ptype; |
|
4512
|
|
|
|
|
|
|
int rc; |
|
4513
|
|
|
|
|
|
|
|
|
4514
|
0
|
|
|
|
|
|
rc = read_varuint(buf, len, &pos, &ptype); |
|
4515
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
4516
|
0
|
0
|
|
|
|
|
if (rc < 0) { |
|
4517
|
0
|
|
|
|
|
|
*errmsg = safe_strdup("malformed packet type"); |
|
4518
|
0
|
|
|
|
|
|
return -1; |
|
4519
|
|
|
|
|
|
|
} |
|
4520
|
|
|
|
|
|
|
|
|
4521
|
0
|
|
|
|
|
|
switch ((int)ptype) { |
|
4522
|
|
|
|
|
|
|
|
|
4523
|
0
|
|
|
|
|
|
case SERVER_HELLO: { |
|
4524
|
0
|
|
|
|
|
|
char *sname = NULL; |
|
4525
|
|
|
|
|
|
|
size_t sname_len; |
|
4526
|
|
|
|
|
|
|
uint64_t major, minor, revision; |
|
4527
|
|
|
|
|
|
|
|
|
4528
|
0
|
|
|
|
|
|
rc = read_native_string_alloc(buf, len, &pos, &sname, &sname_len); |
|
4529
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
4530
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed server name"); return -1; } |
|
4531
|
|
|
|
|
|
|
|
|
4532
|
0
|
|
|
|
|
|
rc = read_varuint(buf, len, &pos, &major); |
|
4533
|
0
|
0
|
|
|
|
|
if (rc == 0) { Safefree(sname); return 0; } |
|
4534
|
0
|
0
|
|
|
|
|
if (rc < 0) { Safefree(sname); *errmsg = safe_strdup("malformed server version major"); return -1; } |
|
4535
|
|
|
|
|
|
|
|
|
4536
|
0
|
|
|
|
|
|
rc = read_varuint(buf, len, &pos, &minor); |
|
4537
|
0
|
0
|
|
|
|
|
if (rc == 0) { Safefree(sname); return 0; } |
|
4538
|
0
|
0
|
|
|
|
|
if (rc < 0) { Safefree(sname); *errmsg = safe_strdup("malformed server version minor"); return -1; } |
|
4539
|
|
|
|
|
|
|
|
|
4540
|
0
|
|
|
|
|
|
rc = read_varuint(buf, len, &pos, &revision); |
|
4541
|
0
|
0
|
|
|
|
|
if (rc == 0) { Safefree(sname); return 0; } |
|
4542
|
0
|
0
|
|
|
|
|
if (rc < 0) { Safefree(sname); *errmsg = safe_strdup("malformed server revision"); return -1; } |
|
4543
|
|
|
|
|
|
|
|
|
4544
|
0
|
0
|
|
|
|
|
if (self->server_name) Safefree(self->server_name); |
|
4545
|
0
|
|
|
|
|
|
self->server_name = sname; |
|
4546
|
0
|
|
|
|
|
|
self->server_version_major = (unsigned int)major; |
|
4547
|
0
|
|
|
|
|
|
self->server_version_minor = (unsigned int)minor; |
|
4548
|
0
|
|
|
|
|
|
self->server_revision = (unsigned int)revision; |
|
4549
|
|
|
|
|
|
|
|
|
4550
|
|
|
|
|
|
|
/* timezone (our revision >= 54423) */ |
|
4551
|
|
|
|
|
|
|
if (CH_CLIENT_REVISION >= DBMS_MIN_REVISION_WITH_SERVER_TIMEZONE) { |
|
4552
|
0
|
|
|
|
|
|
char *tz = NULL; |
|
4553
|
0
|
|
|
|
|
|
rc = read_native_string_alloc(buf, len, &pos, &tz, NULL); |
|
4554
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
4555
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed timezone"); return -1; } |
|
4556
|
0
|
0
|
|
|
|
|
if (self->server_timezone) Safefree(self->server_timezone); |
|
4557
|
0
|
|
|
|
|
|
self->server_timezone = tz; |
|
4558
|
|
|
|
|
|
|
} |
|
4559
|
|
|
|
|
|
|
|
|
4560
|
|
|
|
|
|
|
/* display_name (our revision >= 54372) */ |
|
4561
|
|
|
|
|
|
|
if (CH_CLIENT_REVISION >= DBMS_MIN_REVISION_WITH_SERVER_DISPLAY_NAME) { |
|
4562
|
0
|
|
|
|
|
|
char *dn = NULL; |
|
4563
|
0
|
|
|
|
|
|
rc = read_native_string_alloc(buf, len, &pos, &dn, NULL); |
|
4564
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
4565
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed display name"); return -1; } |
|
4566
|
0
|
0
|
|
|
|
|
if (self->server_display_name) Safefree(self->server_display_name); |
|
4567
|
0
|
|
|
|
|
|
self->server_display_name = dn; |
|
4568
|
|
|
|
|
|
|
} |
|
4569
|
|
|
|
|
|
|
|
|
4570
|
|
|
|
|
|
|
/* version_patch (our revision >= 54401) */ |
|
4571
|
|
|
|
|
|
|
if (CH_CLIENT_REVISION >= DBMS_MIN_REVISION_WITH_VERSION_PATCH) { |
|
4572
|
|
|
|
|
|
|
uint64_t patch; |
|
4573
|
0
|
|
|
|
|
|
rc = read_varuint(buf, len, &pos, &patch); |
|
4574
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
4575
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed version patch"); return -1; } |
|
4576
|
0
|
|
|
|
|
|
self->server_version_patch = (unsigned int)patch; |
|
4577
|
|
|
|
|
|
|
} |
|
4578
|
|
|
|
|
|
|
|
|
4579
|
|
|
|
|
|
|
/* consume from recv_buf */ |
|
4580
|
0
|
0
|
|
|
|
|
if (pos < self->recv_len) { |
|
4581
|
0
|
|
|
|
|
|
memmove(self->recv_buf, self->recv_buf + pos, |
|
4582
|
0
|
|
|
|
|
|
self->recv_len - pos); |
|
4583
|
|
|
|
|
|
|
} |
|
4584
|
0
|
|
|
|
|
|
self->recv_len -= pos; |
|
4585
|
0
|
|
|
|
|
|
return 4; |
|
4586
|
|
|
|
|
|
|
} |
|
4587
|
|
|
|
|
|
|
|
|
4588
|
0
|
|
|
|
|
|
case SERVER_DATA: |
|
4589
|
|
|
|
|
|
|
case SERVER_TOTALS: |
|
4590
|
|
|
|
|
|
|
case SERVER_EXTREMES: { |
|
4591
|
|
|
|
|
|
|
uint64_t num_cols, num_rows; |
|
4592
|
|
|
|
|
|
|
const char *dbuf; /* data buffer (may point to decompressed data) */ |
|
4593
|
|
|
|
|
|
|
size_t dlen, dpos; |
|
4594
|
0
|
|
|
|
|
|
char *decompressed = NULL; |
|
4595
|
|
|
|
|
|
|
|
|
4596
|
|
|
|
|
|
|
/* table name — outside compression */ |
|
4597
|
0
|
|
|
|
|
|
rc = skip_native_string(buf, len, &pos); |
|
4598
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
4599
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed table name"); return -1; } |
|
4600
|
|
|
|
|
|
|
|
|
4601
|
|
|
|
|
|
|
#ifdef HAVE_LZ4 |
|
4602
|
|
|
|
|
|
|
if (self->compress) { |
|
4603
|
|
|
|
|
|
|
/* Decompress the block body — may span multiple LZ4 sub-blocks. |
|
4604
|
|
|
|
|
|
|
* ClickHouse's CompressedWriteBuffer flushes at ~1MB, so a single |
|
4605
|
|
|
|
|
|
|
* Data packet can produce multiple consecutive compressed frames. */ |
|
4606
|
|
|
|
|
|
|
size_t comp_consumed; |
|
4607
|
|
|
|
|
|
|
int need_more; |
|
4608
|
|
|
|
|
|
|
const char *lz4_err = NULL; |
|
4609
|
|
|
|
|
|
|
decompressed = ch_lz4_decompress(buf + pos, len - pos, |
|
4610
|
|
|
|
|
|
|
&dlen, &comp_consumed, |
|
4611
|
|
|
|
|
|
|
&need_more, &lz4_err); |
|
4612
|
|
|
|
|
|
|
if (!decompressed) { |
|
4613
|
|
|
|
|
|
|
if (need_more) return 0; |
|
4614
|
|
|
|
|
|
|
*errmsg = safe_strdup(lz4_err ? lz4_err : "LZ4 decompression failed"); |
|
4615
|
|
|
|
|
|
|
return -1; |
|
4616
|
|
|
|
|
|
|
} |
|
4617
|
|
|
|
|
|
|
pos += comp_consumed; |
|
4618
|
|
|
|
|
|
|
|
|
4619
|
|
|
|
|
|
|
/* Decompress any additional sub-blocks. A compressed sub-block |
|
4620
|
|
|
|
|
|
|
* starts with 16 bytes of checksum followed by method byte 0x82. |
|
4621
|
|
|
|
|
|
|
* If the remaining data doesn't match that signature, it belongs |
|
4622
|
|
|
|
|
|
|
* to the next server packet — stop decompressing. */ |
|
4623
|
|
|
|
|
|
|
while (len - pos >= CH_CHECKSUM_SIZE + CH_COMPRESS_HEADER_SIZE |
|
4624
|
|
|
|
|
|
|
&& (uint8_t)buf[pos + CH_CHECKSUM_SIZE] == CH_LZ4_METHOD) { |
|
4625
|
|
|
|
|
|
|
size_t extra_len, extra_consumed; |
|
4626
|
|
|
|
|
|
|
int extra_need_more; |
|
4627
|
|
|
|
|
|
|
const char *extra_err = NULL; |
|
4628
|
|
|
|
|
|
|
char *extra = ch_lz4_decompress(buf + pos, len - pos, |
|
4629
|
|
|
|
|
|
|
&extra_len, &extra_consumed, |
|
4630
|
|
|
|
|
|
|
&extra_need_more, &extra_err); |
|
4631
|
|
|
|
|
|
|
if (!extra) { |
|
4632
|
|
|
|
|
|
|
if (extra_need_more) { |
|
4633
|
|
|
|
|
|
|
/* Partial sub-block — need more network data. |
|
4634
|
|
|
|
|
|
|
* Discard what we decompressed so far; we'll retry |
|
4635
|
|
|
|
|
|
|
* from the beginning when more data arrives. */ |
|
4636
|
|
|
|
|
|
|
Safefree(decompressed); |
|
4637
|
|
|
|
|
|
|
return 0; |
|
4638
|
|
|
|
|
|
|
} |
|
4639
|
|
|
|
|
|
|
/* Decompression error */ |
|
4640
|
|
|
|
|
|
|
Safefree(decompressed); |
|
4641
|
|
|
|
|
|
|
*errmsg = safe_strdup(extra_err ? extra_err : "LZ4 decompression failed"); |
|
4642
|
|
|
|
|
|
|
return -1; |
|
4643
|
|
|
|
|
|
|
} |
|
4644
|
|
|
|
|
|
|
/* Append to decompressed buffer */ |
|
4645
|
|
|
|
|
|
|
Renew(decompressed, dlen + extra_len, char); |
|
4646
|
|
|
|
|
|
|
Copy(extra, decompressed + dlen, extra_len, char); |
|
4647
|
|
|
|
|
|
|
dlen += extra_len; |
|
4648
|
|
|
|
|
|
|
pos += extra_consumed; |
|
4649
|
|
|
|
|
|
|
Safefree(extra); |
|
4650
|
|
|
|
|
|
|
} |
|
4651
|
|
|
|
|
|
|
|
|
4652
|
|
|
|
|
|
|
dbuf = decompressed; |
|
4653
|
|
|
|
|
|
|
dpos = 0; |
|
4654
|
|
|
|
|
|
|
} else |
|
4655
|
|
|
|
|
|
|
#endif |
|
4656
|
|
|
|
|
|
|
{ |
|
4657
|
0
|
|
|
|
|
|
dbuf = buf; |
|
4658
|
0
|
|
|
|
|
|
dlen = len; |
|
4659
|
0
|
|
|
|
|
|
dpos = pos; |
|
4660
|
|
|
|
|
|
|
} |
|
4661
|
|
|
|
|
|
|
|
|
4662
|
|
|
|
|
|
|
/* block info */ |
|
4663
|
|
|
|
|
|
|
if (CH_CLIENT_REVISION >= DBMS_MIN_REVISION_WITH_BLOCK_INFO) { |
|
4664
|
0
|
|
|
|
|
|
rc = skip_block_info(dbuf, dlen, &dpos); |
|
4665
|
0
|
0
|
|
|
|
|
if (rc == 0) { |
|
4666
|
0
|
0
|
|
|
|
|
if (decompressed) { Safefree(decompressed); *errmsg = safe_strdup("truncated compressed block"); return -1; } |
|
4667
|
0
|
|
|
|
|
|
return 0; |
|
4668
|
|
|
|
|
|
|
} |
|
4669
|
0
|
0
|
|
|
|
|
if (rc < 0) { if (decompressed) Safefree(decompressed); *errmsg = safe_strdup("malformed block info"); return -1; } |
|
|
|
0
|
|
|
|
|
|
|
4670
|
|
|
|
|
|
|
} |
|
4671
|
|
|
|
|
|
|
|
|
4672
|
0
|
|
|
|
|
|
rc = read_varuint(dbuf, dlen, &dpos, &num_cols); |
|
4673
|
0
|
0
|
|
|
|
|
if (rc == 0) { |
|
4674
|
0
|
0
|
|
|
|
|
if (decompressed) { Safefree(decompressed); *errmsg = safe_strdup("truncated compressed block"); return -1; } |
|
4675
|
0
|
|
|
|
|
|
return 0; |
|
4676
|
|
|
|
|
|
|
} |
|
4677
|
0
|
0
|
|
|
|
|
if (rc < 0) { if (decompressed) Safefree(decompressed); *errmsg = safe_strdup("malformed num_cols"); return -1; } |
|
|
|
0
|
|
|
|
|
|
|
4678
|
|
|
|
|
|
|
|
|
4679
|
0
|
|
|
|
|
|
rc = read_varuint(dbuf, dlen, &dpos, &num_rows); |
|
4680
|
0
|
0
|
|
|
|
|
if (rc == 0) { |
|
4681
|
0
|
0
|
|
|
|
|
if (decompressed) { Safefree(decompressed); *errmsg = safe_strdup("truncated compressed block"); return -1; } |
|
4682
|
0
|
|
|
|
|
|
return 0; |
|
4683
|
|
|
|
|
|
|
} |
|
4684
|
0
|
0
|
|
|
|
|
if (rc < 0) { if (decompressed) Safefree(decompressed); *errmsg = safe_strdup("malformed num_rows"); return -1; } |
|
|
|
0
|
|
|
|
|
|
|
4685
|
|
|
|
|
|
|
|
|
4686
|
|
|
|
|
|
|
/* Empty data block — skip or handle column names/types */ |
|
4687
|
0
|
0
|
|
|
|
|
if (num_rows == 0) { |
|
4688
|
|
|
|
|
|
|
/* INSERT two-phase: server sent sample block with column structure */ |
|
4689
|
0
|
0
|
|
|
|
|
if (self->native_state == NATIVE_WAIT_INSERT_META |
|
4690
|
0
|
0
|
|
|
|
|
&& (self->insert_data || self->insert_av) && num_cols > 0) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
4691
|
|
|
|
|
|
|
const char **cnames; |
|
4692
|
|
|
|
|
|
|
size_t *cname_lens; |
|
4693
|
|
|
|
|
|
|
const char **ctypes_str; |
|
4694
|
|
|
|
|
|
|
size_t *ctype_lens; |
|
4695
|
|
|
|
|
|
|
col_type_t **ctypes; |
|
4696
|
|
|
|
|
|
|
char *data_pkt; |
|
4697
|
|
|
|
|
|
|
size_t data_pkt_len; |
|
4698
|
|
|
|
|
|
|
uint64_t c; |
|
4699
|
|
|
|
|
|
|
|
|
4700
|
0
|
0
|
|
|
|
|
Newxz(cnames, num_cols, const char *); |
|
4701
|
0
|
0
|
|
|
|
|
Newxz(cname_lens, num_cols, size_t); |
|
4702
|
0
|
0
|
|
|
|
|
Newxz(ctypes_str, num_cols, const char *); |
|
4703
|
0
|
0
|
|
|
|
|
Newxz(ctype_lens, num_cols, size_t); |
|
4704
|
0
|
0
|
|
|
|
|
Newxz(ctypes, num_cols, col_type_t *); |
|
4705
|
|
|
|
|
|
|
|
|
4706
|
0
|
0
|
|
|
|
|
for (c = 0; c < num_cols; c++) { |
|
4707
|
0
|
|
|
|
|
|
ctypes[c] = NULL; |
|
4708
|
0
|
|
|
|
|
|
rc = read_native_string_ref(dbuf, dlen, &dpos, |
|
4709
|
0
|
|
|
|
|
|
&cnames[c], &cname_lens[c]); |
|
4710
|
0
|
0
|
|
|
|
|
if (rc <= 0) { |
|
4711
|
0
|
0
|
|
|
|
|
for (c = 0; c < num_cols; c++) if (ctypes[c]) free_col_type(ctypes[c]); |
|
|
|
0
|
|
|
|
|
|
|
4712
|
0
|
|
|
|
|
|
Safefree(cnames); Safefree(cname_lens); |
|
4713
|
0
|
|
|
|
|
|
Safefree(ctypes_str); Safefree(ctype_lens); |
|
4714
|
0
|
|
|
|
|
|
Safefree(ctypes); |
|
4715
|
0
|
0
|
|
|
|
|
if (decompressed) Safefree(decompressed); |
|
4716
|
0
|
0
|
|
|
|
|
if (rc < 0 || decompressed) { *errmsg = safe_strdup("malformed cname"); return -1; } |
|
|
|
0
|
|
|
|
|
|
|
4717
|
0
|
|
|
|
|
|
return 0; |
|
4718
|
|
|
|
|
|
|
} |
|
4719
|
0
|
|
|
|
|
|
rc = read_native_string_ref(dbuf, dlen, &dpos, |
|
4720
|
0
|
|
|
|
|
|
&ctypes_str[c], &ctype_lens[c]); |
|
4721
|
0
|
0
|
|
|
|
|
if (rc <= 0) { |
|
4722
|
0
|
0
|
|
|
|
|
for (c = 0; c < num_cols; c++) if (ctypes[c]) free_col_type(ctypes[c]); |
|
|
|
0
|
|
|
|
|
|
|
4723
|
0
|
|
|
|
|
|
Safefree(cnames); Safefree(cname_lens); |
|
4724
|
0
|
|
|
|
|
|
Safefree(ctypes_str); Safefree(ctype_lens); |
|
4725
|
0
|
|
|
|
|
|
Safefree(ctypes); |
|
4726
|
0
|
0
|
|
|
|
|
if (decompressed) Safefree(decompressed); |
|
4727
|
0
|
0
|
|
|
|
|
if (rc < 0 || decompressed) { *errmsg = safe_strdup("malformed ctype"); return -1; } |
|
|
|
0
|
|
|
|
|
|
|
4728
|
0
|
|
|
|
|
|
return 0; |
|
4729
|
|
|
|
|
|
|
} |
|
4730
|
0
|
|
|
|
|
|
ctypes[c] = parse_col_type(ctypes_str[c], ctype_lens[c]); |
|
4731
|
|
|
|
|
|
|
|
|
4732
|
|
|
|
|
|
|
/* custom serialization flag (revision >= 54446) */ |
|
4733
|
0
|
0
|
|
|
|
|
if (dpos >= dlen) { |
|
4734
|
0
|
0
|
|
|
|
|
for (c = 0; c < num_cols; c++) if (ctypes[c]) free_col_type(ctypes[c]); |
|
|
|
0
|
|
|
|
|
|
|
4735
|
0
|
|
|
|
|
|
Safefree(cnames); Safefree(cname_lens); |
|
4736
|
0
|
|
|
|
|
|
Safefree(ctypes_str); Safefree(ctype_lens); |
|
4737
|
0
|
|
|
|
|
|
Safefree(ctypes); |
|
4738
|
0
|
0
|
|
|
|
|
if (decompressed) Safefree(decompressed); |
|
4739
|
0
|
0
|
|
|
|
|
if (decompressed) { *errmsg = safe_strdup("truncated custom_ser"); return -1; } |
|
4740
|
0
|
|
|
|
|
|
return 0; |
|
4741
|
|
|
|
|
|
|
} |
|
4742
|
0
|
0
|
|
|
|
|
if ((uint8_t)dbuf[dpos]) { |
|
4743
|
0
|
0
|
|
|
|
|
for (c = 0; c < num_cols; c++) if (ctypes[c]) free_col_type(ctypes[c]); |
|
|
|
0
|
|
|
|
|
|
|
4744
|
0
|
|
|
|
|
|
Safefree(cnames); Safefree(cname_lens); |
|
4745
|
0
|
|
|
|
|
|
Safefree(ctypes_str); Safefree(ctype_lens); |
|
4746
|
0
|
|
|
|
|
|
Safefree(ctypes); |
|
4747
|
0
|
0
|
|
|
|
|
if (decompressed) Safefree(decompressed); |
|
4748
|
0
|
|
|
|
|
|
*errmsg = safe_strdup("custom serialization not supported"); |
|
4749
|
0
|
|
|
|
|
|
return -1; |
|
4750
|
|
|
|
|
|
|
} |
|
4751
|
0
|
|
|
|
|
|
dpos++; |
|
4752
|
|
|
|
|
|
|
} |
|
4753
|
|
|
|
|
|
|
|
|
4754
|
|
|
|
|
|
|
/* Build binary data block from stored data */ |
|
4755
|
0
|
0
|
|
|
|
|
if (self->insert_av) { |
|
4756
|
0
|
|
|
|
|
|
data_pkt = build_native_insert_data_from_av(aTHX_ self, |
|
4757
|
0
|
|
|
|
|
|
(AV *)SvRV(self->insert_av), |
|
4758
|
|
|
|
|
|
|
cnames, cname_lens, ctypes_str, ctype_lens, |
|
4759
|
|
|
|
|
|
|
ctypes, (int)num_cols, &data_pkt_len); |
|
4760
|
|
|
|
|
|
|
} else { |
|
4761
|
0
|
|
|
|
|
|
data_pkt = build_native_insert_data(self, |
|
4762
|
0
|
|
|
|
|
|
self->insert_data, self->insert_data_len, |
|
4763
|
|
|
|
|
|
|
cnames, cname_lens, ctypes_str, ctype_lens, |
|
4764
|
|
|
|
|
|
|
ctypes, (int)num_cols, &data_pkt_len); |
|
4765
|
|
|
|
|
|
|
} |
|
4766
|
|
|
|
|
|
|
|
|
4767
|
0
|
0
|
|
|
|
|
for (c = 0; c < num_cols; c++) |
|
4768
|
0
|
|
|
|
|
|
free_col_type(ctypes[c]); |
|
4769
|
0
|
|
|
|
|
|
Safefree(cnames); Safefree(cname_lens); |
|
4770
|
0
|
|
|
|
|
|
Safefree(ctypes_str); Safefree(ctype_lens); |
|
4771
|
0
|
|
|
|
|
|
Safefree(ctypes); |
|
4772
|
|
|
|
|
|
|
|
|
4773
|
|
|
|
|
|
|
{ |
|
4774
|
|
|
|
|
|
|
/* Check encode-failure sentinel before freeing insert data */ |
|
4775
|
0
|
0
|
|
|
|
|
int encode_failed = (!data_pkt && data_pkt_len == (size_t)-1); |
|
|
|
0
|
|
|
|
|
|
|
4776
|
|
|
|
|
|
|
|
|
4777
|
|
|
|
|
|
|
/* Free stored INSERT data */ |
|
4778
|
0
|
0
|
|
|
|
|
if (self->insert_data) { |
|
4779
|
0
|
|
|
|
|
|
Safefree(self->insert_data); |
|
4780
|
0
|
|
|
|
|
|
self->insert_data = NULL; |
|
4781
|
0
|
|
|
|
|
|
self->insert_data_len = 0; |
|
4782
|
|
|
|
|
|
|
} |
|
4783
|
0
|
0
|
|
|
|
|
if (self->insert_av) { |
|
4784
|
0
|
|
|
|
|
|
SvREFCNT_dec(self->insert_av); |
|
4785
|
0
|
|
|
|
|
|
self->insert_av = NULL; |
|
4786
|
|
|
|
|
|
|
} |
|
4787
|
|
|
|
|
|
|
|
|
4788
|
0
|
0
|
|
|
|
|
if (decompressed) Safefree(decompressed); |
|
4789
|
0
|
|
|
|
|
|
else pos = dpos; |
|
4790
|
0
|
0
|
|
|
|
|
if (pos < self->recv_len) { |
|
4791
|
0
|
|
|
|
|
|
memmove(self->recv_buf, self->recv_buf + pos, |
|
4792
|
0
|
|
|
|
|
|
self->recv_len - pos); |
|
4793
|
|
|
|
|
|
|
} |
|
4794
|
0
|
|
|
|
|
|
self->recv_len -= pos; |
|
4795
|
|
|
|
|
|
|
|
|
4796
|
0
|
0
|
|
|
|
|
if (!data_pkt) { |
|
4797
|
|
|
|
|
|
|
/* Send empty Data block to complete the INSERT protocol */ |
|
4798
|
|
|
|
|
|
|
native_buf_t fallback; |
|
4799
|
0
|
|
|
|
|
|
nbuf_init(&fallback); |
|
4800
|
0
|
|
|
|
|
|
nbuf_empty_data_block(&fallback, self->compress); |
|
4801
|
0
|
|
|
|
|
|
data_pkt = fallback.data; |
|
4802
|
0
|
|
|
|
|
|
data_pkt_len = fallback.len; |
|
4803
|
0
|
0
|
|
|
|
|
if (encode_failed) |
|
4804
|
0
|
|
|
|
|
|
self->insert_err = safe_strdup( |
|
4805
|
|
|
|
|
|
|
"native INSERT encoding failed (unsupported type)"); |
|
4806
|
|
|
|
|
|
|
} |
|
4807
|
|
|
|
|
|
|
} |
|
4808
|
|
|
|
|
|
|
|
|
4809
|
|
|
|
|
|
|
/* Send the data block — write to send_buf and start writing */ |
|
4810
|
0
|
|
|
|
|
|
self->native_state = NATIVE_WAIT_RESULT; |
|
4811
|
0
|
|
|
|
|
|
ensure_send_cap(self, data_pkt_len); |
|
4812
|
0
|
|
|
|
|
|
Copy(data_pkt, self->send_buf, data_pkt_len, char); |
|
4813
|
0
|
|
|
|
|
|
self->send_len = data_pkt_len; |
|
4814
|
0
|
|
|
|
|
|
self->send_pos = 0; |
|
4815
|
0
|
|
|
|
|
|
Safefree(data_pkt); |
|
4816
|
0
|
0
|
|
|
|
|
if (try_write(self)) return -2; |
|
4817
|
0
|
|
|
|
|
|
return 1; |
|
4818
|
|
|
|
|
|
|
} |
|
4819
|
|
|
|
|
|
|
|
|
4820
|
|
|
|
|
|
|
/* INSERT two-phase with 0-column sample block: free data, |
|
4821
|
|
|
|
|
|
|
* send empty Data block, transition to WAIT_RESULT */ |
|
4822
|
0
|
0
|
|
|
|
|
if (self->native_state == NATIVE_WAIT_INSERT_META |
|
4823
|
0
|
0
|
|
|
|
|
&& (self->insert_data || self->insert_av) && num_cols == 0) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
4824
|
|
|
|
|
|
|
native_buf_t fallback; |
|
4825
|
|
|
|
|
|
|
|
|
4826
|
0
|
0
|
|
|
|
|
if (self->insert_data) { |
|
4827
|
0
|
|
|
|
|
|
Safefree(self->insert_data); |
|
4828
|
0
|
|
|
|
|
|
self->insert_data = NULL; |
|
4829
|
0
|
|
|
|
|
|
self->insert_data_len = 0; |
|
4830
|
|
|
|
|
|
|
} |
|
4831
|
0
|
0
|
|
|
|
|
if (self->insert_av) { |
|
4832
|
0
|
|
|
|
|
|
SvREFCNT_dec(self->insert_av); |
|
4833
|
0
|
|
|
|
|
|
self->insert_av = NULL; |
|
4834
|
|
|
|
|
|
|
} |
|
4835
|
|
|
|
|
|
|
|
|
4836
|
0
|
0
|
|
|
|
|
if (decompressed) Safefree(decompressed); |
|
4837
|
0
|
|
|
|
|
|
else pos = dpos; |
|
4838
|
0
|
0
|
|
|
|
|
if (pos < self->recv_len) { |
|
4839
|
0
|
|
|
|
|
|
memmove(self->recv_buf, self->recv_buf + pos, |
|
4840
|
0
|
|
|
|
|
|
self->recv_len - pos); |
|
4841
|
|
|
|
|
|
|
} |
|
4842
|
0
|
|
|
|
|
|
self->recv_len -= pos; |
|
4843
|
|
|
|
|
|
|
|
|
4844
|
0
|
|
|
|
|
|
nbuf_init(&fallback); |
|
4845
|
0
|
|
|
|
|
|
nbuf_empty_data_block(&fallback, self->compress); |
|
4846
|
0
|
|
|
|
|
|
self->native_state = NATIVE_WAIT_RESULT; |
|
4847
|
0
|
|
|
|
|
|
ensure_send_cap(self, fallback.len); |
|
4848
|
0
|
|
|
|
|
|
Copy(fallback.data, self->send_buf, fallback.len, char); |
|
4849
|
0
|
|
|
|
|
|
self->send_len = fallback.len; |
|
4850
|
0
|
|
|
|
|
|
self->send_pos = 0; |
|
4851
|
0
|
|
|
|
|
|
Safefree(fallback.data); |
|
4852
|
0
|
|
|
|
|
|
self->insert_err = safe_strdup( |
|
4853
|
|
|
|
|
|
|
"INSERT failed: server sent 0-column sample block"); |
|
4854
|
0
|
0
|
|
|
|
|
if (try_write(self)) return -2; |
|
4855
|
0
|
|
|
|
|
|
return 1; |
|
4856
|
|
|
|
|
|
|
} |
|
4857
|
|
|
|
|
|
|
|
|
4858
|
|
|
|
|
|
|
/* Normal empty block — skip column names/types/custom_serialization */ |
|
4859
|
|
|
|
|
|
|
{ |
|
4860
|
|
|
|
|
|
|
uint64_t c; |
|
4861
|
0
|
0
|
|
|
|
|
for (c = 0; c < num_cols; c++) { |
|
4862
|
0
|
0
|
|
|
|
|
if (skip_native_string(dbuf, dlen, &dpos) <= 0) { |
|
4863
|
0
|
0
|
|
|
|
|
if (decompressed) Safefree(decompressed); |
|
4864
|
0
|
|
|
|
|
|
return 0; |
|
4865
|
|
|
|
|
|
|
} |
|
4866
|
0
|
0
|
|
|
|
|
if (skip_native_string(dbuf, dlen, &dpos) <= 0) { |
|
4867
|
0
|
0
|
|
|
|
|
if (decompressed) Safefree(decompressed); |
|
4868
|
0
|
|
|
|
|
|
return 0; |
|
4869
|
|
|
|
|
|
|
} |
|
4870
|
|
|
|
|
|
|
/* custom serialization flag (revision >= 54446) */ |
|
4871
|
0
|
0
|
|
|
|
|
if (dpos >= dlen) { |
|
4872
|
0
|
0
|
|
|
|
|
if (decompressed) Safefree(decompressed); |
|
4873
|
0
|
|
|
|
|
|
return 0; |
|
4874
|
|
|
|
|
|
|
} |
|
4875
|
0
|
0
|
|
|
|
|
if ((uint8_t)dbuf[dpos]) { |
|
4876
|
0
|
0
|
|
|
|
|
if (decompressed) Safefree(decompressed); |
|
4877
|
0
|
|
|
|
|
|
*errmsg = safe_strdup("custom serialization not supported"); |
|
4878
|
0
|
|
|
|
|
|
return -1; |
|
4879
|
|
|
|
|
|
|
} |
|
4880
|
0
|
|
|
|
|
|
dpos++; |
|
4881
|
|
|
|
|
|
|
} |
|
4882
|
|
|
|
|
|
|
} |
|
4883
|
0
|
0
|
|
|
|
|
if (decompressed) Safefree(decompressed); |
|
4884
|
0
|
|
|
|
|
|
else pos = dpos; /* uncompressed: advance pos to match dpos */ |
|
4885
|
0
|
0
|
|
|
|
|
if (pos < self->recv_len) { |
|
4886
|
0
|
|
|
|
|
|
memmove(self->recv_buf, self->recv_buf + pos, |
|
4887
|
0
|
|
|
|
|
|
self->recv_len - pos); |
|
4888
|
|
|
|
|
|
|
} |
|
4889
|
0
|
|
|
|
|
|
self->recv_len -= pos; |
|
4890
|
0
|
|
|
|
|
|
return 1; |
|
4891
|
|
|
|
|
|
|
} |
|
4892
|
|
|
|
|
|
|
|
|
4893
|
|
|
|
|
|
|
/* Decode columns and convert to rows */ |
|
4894
|
|
|
|
|
|
|
{ |
|
4895
|
0
|
|
|
|
|
|
SV ***columns = NULL; |
|
4896
|
0
|
|
|
|
|
|
col_type_t **col_types = NULL; |
|
4897
|
0
|
|
|
|
|
|
const char **cnames = NULL; |
|
4898
|
0
|
|
|
|
|
|
size_t *cname_lens = NULL; |
|
4899
|
|
|
|
|
|
|
uint64_t c, r; |
|
4900
|
0
|
|
|
|
|
|
int named = (self->decode_flags & DECODE_NAMED_ROWS) ? 1 : 0; |
|
4901
|
|
|
|
|
|
|
|
|
4902
|
0
|
0
|
|
|
|
|
Newxz(columns, num_cols, SV**); |
|
4903
|
0
|
0
|
|
|
|
|
Newxz(col_types, num_cols, col_type_t*); |
|
4904
|
0
|
0
|
|
|
|
|
if (named) { |
|
4905
|
0
|
0
|
|
|
|
|
Newxz(cnames, num_cols, const char *); |
|
4906
|
0
|
0
|
|
|
|
|
Newx(cname_lens, num_cols, size_t); |
|
4907
|
|
|
|
|
|
|
} |
|
4908
|
|
|
|
|
|
|
|
|
4909
|
0
|
0
|
|
|
|
|
for (c = 0; c < num_cols; c++) { |
|
4910
|
|
|
|
|
|
|
const char *cname, *ctype; |
|
4911
|
|
|
|
|
|
|
size_t cname_len, ctype_len; |
|
4912
|
|
|
|
|
|
|
|
|
4913
|
0
|
|
|
|
|
|
columns[c] = NULL; |
|
4914
|
0
|
|
|
|
|
|
col_types[c] = NULL; |
|
4915
|
|
|
|
|
|
|
|
|
4916
|
0
|
|
|
|
|
|
rc = read_native_string_ref(dbuf, dlen, &dpos, &cname, &cname_len); |
|
4917
|
0
|
0
|
|
|
|
|
if (rc == 0) { |
|
4918
|
0
|
0
|
|
|
|
|
if (decompressed) { *errmsg = safe_strdup("truncated cname"); goto data_error; } |
|
4919
|
0
|
|
|
|
|
|
goto data_need_more; |
|
4920
|
|
|
|
|
|
|
} |
|
4921
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed cname"); goto data_error; } |
|
4922
|
|
|
|
|
|
|
|
|
4923
|
0
|
0
|
|
|
|
|
if (named) { |
|
4924
|
0
|
|
|
|
|
|
cnames[c] = cname; |
|
4925
|
0
|
|
|
|
|
|
cname_lens[c] = cname_len; |
|
4926
|
|
|
|
|
|
|
} |
|
4927
|
|
|
|
|
|
|
|
|
4928
|
|
|
|
|
|
|
/* Save column names/types on first data block of each query */ |
|
4929
|
0
|
0
|
|
|
|
|
if (c == 0 && !self->native_rows && self->native_col_names) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
4930
|
0
|
|
|
|
|
|
SvREFCNT_dec((SV*)self->native_col_names); |
|
4931
|
0
|
|
|
|
|
|
self->native_col_names = NULL; |
|
4932
|
0
|
0
|
|
|
|
|
if (self->native_col_types) { |
|
4933
|
0
|
|
|
|
|
|
SvREFCNT_dec((SV*)self->native_col_types); |
|
4934
|
0
|
|
|
|
|
|
self->native_col_types = NULL; |
|
4935
|
|
|
|
|
|
|
} |
|
4936
|
|
|
|
|
|
|
} |
|
4937
|
0
|
0
|
|
|
|
|
if (!self->native_col_names) { |
|
4938
|
0
|
0
|
|
|
|
|
if (c == 0) { |
|
4939
|
0
|
|
|
|
|
|
self->native_col_names = newAV(); |
|
4940
|
0
|
|
|
|
|
|
self->native_col_types = newAV(); |
|
4941
|
|
|
|
|
|
|
} |
|
4942
|
|
|
|
|
|
|
} |
|
4943
|
0
|
0
|
|
|
|
|
if (self->native_col_names && av_len(self->native_col_names) + 1 < (SSize_t)num_cols) |
|
|
|
0
|
|
|
|
|
|
|
4944
|
0
|
|
|
|
|
|
av_push(self->native_col_names, newSVpvn(cname, cname_len)); |
|
4945
|
|
|
|
|
|
|
|
|
4946
|
0
|
|
|
|
|
|
rc = read_native_string_ref(dbuf, dlen, &dpos, &ctype, &ctype_len); |
|
4947
|
0
|
0
|
|
|
|
|
if (rc == 0) { |
|
4948
|
0
|
0
|
|
|
|
|
if (decompressed) { *errmsg = safe_strdup("truncated ctype"); goto data_error; } |
|
4949
|
0
|
|
|
|
|
|
goto data_need_more; |
|
4950
|
|
|
|
|
|
|
} |
|
4951
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed ctype"); goto data_error; } |
|
4952
|
|
|
|
|
|
|
|
|
4953
|
0
|
|
|
|
|
|
col_types[c] = parse_col_type(ctype, ctype_len); |
|
4954
|
0
|
0
|
|
|
|
|
if (self->native_col_types && av_len(self->native_col_types) + 1 < (SSize_t)num_cols) |
|
|
|
0
|
|
|
|
|
|
|
4955
|
0
|
|
|
|
|
|
av_push(self->native_col_types, newSVpvn(ctype, ctype_len)); |
|
4956
|
|
|
|
|
|
|
|
|
4957
|
|
|
|
|
|
|
/* custom serialization flag (revision >= 54446) */ |
|
4958
|
0
|
0
|
|
|
|
|
if (dpos >= dlen) { |
|
4959
|
0
|
0
|
|
|
|
|
if (decompressed) { *errmsg = safe_strdup("truncated custom_ser"); goto data_error; } |
|
4960
|
0
|
|
|
|
|
|
goto data_need_more; |
|
4961
|
|
|
|
|
|
|
} |
|
4962
|
0
|
0
|
|
|
|
|
if ((uint8_t)dbuf[dpos]) { |
|
4963
|
0
|
|
|
|
|
|
*errmsg = safe_strdup("custom serialization not supported"); |
|
4964
|
0
|
|
|
|
|
|
goto data_error; |
|
4965
|
|
|
|
|
|
|
} |
|
4966
|
0
|
|
|
|
|
|
dpos++; |
|
4967
|
|
|
|
|
|
|
|
|
4968
|
|
|
|
|
|
|
/* Allocate LC dict state on first column of first block */ |
|
4969
|
0
|
0
|
|
|
|
|
if (c == 0 && !self->lc_dicts && num_cols > 0) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
4970
|
0
|
0
|
|
|
|
|
Newxz(self->lc_dicts, num_cols, SV**); |
|
4971
|
0
|
0
|
|
|
|
|
Newxz(self->lc_dict_sizes, num_cols, uint64_t); |
|
4972
|
0
|
|
|
|
|
|
self->lc_num_cols = (int)num_cols; |
|
4973
|
|
|
|
|
|
|
} |
|
4974
|
|
|
|
|
|
|
|
|
4975
|
|
|
|
|
|
|
{ |
|
4976
|
0
|
|
|
|
|
|
int col_err = 0; |
|
4977
|
0
|
|
|
|
|
|
columns[c] = decode_column_ex(dbuf, dlen, &dpos, num_rows, col_types[c], &col_err, self->decode_flags, self, (int)c); |
|
4978
|
0
|
0
|
|
|
|
|
if (!columns[c]) { |
|
4979
|
0
|
0
|
|
|
|
|
if (col_err || decompressed) { |
|
|
|
0
|
|
|
|
|
|
|
4980
|
0
|
|
|
|
|
|
*errmsg = safe_strdup("decode_column failed"); |
|
4981
|
0
|
|
|
|
|
|
goto data_error; |
|
4982
|
|
|
|
|
|
|
} |
|
4983
|
0
|
|
|
|
|
|
goto data_need_more; |
|
4984
|
|
|
|
|
|
|
} |
|
4985
|
|
|
|
|
|
|
} |
|
4986
|
|
|
|
|
|
|
} |
|
4987
|
|
|
|
|
|
|
|
|
4988
|
|
|
|
|
|
|
/* Convert column-oriented to row-oriented */ |
|
4989
|
|
|
|
|
|
|
{ |
|
4990
|
|
|
|
|
|
|
AV **target; |
|
4991
|
0
|
0
|
|
|
|
|
if (ptype == SERVER_TOTALS) { |
|
4992
|
0
|
0
|
|
|
|
|
if (!self->native_totals) self->native_totals = newAV(); |
|
4993
|
0
|
|
|
|
|
|
target = &self->native_totals; |
|
4994
|
0
|
0
|
|
|
|
|
} else if (ptype == SERVER_EXTREMES) { |
|
4995
|
0
|
0
|
|
|
|
|
if (!self->native_extremes) self->native_extremes = newAV(); |
|
4996
|
0
|
|
|
|
|
|
target = &self->native_extremes; |
|
4997
|
|
|
|
|
|
|
} else { |
|
4998
|
0
|
0
|
|
|
|
|
if (!self->native_rows) self->native_rows = newAV(); |
|
4999
|
0
|
|
|
|
|
|
target = &self->native_rows; |
|
5000
|
|
|
|
|
|
|
} |
|
5001
|
|
|
|
|
|
|
|
|
5002
|
0
|
0
|
|
|
|
|
if (named) { |
|
5003
|
0
|
0
|
|
|
|
|
for (r = 0; r < num_rows; r++) { |
|
5004
|
0
|
|
|
|
|
|
HV *hv = newHV(); |
|
5005
|
0
|
0
|
|
|
|
|
for (c = 0; c < num_cols; c++) { |
|
5006
|
0
|
0
|
|
|
|
|
if (!hv_store(hv, cnames[c], cname_lens[c], columns[c][r], 0)) |
|
5007
|
0
|
|
|
|
|
|
SvREFCNT_dec(columns[c][r]); |
|
5008
|
|
|
|
|
|
|
} |
|
5009
|
0
|
|
|
|
|
|
av_push(*target, newRV_noinc((SV*)hv)); |
|
5010
|
|
|
|
|
|
|
} |
|
5011
|
|
|
|
|
|
|
} else { |
|
5012
|
0
|
0
|
|
|
|
|
for (r = 0; r < num_rows; r++) { |
|
5013
|
0
|
|
|
|
|
|
AV *row = newAV(); |
|
5014
|
0
|
0
|
|
|
|
|
if (num_cols > 0) |
|
5015
|
0
|
|
|
|
|
|
av_extend(row, num_cols - 1); |
|
5016
|
0
|
0
|
|
|
|
|
for (c = 0; c < num_cols; c++) { |
|
5017
|
0
|
|
|
|
|
|
av_push(row, columns[c][r]); |
|
5018
|
|
|
|
|
|
|
} |
|
5019
|
0
|
|
|
|
|
|
av_push(*target, newRV_noinc((SV*)row)); |
|
5020
|
|
|
|
|
|
|
} |
|
5021
|
|
|
|
|
|
|
} |
|
5022
|
|
|
|
|
|
|
} |
|
5023
|
|
|
|
|
|
|
|
|
5024
|
|
|
|
|
|
|
/* Fire on_data streaming callback if set (only for DATA, not TOTALS/EXTREMES) */ |
|
5025
|
|
|
|
|
|
|
{ |
|
5026
|
0
|
0
|
|
|
|
|
SV *on_data = (ptype == SERVER_DATA) ? peek_cb_on_data(self) : NULL; |
|
5027
|
0
|
0
|
|
|
|
|
if (on_data && self->native_rows) { |
|
|
|
0
|
|
|
|
|
|
|
5028
|
0
|
|
|
|
|
|
self->callback_depth++; |
|
5029
|
|
|
|
|
|
|
{ |
|
5030
|
0
|
|
|
|
|
|
dSP; |
|
5031
|
0
|
|
|
|
|
|
ENTER; SAVETMPS; |
|
5032
|
0
|
0
|
|
|
|
|
PUSHMARK(SP); |
|
5033
|
0
|
|
|
|
|
|
PUSHs(sv_2mortal(newRV_inc((SV*)self->native_rows))); |
|
5034
|
0
|
|
|
|
|
|
PUTBACK; |
|
5035
|
0
|
|
|
|
|
|
call_sv(on_data, G_DISCARD | G_EVAL); |
|
5036
|
0
|
0
|
|
|
|
|
if (SvTRUE(ERRSV)) |
|
|
|
0
|
|
|
|
|
|
|
5037
|
0
|
0
|
|
|
|
|
warn("EV::ClickHouse: exception in on_data handler: %s", |
|
5038
|
|
|
|
|
|
|
SvPV_nolen(ERRSV)); |
|
5039
|
0
|
0
|
|
|
|
|
FREETMPS; LEAVE; |
|
5040
|
|
|
|
|
|
|
} |
|
5041
|
0
|
|
|
|
|
|
self->callback_depth--; |
|
5042
|
|
|
|
|
|
|
/* Clear accumulated rows for next block */ |
|
5043
|
0
|
|
|
|
|
|
SvREFCNT_dec((SV*)self->native_rows); |
|
5044
|
0
|
|
|
|
|
|
self->native_rows = NULL; |
|
5045
|
0
|
0
|
|
|
|
|
if (check_destroyed(self)) { |
|
5046
|
0
|
0
|
|
|
|
|
if (cnames) Safefree(cnames); |
|
5047
|
0
|
0
|
|
|
|
|
if (cname_lens) Safefree(cname_lens); |
|
5048
|
0
|
0
|
|
|
|
|
for (c = 0; c < num_cols; c++) { |
|
5049
|
0
|
|
|
|
|
|
Safefree(columns[c]); |
|
5050
|
0
|
|
|
|
|
|
free_col_type(col_types[c]); |
|
5051
|
|
|
|
|
|
|
} |
|
5052
|
0
|
|
|
|
|
|
Safefree(columns); Safefree(col_types); |
|
5053
|
0
|
0
|
|
|
|
|
if (decompressed) Safefree(decompressed); |
|
5054
|
0
|
|
|
|
|
|
return -2; |
|
5055
|
|
|
|
|
|
|
} |
|
5056
|
|
|
|
|
|
|
} |
|
5057
|
|
|
|
|
|
|
} |
|
5058
|
|
|
|
|
|
|
|
|
5059
|
|
|
|
|
|
|
/* Cleanup column arrays (SVs moved to rows, don't dec refcnt) */ |
|
5060
|
0
|
0
|
|
|
|
|
for (c = 0; c < num_cols; c++) { |
|
5061
|
0
|
|
|
|
|
|
Safefree(columns[c]); |
|
5062
|
0
|
|
|
|
|
|
free_col_type(col_types[c]); |
|
5063
|
|
|
|
|
|
|
} |
|
5064
|
0
|
|
|
|
|
|
Safefree(columns); |
|
5065
|
0
|
|
|
|
|
|
Safefree(col_types); |
|
5066
|
0
|
0
|
|
|
|
|
if (cnames) Safefree(cnames); |
|
5067
|
0
|
0
|
|
|
|
|
if (cname_lens) Safefree(cname_lens); |
|
5068
|
0
|
0
|
|
|
|
|
if (decompressed) Safefree(decompressed); |
|
5069
|
0
|
|
|
|
|
|
else pos = dpos; /* uncompressed: advance pos to match dpos */ |
|
5070
|
|
|
|
|
|
|
|
|
5071
|
|
|
|
|
|
|
/* Consume from recv_buf */ |
|
5072
|
0
|
0
|
|
|
|
|
if (pos < self->recv_len) { |
|
5073
|
0
|
|
|
|
|
|
memmove(self->recv_buf, self->recv_buf + pos, |
|
5074
|
0
|
|
|
|
|
|
self->recv_len - pos); |
|
5075
|
|
|
|
|
|
|
} |
|
5076
|
0
|
|
|
|
|
|
self->recv_len -= pos; |
|
5077
|
0
|
|
|
|
|
|
return 1; |
|
5078
|
|
|
|
|
|
|
|
|
5079
|
0
|
|
|
|
|
|
data_error: |
|
5080
|
0
|
|
|
|
|
|
data_need_more: |
|
5081
|
|
|
|
|
|
|
/* Cleanup partial decode */ |
|
5082
|
0
|
0
|
|
|
|
|
for (c = 0; c < num_cols; c++) { |
|
5083
|
0
|
0
|
|
|
|
|
if (columns[c]) { |
|
5084
|
|
|
|
|
|
|
uint64_t j; |
|
5085
|
0
|
0
|
|
|
|
|
for (j = 0; j < num_rows; j++) { |
|
5086
|
0
|
0
|
|
|
|
|
if (columns[c][j]) SvREFCNT_dec(columns[c][j]); |
|
5087
|
|
|
|
|
|
|
} |
|
5088
|
0
|
|
|
|
|
|
Safefree(columns[c]); |
|
5089
|
|
|
|
|
|
|
} |
|
5090
|
0
|
0
|
|
|
|
|
if (col_types[c]) free_col_type(col_types[c]); |
|
5091
|
|
|
|
|
|
|
} |
|
5092
|
0
|
|
|
|
|
|
Safefree(columns); |
|
5093
|
0
|
|
|
|
|
|
Safefree(col_types); |
|
5094
|
0
|
0
|
|
|
|
|
if (cnames) Safefree(cnames); |
|
5095
|
0
|
0
|
|
|
|
|
if (cname_lens) Safefree(cname_lens); |
|
5096
|
0
|
0
|
|
|
|
|
if (decompressed) Safefree(decompressed); |
|
5097
|
0
|
0
|
|
|
|
|
if (*errmsg) { |
|
5098
|
|
|
|
|
|
|
/* data_error: flush recv_buf — data is malformed, cannot resume */ |
|
5099
|
0
|
|
|
|
|
|
self->recv_len = 0; |
|
5100
|
0
|
|
|
|
|
|
return -1; |
|
5101
|
|
|
|
|
|
|
} |
|
5102
|
0
|
|
|
|
|
|
return 0; |
|
5103
|
|
|
|
|
|
|
} |
|
5104
|
|
|
|
|
|
|
} |
|
5105
|
|
|
|
|
|
|
|
|
5106
|
0
|
|
|
|
|
|
case SERVER_EXCEPTION: { |
|
5107
|
|
|
|
|
|
|
/* code: Int32, name: String, message: String, |
|
5108
|
|
|
|
|
|
|
* stack_trace: String, has_nested: UInt8 */ |
|
5109
|
|
|
|
|
|
|
int32_t code; |
|
5110
|
|
|
|
|
|
|
const char *name, *msg, *stack; |
|
5111
|
|
|
|
|
|
|
size_t name_len, msg_len, stack_len; |
|
5112
|
|
|
|
|
|
|
uint8_t has_nested; |
|
5113
|
|
|
|
|
|
|
char *err; |
|
5114
|
|
|
|
|
|
|
|
|
5115
|
|
|
|
|
|
|
/* We just read the top-level exception */ |
|
5116
|
0
|
|
|
|
|
|
rc = read_i32(buf, len, &pos, &code); |
|
5117
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
5118
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed exception code"); return -1; } |
|
5119
|
|
|
|
|
|
|
|
|
5120
|
0
|
|
|
|
|
|
rc = read_native_string_ref(buf, len, &pos, &name, &name_len); |
|
5121
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
5122
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed exception name"); return -1; } |
|
5123
|
|
|
|
|
|
|
|
|
5124
|
0
|
|
|
|
|
|
rc = read_native_string_ref(buf, len, &pos, &msg, &msg_len); |
|
5125
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
5126
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed exception message"); return -1; } |
|
5127
|
|
|
|
|
|
|
|
|
5128
|
0
|
|
|
|
|
|
rc = read_native_string_ref(buf, len, &pos, &stack, &stack_len); |
|
5129
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
5130
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed exception stack"); return -1; } |
|
5131
|
|
|
|
|
|
|
|
|
5132
|
0
|
|
|
|
|
|
rc = read_u8(buf, len, &pos, &has_nested); |
|
5133
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
5134
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed exception has_nested"); return -1; } |
|
5135
|
|
|
|
|
|
|
|
|
5136
|
|
|
|
|
|
|
/* Skip nested exceptions */ |
|
5137
|
0
|
0
|
|
|
|
|
while (has_nested) { |
|
5138
|
0
|
|
|
|
|
|
rc = read_i32(buf, len, &pos, &code); |
|
5139
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
5140
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed nested exception"); return -1; } |
|
5141
|
|
|
|
|
|
|
|
|
5142
|
0
|
|
|
|
|
|
rc = skip_native_string(buf, len, &pos); |
|
5143
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
5144
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed nested exception"); return -1; } |
|
5145
|
|
|
|
|
|
|
|
|
5146
|
0
|
|
|
|
|
|
rc = skip_native_string(buf, len, &pos); |
|
5147
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
5148
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed nested exception"); return -1; } |
|
5149
|
|
|
|
|
|
|
|
|
5150
|
0
|
|
|
|
|
|
rc = skip_native_string(buf, len, &pos); |
|
5151
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
5152
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed nested exception"); return -1; } |
|
5153
|
|
|
|
|
|
|
|
|
5154
|
0
|
|
|
|
|
|
rc = read_u8(buf, len, &pos, &has_nested); |
|
5155
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
5156
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed nested exception"); return -1; } |
|
5157
|
|
|
|
|
|
|
} |
|
5158
|
|
|
|
|
|
|
|
|
5159
|
0
|
|
|
|
|
|
self->last_error_code = code; |
|
5160
|
|
|
|
|
|
|
|
|
5161
|
0
|
|
|
|
|
|
Newx(err, msg_len + name_len + 64, char); |
|
5162
|
0
|
|
|
|
|
|
snprintf(err, msg_len + name_len + 64, "Code: %d. %.*s: %.*s", |
|
5163
|
|
|
|
|
|
|
(int)code, (int)name_len, name, (int)msg_len, msg); |
|
5164
|
|
|
|
|
|
|
|
|
5165
|
0
|
0
|
|
|
|
|
if (pos < self->recv_len) { |
|
5166
|
0
|
|
|
|
|
|
memmove(self->recv_buf, self->recv_buf + pos, |
|
5167
|
0
|
|
|
|
|
|
self->recv_len - pos); |
|
5168
|
|
|
|
|
|
|
} |
|
5169
|
0
|
|
|
|
|
|
self->recv_len -= pos; |
|
5170
|
|
|
|
|
|
|
|
|
5171
|
0
|
|
|
|
|
|
*errmsg = err; |
|
5172
|
0
|
|
|
|
|
|
return -1; |
|
5173
|
|
|
|
|
|
|
} |
|
5174
|
|
|
|
|
|
|
|
|
5175
|
0
|
|
|
|
|
|
case SERVER_PROGRESS: { |
|
5176
|
|
|
|
|
|
|
/* rows: VarUInt, bytes: VarUInt, total_rows: VarUInt, |
|
5177
|
|
|
|
|
|
|
* written_rows: VarUInt (>= 54420), written_bytes: VarUInt (>= 54420) |
|
5178
|
|
|
|
|
|
|
*/ |
|
5179
|
0
|
|
|
|
|
|
uint64_t p_rows, p_bytes, p_total, p_wrows = 0, p_wbytes = 0; |
|
5180
|
0
|
|
|
|
|
|
rc = read_varuint(buf, len, &pos, &p_rows); |
|
5181
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
5182
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed progress packet"); return -1; } |
|
5183
|
|
|
|
|
|
|
|
|
5184
|
0
|
|
|
|
|
|
rc = read_varuint(buf, len, &pos, &p_bytes); |
|
5185
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
5186
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed progress packet"); return -1; } |
|
5187
|
|
|
|
|
|
|
|
|
5188
|
0
|
|
|
|
|
|
rc = read_varuint(buf, len, &pos, &p_total); |
|
5189
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
5190
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed progress packet"); return -1; } |
|
5191
|
|
|
|
|
|
|
|
|
5192
|
|
|
|
|
|
|
if (CH_CLIENT_REVISION >= DBMS_MIN_REVISION_WITH_PROGRESS_WRITES) { |
|
5193
|
0
|
|
|
|
|
|
rc = read_varuint(buf, len, &pos, &p_wrows); |
|
5194
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
5195
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed progress packet"); return -1; } |
|
5196
|
|
|
|
|
|
|
|
|
5197
|
0
|
|
|
|
|
|
rc = read_varuint(buf, len, &pos, &p_wbytes); |
|
5198
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
5199
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed progress packet"); return -1; } |
|
5200
|
|
|
|
|
|
|
} |
|
5201
|
|
|
|
|
|
|
|
|
5202
|
0
|
0
|
|
|
|
|
if (pos < self->recv_len) { |
|
5203
|
0
|
|
|
|
|
|
memmove(self->recv_buf, self->recv_buf + pos, |
|
5204
|
0
|
|
|
|
|
|
self->recv_len - pos); |
|
5205
|
|
|
|
|
|
|
} |
|
5206
|
0
|
|
|
|
|
|
self->recv_len -= pos; |
|
5207
|
|
|
|
|
|
|
|
|
5208
|
0
|
0
|
|
|
|
|
if (NULL != self->on_progress) { |
|
5209
|
0
|
|
|
|
|
|
dSP; |
|
5210
|
0
|
|
|
|
|
|
self->callback_depth++; |
|
5211
|
0
|
|
|
|
|
|
ENTER; SAVETMPS; |
|
5212
|
0
|
0
|
|
|
|
|
PUSHMARK(SP); |
|
5213
|
0
|
0
|
|
|
|
|
EXTEND(SP, 5); |
|
5214
|
0
|
|
|
|
|
|
PUSHs(sv_2mortal(newSVuv(p_rows))); |
|
5215
|
0
|
|
|
|
|
|
PUSHs(sv_2mortal(newSVuv(p_bytes))); |
|
5216
|
0
|
|
|
|
|
|
PUSHs(sv_2mortal(newSVuv(p_total))); |
|
5217
|
0
|
|
|
|
|
|
PUSHs(sv_2mortal(newSVuv(p_wrows))); |
|
5218
|
0
|
|
|
|
|
|
PUSHs(sv_2mortal(newSVuv(p_wbytes))); |
|
5219
|
0
|
|
|
|
|
|
PUTBACK; |
|
5220
|
0
|
|
|
|
|
|
call_sv(self->on_progress, G_DISCARD | G_EVAL); |
|
5221
|
0
|
0
|
|
|
|
|
if (SvTRUE(ERRSV)) |
|
|
|
0
|
|
|
|
|
|
|
5222
|
0
|
0
|
|
|
|
|
warn("EV::ClickHouse: exception in progress handler: %s", |
|
5223
|
|
|
|
|
|
|
SvPV_nolen(ERRSV)); |
|
5224
|
0
|
0
|
|
|
|
|
FREETMPS; LEAVE; |
|
5225
|
0
|
|
|
|
|
|
self->callback_depth--; |
|
5226
|
0
|
0
|
|
|
|
|
if (check_destroyed(self)) return -2; /* destroyed */ |
|
5227
|
|
|
|
|
|
|
} |
|
5228
|
|
|
|
|
|
|
|
|
5229
|
0
|
|
|
|
|
|
return 1; |
|
5230
|
|
|
|
|
|
|
} |
|
5231
|
|
|
|
|
|
|
|
|
5232
|
0
|
|
|
|
|
|
case SERVER_PROFILE_INFO: { |
|
5233
|
|
|
|
|
|
|
uint64_t pi_rows, pi_blocks, pi_bytes, pi_applied_limit; |
|
5234
|
|
|
|
|
|
|
uint64_t pi_rows_before_limit, pi_calc_rows_before_limit; |
|
5235
|
|
|
|
|
|
|
|
|
5236
|
0
|
|
|
|
|
|
rc = read_varuint(buf, len, &pos, &pi_rows); |
|
5237
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
5238
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed profile_info packet"); return -1; } |
|
5239
|
0
|
|
|
|
|
|
rc = read_varuint(buf, len, &pos, &pi_blocks); |
|
5240
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
5241
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed profile_info packet"); return -1; } |
|
5242
|
0
|
|
|
|
|
|
rc = read_varuint(buf, len, &pos, &pi_bytes); |
|
5243
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
5244
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed profile_info packet"); return -1; } |
|
5245
|
0
|
|
|
|
|
|
rc = read_varuint(buf, len, &pos, &pi_applied_limit); |
|
5246
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
5247
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed profile_info packet"); return -1; } |
|
5248
|
0
|
|
|
|
|
|
rc = read_varuint(buf, len, &pos, &pi_rows_before_limit); |
|
5249
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
5250
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed profile_info packet"); return -1; } |
|
5251
|
0
|
|
|
|
|
|
rc = read_varuint(buf, len, &pos, &pi_calc_rows_before_limit); |
|
5252
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
5253
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed profile_info packet"); return -1; } |
|
5254
|
|
|
|
|
|
|
|
|
5255
|
0
|
|
|
|
|
|
self->profile_rows = pi_rows; |
|
5256
|
0
|
|
|
|
|
|
self->profile_bytes = pi_bytes; |
|
5257
|
0
|
|
|
|
|
|
self->profile_rows_before_limit = pi_rows_before_limit; |
|
5258
|
0
|
0
|
|
|
|
|
if (pos < self->recv_len) { |
|
5259
|
0
|
|
|
|
|
|
memmove(self->recv_buf, self->recv_buf + pos, |
|
5260
|
0
|
|
|
|
|
|
self->recv_len - pos); |
|
5261
|
|
|
|
|
|
|
} |
|
5262
|
0
|
|
|
|
|
|
self->recv_len -= pos; |
|
5263
|
0
|
|
|
|
|
|
return 1; |
|
5264
|
|
|
|
|
|
|
} |
|
5265
|
|
|
|
|
|
|
|
|
5266
|
0
|
|
|
|
|
|
case SERVER_TABLE_COLUMNS: { |
|
5267
|
|
|
|
|
|
|
/* Format: string(table_name) + string(column_description) */ |
|
5268
|
0
|
|
|
|
|
|
rc = skip_native_string(buf, len, &pos); |
|
5269
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
5270
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed table_columns packet"); return -1; } |
|
5271
|
0
|
|
|
|
|
|
rc = skip_native_string(buf, len, &pos); |
|
5272
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
5273
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed table_columns packet"); return -1; } |
|
5274
|
0
|
0
|
|
|
|
|
if (pos < self->recv_len) { |
|
5275
|
0
|
|
|
|
|
|
memmove(self->recv_buf, self->recv_buf + pos, |
|
5276
|
0
|
|
|
|
|
|
self->recv_len - pos); |
|
5277
|
|
|
|
|
|
|
} |
|
5278
|
0
|
|
|
|
|
|
self->recv_len -= pos; |
|
5279
|
0
|
|
|
|
|
|
return 1; |
|
5280
|
|
|
|
|
|
|
} |
|
5281
|
|
|
|
|
|
|
|
|
5282
|
0
|
|
|
|
|
|
case SERVER_LOG: { |
|
5283
|
|
|
|
|
|
|
/* Contains a Data block — parse like SERVER_DATA but discard */ |
|
5284
|
|
|
|
|
|
|
const char *lbuf; |
|
5285
|
|
|
|
|
|
|
size_t llen, lpos; |
|
5286
|
0
|
|
|
|
|
|
char *log_decompressed = NULL; |
|
5287
|
|
|
|
|
|
|
|
|
5288
|
|
|
|
|
|
|
/* table name — outside compression */ |
|
5289
|
0
|
|
|
|
|
|
rc = skip_native_string(buf, len, &pos); |
|
5290
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
5291
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed server log block"); return -1; } |
|
5292
|
|
|
|
|
|
|
|
|
5293
|
|
|
|
|
|
|
#ifdef HAVE_LZ4 |
|
5294
|
|
|
|
|
|
|
if (self->compress) { |
|
5295
|
|
|
|
|
|
|
size_t comp_consumed; |
|
5296
|
|
|
|
|
|
|
int need_more; |
|
5297
|
|
|
|
|
|
|
const char *lz4_err = NULL; |
|
5298
|
|
|
|
|
|
|
log_decompressed = ch_lz4_decompress(buf + pos, len - pos, |
|
5299
|
|
|
|
|
|
|
&llen, &comp_consumed, |
|
5300
|
|
|
|
|
|
|
&need_more, &lz4_err); |
|
5301
|
|
|
|
|
|
|
if (!log_decompressed) { |
|
5302
|
|
|
|
|
|
|
if (need_more) return 0; |
|
5303
|
|
|
|
|
|
|
*errmsg = safe_strdup("server log: LZ4 decompression failed"); |
|
5304
|
|
|
|
|
|
|
return -1; |
|
5305
|
|
|
|
|
|
|
} |
|
5306
|
|
|
|
|
|
|
pos += comp_consumed; |
|
5307
|
|
|
|
|
|
|
|
|
5308
|
|
|
|
|
|
|
/* Additional sub-blocks (same logic as SERVER_DATA) */ |
|
5309
|
|
|
|
|
|
|
while (len - pos >= CH_CHECKSUM_SIZE + CH_COMPRESS_HEADER_SIZE |
|
5310
|
|
|
|
|
|
|
&& (uint8_t)buf[pos + CH_CHECKSUM_SIZE] == CH_LZ4_METHOD) { |
|
5311
|
|
|
|
|
|
|
size_t extra_len, extra_consumed; |
|
5312
|
|
|
|
|
|
|
int extra_need_more; |
|
5313
|
|
|
|
|
|
|
const char *extra_err = NULL; |
|
5314
|
|
|
|
|
|
|
char *extra = ch_lz4_decompress(buf + pos, len - pos, |
|
5315
|
|
|
|
|
|
|
&extra_len, &extra_consumed, |
|
5316
|
|
|
|
|
|
|
&extra_need_more, &extra_err); |
|
5317
|
|
|
|
|
|
|
if (!extra) { |
|
5318
|
|
|
|
|
|
|
if (extra_need_more) { |
|
5319
|
|
|
|
|
|
|
Safefree(log_decompressed); |
|
5320
|
|
|
|
|
|
|
return 0; |
|
5321
|
|
|
|
|
|
|
} |
|
5322
|
|
|
|
|
|
|
Safefree(log_decompressed); |
|
5323
|
|
|
|
|
|
|
*errmsg = safe_strdup(extra_err ? extra_err : "server log: LZ4 decompression failed"); |
|
5324
|
|
|
|
|
|
|
return -1; |
|
5325
|
|
|
|
|
|
|
} |
|
5326
|
|
|
|
|
|
|
Renew(log_decompressed, llen + extra_len, char); |
|
5327
|
|
|
|
|
|
|
Copy(extra, log_decompressed + llen, extra_len, char); |
|
5328
|
|
|
|
|
|
|
llen += extra_len; |
|
5329
|
|
|
|
|
|
|
pos += extra_consumed; |
|
5330
|
|
|
|
|
|
|
Safefree(extra); |
|
5331
|
|
|
|
|
|
|
} |
|
5332
|
|
|
|
|
|
|
|
|
5333
|
|
|
|
|
|
|
lbuf = log_decompressed; |
|
5334
|
|
|
|
|
|
|
lpos = 0; |
|
5335
|
|
|
|
|
|
|
} else |
|
5336
|
|
|
|
|
|
|
#endif |
|
5337
|
|
|
|
|
|
|
{ |
|
5338
|
0
|
|
|
|
|
|
lbuf = buf; |
|
5339
|
0
|
|
|
|
|
|
llen = len; |
|
5340
|
0
|
|
|
|
|
|
lpos = pos; |
|
5341
|
|
|
|
|
|
|
} |
|
5342
|
|
|
|
|
|
|
|
|
5343
|
|
|
|
|
|
|
/* block info */ |
|
5344
|
|
|
|
|
|
|
if (CH_CLIENT_REVISION >= DBMS_MIN_REVISION_WITH_BLOCK_INFO) { |
|
5345
|
0
|
|
|
|
|
|
rc = skip_block_info(lbuf, llen, &lpos); |
|
5346
|
0
|
0
|
|
|
|
|
if (rc <= 0) { if (log_decompressed) Safefree(log_decompressed); if (rc < 0) *errmsg = safe_strdup("malformed server log block"); return rc; } |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
5347
|
|
|
|
|
|
|
} |
|
5348
|
|
|
|
|
|
|
uint64_t nc, nr; |
|
5349
|
0
|
|
|
|
|
|
rc = read_varuint(lbuf, llen, &lpos, &nc); |
|
5350
|
0
|
0
|
|
|
|
|
if (rc <= 0) { if (log_decompressed) Safefree(log_decompressed); if (rc < 0) *errmsg = safe_strdup("malformed server log block"); return rc; } |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
5351
|
0
|
|
|
|
|
|
rc = read_varuint(lbuf, llen, &lpos, &nr); |
|
5352
|
0
|
0
|
|
|
|
|
if (rc <= 0) { if (log_decompressed) Safefree(log_decompressed); if (rc < 0) *errmsg = safe_strdup("malformed server log block"); return rc; } |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
5353
|
|
|
|
|
|
|
|
|
5354
|
0
|
0
|
|
|
|
|
if (nc > 0) { |
|
5355
|
|
|
|
|
|
|
uint64_t c; |
|
5356
|
0
|
0
|
|
|
|
|
for (c = 0; c < nc; c++) { |
|
5357
|
|
|
|
|
|
|
const char *ctype; |
|
5358
|
|
|
|
|
|
|
size_t ctype_len; |
|
5359
|
0
|
|
|
|
|
|
rc = skip_native_string(lbuf, llen, &lpos); |
|
5360
|
0
|
0
|
|
|
|
|
if (rc <= 0) { if (log_decompressed) Safefree(log_decompressed); if (rc < 0) *errmsg = safe_strdup("malformed server log block"); return rc; } |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
5361
|
0
|
|
|
|
|
|
rc = read_native_string_ref(lbuf, llen, &lpos, &ctype, &ctype_len); |
|
5362
|
0
|
0
|
|
|
|
|
if (rc <= 0) { if (log_decompressed) Safefree(log_decompressed); if (rc < 0) *errmsg = safe_strdup("malformed server log block"); return rc; } |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
5363
|
|
|
|
|
|
|
/* custom serialization flag (revision >= 54446) */ |
|
5364
|
0
|
0
|
|
|
|
|
if (lpos >= llen) { if (log_decompressed) Safefree(log_decompressed); return 0; } |
|
|
|
0
|
|
|
|
|
|
|
5365
|
0
|
0
|
|
|
|
|
if ((uint8_t)lbuf[lpos]) { if (log_decompressed) Safefree(log_decompressed); *errmsg = safe_strdup("custom serialization not supported"); return -1; } |
|
|
|
0
|
|
|
|
|
|
|
5366
|
0
|
|
|
|
|
|
lpos++; |
|
5367
|
0
|
0
|
|
|
|
|
if (nr > 0) { |
|
5368
|
0
|
|
|
|
|
|
col_type_t *ct = parse_col_type(ctype, ctype_len); |
|
5369
|
0
|
|
|
|
|
|
int log_col_err = 0; |
|
5370
|
0
|
|
|
|
|
|
SV **vals = decode_column(lbuf, llen, &lpos, nr, ct, &log_col_err, 0); |
|
5371
|
0
|
0
|
|
|
|
|
if (!vals) { |
|
5372
|
0
|
|
|
|
|
|
free_col_type(ct); |
|
5373
|
0
|
0
|
|
|
|
|
if (log_col_err || log_decompressed) { |
|
|
|
0
|
|
|
|
|
|
|
5374
|
0
|
0
|
|
|
|
|
if (log_decompressed) Safefree(log_decompressed); |
|
5375
|
0
|
|
|
|
|
|
*errmsg = safe_strdup("malformed server log block"); |
|
5376
|
0
|
|
|
|
|
|
return -1; |
|
5377
|
|
|
|
|
|
|
} |
|
5378
|
0
|
|
|
|
|
|
return 0; |
|
5379
|
|
|
|
|
|
|
} |
|
5380
|
|
|
|
|
|
|
uint64_t j; |
|
5381
|
0
|
0
|
|
|
|
|
for (j = 0; j < nr; j++) SvREFCNT_dec(vals[j]); |
|
5382
|
0
|
|
|
|
|
|
Safefree(vals); |
|
5383
|
0
|
|
|
|
|
|
free_col_type(ct); |
|
5384
|
|
|
|
|
|
|
} |
|
5385
|
|
|
|
|
|
|
} |
|
5386
|
|
|
|
|
|
|
} |
|
5387
|
|
|
|
|
|
|
|
|
5388
|
0
|
0
|
|
|
|
|
if (!log_decompressed) pos = lpos; |
|
5389
|
0
|
0
|
|
|
|
|
if (log_decompressed) Safefree(log_decompressed); |
|
5390
|
|
|
|
|
|
|
|
|
5391
|
0
|
0
|
|
|
|
|
if (pos < self->recv_len) { |
|
5392
|
0
|
|
|
|
|
|
memmove(self->recv_buf, self->recv_buf + pos, |
|
5393
|
0
|
|
|
|
|
|
self->recv_len - pos); |
|
5394
|
|
|
|
|
|
|
} |
|
5395
|
0
|
|
|
|
|
|
self->recv_len -= pos; |
|
5396
|
0
|
|
|
|
|
|
return 1; |
|
5397
|
|
|
|
|
|
|
} |
|
5398
|
|
|
|
|
|
|
|
|
5399
|
0
|
|
|
|
|
|
case SERVER_PROFILE_EVENTS: |
|
5400
|
|
|
|
|
|
|
/* Same structure as SERVER_LOG — data block to discard. |
|
5401
|
|
|
|
|
|
|
* Fall through to SERVER_LOG handler would work, but SERVER_LOG |
|
5402
|
|
|
|
|
|
|
* is above us. Just skip: table_name + rest handled like LOG. */ |
|
5403
|
|
|
|
|
|
|
{ |
|
5404
|
|
|
|
|
|
|
const char *pebuf; |
|
5405
|
|
|
|
|
|
|
size_t pelen, pepos; |
|
5406
|
0
|
|
|
|
|
|
char *pe_decompressed = NULL; |
|
5407
|
|
|
|
|
|
|
|
|
5408
|
0
|
|
|
|
|
|
rc = skip_native_string(buf, len, &pos); |
|
5409
|
0
|
0
|
|
|
|
|
if (rc == 0) return 0; |
|
5410
|
0
|
0
|
|
|
|
|
if (rc < 0) { *errmsg = safe_strdup("malformed profile_events block"); return -1; } |
|
5411
|
|
|
|
|
|
|
|
|
5412
|
|
|
|
|
|
|
#ifdef HAVE_LZ4 |
|
5413
|
|
|
|
|
|
|
if (self->compress) { |
|
5414
|
|
|
|
|
|
|
size_t comp_consumed; |
|
5415
|
|
|
|
|
|
|
int need_more; |
|
5416
|
|
|
|
|
|
|
const char *lz4_err = NULL; |
|
5417
|
|
|
|
|
|
|
pe_decompressed = ch_lz4_decompress(buf + pos, len - pos, |
|
5418
|
|
|
|
|
|
|
&pelen, &comp_consumed, |
|
5419
|
|
|
|
|
|
|
&need_more, &lz4_err); |
|
5420
|
|
|
|
|
|
|
if (!pe_decompressed) { |
|
5421
|
|
|
|
|
|
|
if (need_more) return 0; |
|
5422
|
|
|
|
|
|
|
/* Profile events may be uncompressed — fall back */ |
|
5423
|
|
|
|
|
|
|
goto pe_uncompressed; |
|
5424
|
|
|
|
|
|
|
} |
|
5425
|
|
|
|
|
|
|
pos += comp_consumed; |
|
5426
|
|
|
|
|
|
|
while (len - pos >= CH_CHECKSUM_SIZE + CH_COMPRESS_HEADER_SIZE |
|
5427
|
|
|
|
|
|
|
&& (uint8_t)buf[pos + CH_CHECKSUM_SIZE] == CH_LZ4_METHOD) { |
|
5428
|
|
|
|
|
|
|
size_t extra_len, extra_consumed; |
|
5429
|
|
|
|
|
|
|
int extra_need_more; |
|
5430
|
|
|
|
|
|
|
const char *extra_err = NULL; |
|
5431
|
|
|
|
|
|
|
char *extra = ch_lz4_decompress(buf + pos, len - pos, |
|
5432
|
|
|
|
|
|
|
&extra_len, &extra_consumed, |
|
5433
|
|
|
|
|
|
|
&extra_need_more, &extra_err); |
|
5434
|
|
|
|
|
|
|
if (!extra) { |
|
5435
|
|
|
|
|
|
|
if (extra_need_more) { Safefree(pe_decompressed); return 0; } |
|
5436
|
|
|
|
|
|
|
Safefree(pe_decompressed); |
|
5437
|
|
|
|
|
|
|
*errmsg = safe_strdup("profile_events: LZ4 decompression failed"); |
|
5438
|
|
|
|
|
|
|
return -1; |
|
5439
|
|
|
|
|
|
|
} |
|
5440
|
|
|
|
|
|
|
Renew(pe_decompressed, pelen + extra_len, char); |
|
5441
|
|
|
|
|
|
|
Copy(extra, pe_decompressed + pelen, extra_len, char); |
|
5442
|
|
|
|
|
|
|
pelen += extra_len; |
|
5443
|
|
|
|
|
|
|
pos += extra_consumed; |
|
5444
|
|
|
|
|
|
|
Safefree(extra); |
|
5445
|
|
|
|
|
|
|
} |
|
5446
|
|
|
|
|
|
|
pebuf = pe_decompressed; |
|
5447
|
|
|
|
|
|
|
pepos = 0; |
|
5448
|
|
|
|
|
|
|
} else |
|
5449
|
|
|
|
|
|
|
#endif |
|
5450
|
|
|
|
|
|
|
{ |
|
5451
|
|
|
|
|
|
|
#ifdef HAVE_LZ4 |
|
5452
|
|
|
|
|
|
|
pe_uncompressed: |
|
5453
|
|
|
|
|
|
|
#endif |
|
5454
|
0
|
|
|
|
|
|
pebuf = buf; |
|
5455
|
0
|
|
|
|
|
|
pelen = len; |
|
5456
|
0
|
|
|
|
|
|
pepos = pos; |
|
5457
|
|
|
|
|
|
|
} |
|
5458
|
|
|
|
|
|
|
|
|
5459
|
|
|
|
|
|
|
if (CH_CLIENT_REVISION >= DBMS_MIN_REVISION_WITH_BLOCK_INFO) { |
|
5460
|
0
|
|
|
|
|
|
rc = skip_block_info(pebuf, pelen, &pepos); |
|
5461
|
0
|
0
|
|
|
|
|
if (rc <= 0) { if (pe_decompressed) Safefree(pe_decompressed); if (rc < 0) *errmsg = safe_strdup("malformed profile_events block"); return rc; } |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
5462
|
|
|
|
|
|
|
} |
|
5463
|
|
|
|
|
|
|
uint64_t pe_nc, pe_nr; |
|
5464
|
0
|
|
|
|
|
|
rc = read_varuint(pebuf, pelen, &pepos, &pe_nc); |
|
5465
|
0
|
0
|
|
|
|
|
if (rc <= 0) { if (pe_decompressed) Safefree(pe_decompressed); if (rc < 0) *errmsg = safe_strdup("malformed profile_events block"); return rc; } |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
5466
|
0
|
|
|
|
|
|
rc = read_varuint(pebuf, pelen, &pepos, &pe_nr); |
|
5467
|
0
|
0
|
|
|
|
|
if (rc <= 0) { if (pe_decompressed) Safefree(pe_decompressed); if (rc < 0) *errmsg = safe_strdup("malformed profile_events block"); return rc; } |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
5468
|
|
|
|
|
|
|
|
|
5469
|
0
|
0
|
|
|
|
|
if (pe_nc > 0) { |
|
5470
|
|
|
|
|
|
|
uint64_t c; |
|
5471
|
0
|
0
|
|
|
|
|
for (c = 0; c < pe_nc; c++) { |
|
5472
|
|
|
|
|
|
|
const char *ctype; |
|
5473
|
|
|
|
|
|
|
size_t ctype_len; |
|
5474
|
0
|
|
|
|
|
|
rc = skip_native_string(pebuf, pelen, &pepos); |
|
5475
|
0
|
0
|
|
|
|
|
if (rc <= 0) { if (pe_decompressed) Safefree(pe_decompressed); if (rc < 0) *errmsg = safe_strdup("malformed profile_events block"); return rc; } |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
5476
|
0
|
|
|
|
|
|
rc = read_native_string_ref(pebuf, pelen, &pepos, &ctype, &ctype_len); |
|
5477
|
0
|
0
|
|
|
|
|
if (rc <= 0) { if (pe_decompressed) Safefree(pe_decompressed); if (rc < 0) *errmsg = safe_strdup("malformed profile_events block"); return rc; } |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
5478
|
|
|
|
|
|
|
/* custom serialization flag */ |
|
5479
|
0
|
0
|
|
|
|
|
if (pepos >= pelen) { if (pe_decompressed) Safefree(pe_decompressed); return 0; } |
|
|
|
0
|
|
|
|
|
|
|
5480
|
0
|
0
|
|
|
|
|
if ((uint8_t)pebuf[pepos]) { if (pe_decompressed) Safefree(pe_decompressed); *errmsg = safe_strdup("custom serialization not supported"); return -1; } |
|
|
|
0
|
|
|
|
|
|
|
5481
|
0
|
|
|
|
|
|
pepos++; |
|
5482
|
0
|
0
|
|
|
|
|
if (pe_nr > 0) { |
|
5483
|
0
|
|
|
|
|
|
col_type_t *ct = parse_col_type(ctype, ctype_len); |
|
5484
|
0
|
|
|
|
|
|
int pe_col_err = 0; |
|
5485
|
0
|
|
|
|
|
|
SV **vals = decode_column(pebuf, pelen, &pepos, pe_nr, ct, &pe_col_err, 0); |
|
5486
|
0
|
0
|
|
|
|
|
if (!vals) { |
|
5487
|
0
|
|
|
|
|
|
free_col_type(ct); |
|
5488
|
0
|
0
|
|
|
|
|
if (pe_col_err || pe_decompressed) { |
|
|
|
0
|
|
|
|
|
|
|
5489
|
0
|
0
|
|
|
|
|
if (pe_decompressed) Safefree(pe_decompressed); |
|
5490
|
0
|
|
|
|
|
|
*errmsg = safe_strdup("malformed profile_events block"); |
|
5491
|
0
|
|
|
|
|
|
return -1; |
|
5492
|
|
|
|
|
|
|
} |
|
5493
|
0
|
|
|
|
|
|
return 0; |
|
5494
|
|
|
|
|
|
|
} |
|
5495
|
|
|
|
|
|
|
uint64_t j; |
|
5496
|
0
|
0
|
|
|
|
|
for (j = 0; j < pe_nr; j++) SvREFCNT_dec(vals[j]); |
|
5497
|
0
|
|
|
|
|
|
Safefree(vals); |
|
5498
|
0
|
|
|
|
|
|
free_col_type(ct); |
|
5499
|
|
|
|
|
|
|
} |
|
5500
|
|
|
|
|
|
|
} |
|
5501
|
|
|
|
|
|
|
} |
|
5502
|
|
|
|
|
|
|
|
|
5503
|
0
|
0
|
|
|
|
|
if (!pe_decompressed) pos = pepos; |
|
5504
|
0
|
0
|
|
|
|
|
if (pe_decompressed) Safefree(pe_decompressed); |
|
5505
|
|
|
|
|
|
|
|
|
5506
|
0
|
0
|
|
|
|
|
if (pos < self->recv_len) { |
|
5507
|
0
|
|
|
|
|
|
memmove(self->recv_buf, self->recv_buf + pos, |
|
5508
|
0
|
|
|
|
|
|
self->recv_len - pos); |
|
5509
|
|
|
|
|
|
|
} |
|
5510
|
0
|
|
|
|
|
|
self->recv_len -= pos; |
|
5511
|
0
|
|
|
|
|
|
return 1; |
|
5512
|
|
|
|
|
|
|
} |
|
5513
|
|
|
|
|
|
|
|
|
5514
|
0
|
|
|
|
|
|
case SERVER_PONG: |
|
5515
|
0
|
0
|
|
|
|
|
if (pos < self->recv_len) { |
|
5516
|
0
|
|
|
|
|
|
memmove(self->recv_buf, self->recv_buf + pos, |
|
5517
|
0
|
|
|
|
|
|
self->recv_len - pos); |
|
5518
|
|
|
|
|
|
|
} |
|
5519
|
0
|
|
|
|
|
|
self->recv_len -= pos; |
|
5520
|
0
|
|
|
|
|
|
return 3; |
|
5521
|
|
|
|
|
|
|
|
|
5522
|
0
|
|
|
|
|
|
case SERVER_END_OF_STREAM: |
|
5523
|
0
|
0
|
|
|
|
|
if (pos < self->recv_len) { |
|
5524
|
0
|
|
|
|
|
|
memmove(self->recv_buf, self->recv_buf + pos, |
|
5525
|
0
|
|
|
|
|
|
self->recv_len - pos); |
|
5526
|
|
|
|
|
|
|
} |
|
5527
|
0
|
|
|
|
|
|
self->recv_len -= pos; |
|
5528
|
0
|
|
|
|
|
|
return 2; |
|
5529
|
|
|
|
|
|
|
|
|
5530
|
0
|
|
|
|
|
|
default: { |
|
5531
|
|
|
|
|
|
|
/* Unknown packet type */ |
|
5532
|
|
|
|
|
|
|
char err[64]; |
|
5533
|
0
|
|
|
|
|
|
snprintf(err, sizeof(err), "unknown server packet type: %llu", |
|
5534
|
|
|
|
|
|
|
(unsigned long long)ptype); |
|
5535
|
0
|
|
|
|
|
|
*errmsg = safe_strdup(err); |
|
5536
|
0
|
|
|
|
|
|
self->recv_len = 0; |
|
5537
|
0
|
|
|
|
|
|
return -1; |
|
5538
|
|
|
|
|
|
|
} |
|
5539
|
|
|
|
|
|
|
} |
|
5540
|
|
|
|
|
|
|
} |
|
5541
|
|
|
|
|
|
|
|
|
5542
|
|
|
|
|
|
|
/* |
|
5543
|
|
|
|
|
|
|
* Process native protocol responses from recv_buf. |
|
5544
|
|
|
|
|
|
|
* Called from on_readable when protocol == PROTO_NATIVE. |
|
5545
|
|
|
|
|
|
|
*/ |
|
5546
|
0
|
|
|
|
|
|
static void process_native_response(ev_clickhouse_t *self) { |
|
5547
|
0
|
0
|
|
|
|
|
while (self->recv_len > 0 && self->magic == EV_CH_MAGIC) { |
|
|
|
0
|
|
|
|
|
|
|
5548
|
0
|
|
|
|
|
|
char *errmsg = NULL; |
|
5549
|
|
|
|
|
|
|
int rc; |
|
5550
|
0
|
|
|
|
|
|
rc = parse_native_packet(self, &errmsg); |
|
5551
|
|
|
|
|
|
|
|
|
5552
|
0
|
0
|
|
|
|
|
if (rc == 0) { |
|
5553
|
|
|
|
|
|
|
/* need more data */ |
|
5554
|
0
|
|
|
|
|
|
return; |
|
5555
|
|
|
|
|
|
|
} |
|
5556
|
|
|
|
|
|
|
|
|
5557
|
0
|
0
|
|
|
|
|
if (rc == -2) { |
|
5558
|
|
|
|
|
|
|
/* object destroyed inside callback */ |
|
5559
|
0
|
|
|
|
|
|
return; |
|
5560
|
|
|
|
|
|
|
} |
|
5561
|
|
|
|
|
|
|
|
|
5562
|
0
|
0
|
|
|
|
|
if (rc == 4) { |
|
5563
|
|
|
|
|
|
|
/* ServerHello received — send addendum (revision >= 54458) */ |
|
5564
|
0
|
0
|
|
|
|
|
if (self->native_state == NATIVE_WAIT_HELLO) { |
|
5565
|
|
|
|
|
|
|
/* Addendum: quota_key (only if server supports it) */ |
|
5566
|
0
|
0
|
|
|
|
|
if (self->server_revision >= DBMS_MIN_PROTOCOL_VERSION_WITH_ADDENDUM) { |
|
5567
|
|
|
|
|
|
|
native_buf_t ab; |
|
5568
|
0
|
|
|
|
|
|
nbuf_init(&ab); |
|
5569
|
0
|
|
|
|
|
|
nbuf_cstring(&ab, ""); /* quota_key */ |
|
5570
|
0
|
|
|
|
|
|
ensure_send_cap(self, ab.len); |
|
5571
|
0
|
|
|
|
|
|
Copy(ab.data, self->send_buf, ab.len, char); |
|
5572
|
0
|
|
|
|
|
|
self->send_len = ab.len; |
|
5573
|
0
|
|
|
|
|
|
self->send_pos = 0; |
|
5574
|
0
|
|
|
|
|
|
Safefree(ab.data); |
|
5575
|
0
|
0
|
|
|
|
|
if (try_write(self)) return; |
|
5576
|
|
|
|
|
|
|
} |
|
5577
|
0
|
|
|
|
|
|
self->native_state = NATIVE_IDLE; |
|
5578
|
0
|
|
|
|
|
|
self->connected = 1; |
|
5579
|
|
|
|
|
|
|
|
|
5580
|
|
|
|
|
|
|
/* fire on_connect */ |
|
5581
|
0
|
0
|
|
|
|
|
if (NULL != self->on_connect) { |
|
5582
|
0
|
|
|
|
|
|
self->callback_depth++; |
|
5583
|
|
|
|
|
|
|
{ |
|
5584
|
0
|
|
|
|
|
|
dSP; |
|
5585
|
0
|
|
|
|
|
|
ENTER; |
|
5586
|
0
|
|
|
|
|
|
SAVETMPS; |
|
5587
|
0
|
0
|
|
|
|
|
PUSHMARK(SP); |
|
5588
|
0
|
|
|
|
|
|
PUTBACK; |
|
5589
|
0
|
|
|
|
|
|
call_sv(self->on_connect, G_DISCARD | G_EVAL); |
|
5590
|
0
|
0
|
|
|
|
|
if (SvTRUE(ERRSV)) { |
|
|
|
0
|
|
|
|
|
|
|
5591
|
0
|
0
|
|
|
|
|
warn("EV::ClickHouse: exception in connect handler: %s", |
|
5592
|
|
|
|
|
|
|
SvPV_nolen(ERRSV)); |
|
5593
|
|
|
|
|
|
|
} |
|
5594
|
0
|
0
|
|
|
|
|
FREETMPS; |
|
5595
|
0
|
|
|
|
|
|
LEAVE; |
|
5596
|
|
|
|
|
|
|
} |
|
5597
|
0
|
|
|
|
|
|
self->callback_depth--; |
|
5598
|
0
|
0
|
|
|
|
|
if (check_destroyed(self)) return; |
|
5599
|
|
|
|
|
|
|
} |
|
5600
|
|
|
|
|
|
|
/* start pipeline if queries were queued during connect */ |
|
5601
|
0
|
0
|
|
|
|
|
if (!ngx_queue_empty(&self->send_queue)) |
|
5602
|
0
|
|
|
|
|
|
pipeline_advance(self); |
|
5603
|
|
|
|
|
|
|
} |
|
5604
|
|
|
|
|
|
|
/* pipeline_advance -> try_write may free self; no data |
|
5605
|
|
|
|
|
|
|
* in recv_buf for the just-dispatched request yet */ |
|
5606
|
0
|
|
|
|
|
|
return; |
|
5607
|
|
|
|
|
|
|
} |
|
5608
|
|
|
|
|
|
|
|
|
5609
|
0
|
0
|
|
|
|
|
if (rc == -1) { |
|
5610
|
|
|
|
|
|
|
/* error */ |
|
5611
|
0
|
0
|
|
|
|
|
if (self->native_state == NATIVE_WAIT_HELLO) { |
|
5612
|
|
|
|
|
|
|
/* Hello failed — connection-level error */ |
|
5613
|
0
|
|
|
|
|
|
self->callback_depth++; |
|
5614
|
0
|
|
|
|
|
|
emit_error(self, errmsg); |
|
5615
|
0
|
|
|
|
|
|
self->callback_depth--; |
|
5616
|
0
|
|
|
|
|
|
Safefree(errmsg); |
|
5617
|
0
|
0
|
|
|
|
|
if (check_destroyed(self)) return; |
|
5618
|
0
|
0
|
|
|
|
|
if (cancel_pending(self, "connection failed")) return; |
|
5619
|
0
|
|
|
|
|
|
cleanup_connection(self); |
|
5620
|
0
|
|
|
|
|
|
return; |
|
5621
|
|
|
|
|
|
|
} |
|
5622
|
|
|
|
|
|
|
|
|
5623
|
|
|
|
|
|
|
/* Stop query timeout timer */ |
|
5624
|
0
|
0
|
|
|
|
|
if (self->timing) { |
|
5625
|
0
|
|
|
|
|
|
ev_timer_stop(self->loop, &self->timer); |
|
5626
|
0
|
|
|
|
|
|
self->timing = 0; |
|
5627
|
|
|
|
|
|
|
} |
|
5628
|
|
|
|
|
|
|
|
|
5629
|
|
|
|
|
|
|
/* Query error — deliver to callback */ |
|
5630
|
0
|
0
|
|
|
|
|
if (self->native_rows) { |
|
5631
|
0
|
|
|
|
|
|
SvREFCNT_dec((SV*)self->native_rows); |
|
5632
|
0
|
|
|
|
|
|
self->native_rows = NULL; |
|
5633
|
|
|
|
|
|
|
} |
|
5634
|
0
|
0
|
|
|
|
|
if (self->insert_data) { |
|
5635
|
0
|
|
|
|
|
|
Safefree(self->insert_data); |
|
5636
|
0
|
|
|
|
|
|
self->insert_data = NULL; |
|
5637
|
0
|
|
|
|
|
|
self->insert_data_len = 0; |
|
5638
|
|
|
|
|
|
|
} |
|
5639
|
0
|
0
|
|
|
|
|
if (self->insert_av) { |
|
5640
|
0
|
|
|
|
|
|
SvREFCNT_dec(self->insert_av); |
|
5641
|
0
|
|
|
|
|
|
self->insert_av = NULL; |
|
5642
|
|
|
|
|
|
|
} |
|
5643
|
0
|
0
|
|
|
|
|
if (self->insert_err) { |
|
5644
|
0
|
|
|
|
|
|
Safefree(self->insert_err); |
|
5645
|
0
|
|
|
|
|
|
self->insert_err = NULL; |
|
5646
|
|
|
|
|
|
|
} |
|
5647
|
0
|
|
|
|
|
|
self->native_state = NATIVE_IDLE; |
|
5648
|
0
|
|
|
|
|
|
self->recv_len = 0; /* flush malformed data */ |
|
5649
|
0
|
0
|
|
|
|
|
if (self->send_count > 0) self->send_count--; |
|
5650
|
0
|
|
|
|
|
|
lc_free_dicts(self); |
|
5651
|
0
|
|
|
|
|
|
int destroyed = deliver_error(self, errmsg); |
|
5652
|
0
|
|
|
|
|
|
Safefree(errmsg); |
|
5653
|
0
|
0
|
|
|
|
|
if (destroyed) return; |
|
5654
|
|
|
|
|
|
|
|
|
5655
|
|
|
|
|
|
|
/* advance pipeline — may free self via try_write error */ |
|
5656
|
0
|
|
|
|
|
|
pipeline_advance(self); |
|
5657
|
0
|
|
|
|
|
|
return; |
|
5658
|
|
|
|
|
|
|
} |
|
5659
|
|
|
|
|
|
|
|
|
5660
|
0
|
0
|
|
|
|
|
if (rc == 2) { |
|
5661
|
|
|
|
|
|
|
/* EndOfStream — deliver accumulated rows or deferred error */ |
|
5662
|
0
|
0
|
|
|
|
|
if (self->timing) { |
|
5663
|
0
|
|
|
|
|
|
ev_timer_stop(self->loop, &self->timer); |
|
5664
|
0
|
|
|
|
|
|
self->timing = 0; |
|
5665
|
|
|
|
|
|
|
} |
|
5666
|
0
|
|
|
|
|
|
self->native_state = NATIVE_IDLE; |
|
5667
|
0
|
0
|
|
|
|
|
if (self->send_count > 0) self->send_count--; |
|
5668
|
0
|
|
|
|
|
|
lc_free_dicts(self); |
|
5669
|
|
|
|
|
|
|
|
|
5670
|
0
|
0
|
|
|
|
|
if (self->insert_err) { |
|
5671
|
0
|
|
|
|
|
|
char *err = self->insert_err; |
|
5672
|
0
|
|
|
|
|
|
self->insert_err = NULL; |
|
5673
|
0
|
0
|
|
|
|
|
if (self->native_rows) { |
|
5674
|
0
|
|
|
|
|
|
SvREFCNT_dec((SV*)self->native_rows); |
|
5675
|
0
|
|
|
|
|
|
self->native_rows = NULL; |
|
5676
|
|
|
|
|
|
|
} |
|
5677
|
0
|
|
|
|
|
|
int destroyed = deliver_error(self, err); |
|
5678
|
0
|
|
|
|
|
|
Safefree(err); |
|
5679
|
0
|
0
|
|
|
|
|
if (destroyed) return; |
|
5680
|
|
|
|
|
|
|
} else { |
|
5681
|
0
|
|
|
|
|
|
AV *rows = self->native_rows; |
|
5682
|
0
|
|
|
|
|
|
self->native_rows = NULL; |
|
5683
|
0
|
0
|
|
|
|
|
if (deliver_rows(self, rows)) return; |
|
5684
|
|
|
|
|
|
|
} |
|
5685
|
|
|
|
|
|
|
|
|
5686
|
|
|
|
|
|
|
/* advance pipeline — may free self via try_write error */ |
|
5687
|
0
|
|
|
|
|
|
pipeline_advance(self); |
|
5688
|
0
|
|
|
|
|
|
return; |
|
5689
|
|
|
|
|
|
|
} |
|
5690
|
|
|
|
|
|
|
|
|
5691
|
0
|
0
|
|
|
|
|
if (rc == 3) { |
|
5692
|
|
|
|
|
|
|
/* Pong — deliver success to callback */ |
|
5693
|
0
|
0
|
|
|
|
|
if (self->timing) { |
|
5694
|
0
|
|
|
|
|
|
ev_timer_stop(self->loop, &self->timer); |
|
5695
|
0
|
|
|
|
|
|
self->timing = 0; |
|
5696
|
|
|
|
|
|
|
} |
|
5697
|
0
|
|
|
|
|
|
self->native_state = NATIVE_IDLE; |
|
5698
|
0
|
0
|
|
|
|
|
if (self->send_count > 0) self->send_count--; |
|
5699
|
0
|
|
|
|
|
|
AV *rows = newAV(); |
|
5700
|
0
|
0
|
|
|
|
|
if (deliver_rows(self, rows)) return; |
|
5701
|
0
|
|
|
|
|
|
pipeline_advance(self); |
|
5702
|
0
|
|
|
|
|
|
return; |
|
5703
|
|
|
|
|
|
|
} |
|
5704
|
|
|
|
|
|
|
|
|
5705
|
|
|
|
|
|
|
/* rc == 1: Data/Progress/ProfileInfo — continue reading */ |
|
5706
|
|
|
|
|
|
|
} |
|
5707
|
|
|
|
|
|
|
} |
|
5708
|
|
|
|
|
|
|
|
|
5709
|
|
|
|
|
|
|
/* --- Async TCP connect --- */ |
|
5710
|
|
|
|
|
|
|
|
|
5711
|
0
|
|
|
|
|
|
static void start_connect(ev_clickhouse_t *self) { |
|
5712
|
0
|
|
|
|
|
|
struct addrinfo hints, *res = NULL; |
|
5713
|
|
|
|
|
|
|
int fd, ret; |
|
5714
|
|
|
|
|
|
|
char port_str[16]; |
|
5715
|
|
|
|
|
|
|
|
|
5716
|
0
|
|
|
|
|
|
emit_trace(self, "connect %s:%u (%s)", |
|
5717
|
|
|
|
|
|
|
self->host, self->port, |
|
5718
|
0
|
0
|
|
|
|
|
self->protocol == PROTO_NATIVE ? "native" : "http"); |
|
5719
|
0
|
|
|
|
|
|
snprintf(port_str, sizeof(port_str), "%u", self->port); |
|
5720
|
|
|
|
|
|
|
|
|
5721
|
0
|
|
|
|
|
|
Zero(&hints, 1, struct addrinfo); |
|
5722
|
0
|
|
|
|
|
|
hints.ai_family = AF_UNSPEC; |
|
5723
|
0
|
|
|
|
|
|
hints.ai_socktype = SOCK_STREAM; |
|
5724
|
|
|
|
|
|
|
|
|
5725
|
0
|
|
|
|
|
|
ret = getaddrinfo(self->host, port_str, &hints, &res); |
|
5726
|
0
|
0
|
|
|
|
|
if (ret != 0) { |
|
5727
|
|
|
|
|
|
|
char errbuf[256]; |
|
5728
|
0
|
|
|
|
|
|
snprintf(errbuf, sizeof(errbuf), "getaddrinfo: %s", gai_strerror(ret)); |
|
5729
|
0
|
|
|
|
|
|
self->callback_depth++; |
|
5730
|
0
|
|
|
|
|
|
emit_error(self, errbuf); |
|
5731
|
0
|
|
|
|
|
|
self->callback_depth--; |
|
5732
|
0
|
0
|
|
|
|
|
if (check_destroyed(self)) return; |
|
5733
|
0
|
0
|
|
|
|
|
if (cancel_pending(self, errbuf)) return; |
|
5734
|
0
|
|
|
|
|
|
cleanup_connection(self); |
|
5735
|
0
|
|
|
|
|
|
return; |
|
5736
|
|
|
|
|
|
|
} |
|
5737
|
|
|
|
|
|
|
|
|
5738
|
0
|
|
|
|
|
|
fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); |
|
5739
|
0
|
0
|
|
|
|
|
if (fd < 0) { |
|
5740
|
0
|
|
|
|
|
|
freeaddrinfo(res); |
|
5741
|
0
|
|
|
|
|
|
self->callback_depth++; |
|
5742
|
0
|
|
|
|
|
|
emit_error(self, "socket() failed"); |
|
5743
|
0
|
|
|
|
|
|
self->callback_depth--; |
|
5744
|
0
|
0
|
|
|
|
|
if (check_destroyed(self)) return; |
|
5745
|
0
|
0
|
|
|
|
|
if (cancel_pending(self, "socket() failed")) return; |
|
5746
|
0
|
|
|
|
|
|
cleanup_connection(self); |
|
5747
|
0
|
|
|
|
|
|
return; |
|
5748
|
|
|
|
|
|
|
} |
|
5749
|
|
|
|
|
|
|
|
|
5750
|
|
|
|
|
|
|
/* non-blocking */ |
|
5751
|
|
|
|
|
|
|
{ |
|
5752
|
0
|
|
|
|
|
|
int fl = fcntl(fd, F_GETFL); |
|
5753
|
0
|
0
|
|
|
|
|
if (fl < 0 || fcntl(fd, F_SETFL, fl | O_NONBLOCK) < 0) { |
|
|
|
0
|
|
|
|
|
|
|
5754
|
0
|
|
|
|
|
|
freeaddrinfo(res); |
|
5755
|
0
|
|
|
|
|
|
close(fd); |
|
5756
|
0
|
|
|
|
|
|
self->callback_depth++; |
|
5757
|
0
|
|
|
|
|
|
emit_error(self, "fcntl O_NONBLOCK failed"); |
|
5758
|
0
|
|
|
|
|
|
self->callback_depth--; |
|
5759
|
0
|
0
|
|
|
|
|
if (check_destroyed(self)) return; |
|
5760
|
0
|
0
|
|
|
|
|
if (cancel_pending(self, "fcntl O_NONBLOCK failed")) return; |
|
5761
|
0
|
|
|
|
|
|
cleanup_connection(self); |
|
5762
|
0
|
|
|
|
|
|
return; |
|
5763
|
|
|
|
|
|
|
} |
|
5764
|
|
|
|
|
|
|
} |
|
5765
|
|
|
|
|
|
|
|
|
5766
|
|
|
|
|
|
|
/* TCP_NODELAY */ |
|
5767
|
|
|
|
|
|
|
{ |
|
5768
|
0
|
|
|
|
|
|
int one = 1; |
|
5769
|
0
|
|
|
|
|
|
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)); |
|
5770
|
|
|
|
|
|
|
} |
|
5771
|
|
|
|
|
|
|
|
|
5772
|
0
|
|
|
|
|
|
self->fd = fd; |
|
5773
|
0
|
|
|
|
|
|
self->connecting = 1; |
|
5774
|
|
|
|
|
|
|
|
|
5775
|
0
|
|
|
|
|
|
ret = connect(fd, res->ai_addr, res->ai_addrlen); |
|
5776
|
0
|
|
|
|
|
|
freeaddrinfo(res); |
|
5777
|
|
|
|
|
|
|
|
|
5778
|
0
|
0
|
|
|
|
|
if (ret == 0) { |
|
5779
|
|
|
|
|
|
|
/* connected immediately — connected=1 is deferred for native |
|
5780
|
|
|
|
|
|
|
* (until ServerHello) and TLS (until handshake completes) */ |
|
5781
|
0
|
|
|
|
|
|
self->connecting = 0; |
|
5782
|
0
|
0
|
|
|
|
|
if (self->protocol != PROTO_NATIVE && !self->tls_enabled) |
|
|
|
0
|
|
|
|
|
|
|
5783
|
0
|
|
|
|
|
|
self->connected = 1; |
|
5784
|
0
|
|
|
|
|
|
ev_io_init(&self->rio, io_cb, self->fd, EV_READ); |
|
5785
|
0
|
|
|
|
|
|
self->rio.data = (void *)self; |
|
5786
|
0
|
|
|
|
|
|
ev_io_init(&self->wio, io_cb, self->fd, EV_WRITE); |
|
5787
|
0
|
|
|
|
|
|
self->wio.data = (void *)self; |
|
5788
|
0
|
|
|
|
|
|
on_connect_done(self); |
|
5789
|
0
|
|
|
|
|
|
return; |
|
5790
|
|
|
|
|
|
|
} |
|
5791
|
|
|
|
|
|
|
|
|
5792
|
0
|
0
|
|
|
|
|
if (errno != EINPROGRESS) { |
|
5793
|
|
|
|
|
|
|
char errbuf[256]; |
|
5794
|
0
|
|
|
|
|
|
snprintf(errbuf, sizeof(errbuf), "connect: %s", strerror(errno)); |
|
5795
|
0
|
|
|
|
|
|
close(fd); |
|
5796
|
0
|
|
|
|
|
|
self->fd = -1; |
|
5797
|
0
|
|
|
|
|
|
self->connecting = 0; |
|
5798
|
0
|
|
|
|
|
|
self->callback_depth++; |
|
5799
|
0
|
|
|
|
|
|
emit_error(self, errbuf); |
|
5800
|
0
|
|
|
|
|
|
self->callback_depth--; |
|
5801
|
0
|
0
|
|
|
|
|
if (check_destroyed(self)) return; |
|
5802
|
0
|
0
|
|
|
|
|
if (cancel_pending(self, errbuf)) return; |
|
5803
|
0
|
|
|
|
|
|
cleanup_connection(self); |
|
5804
|
0
|
|
|
|
|
|
return; |
|
5805
|
|
|
|
|
|
|
} |
|
5806
|
|
|
|
|
|
|
|
|
5807
|
|
|
|
|
|
|
/* in progress — wait for writability */ |
|
5808
|
0
|
|
|
|
|
|
ev_io_init(&self->rio, io_cb, self->fd, EV_READ); |
|
5809
|
0
|
|
|
|
|
|
self->rio.data = (void *)self; |
|
5810
|
0
|
|
|
|
|
|
ev_io_init(&self->wio, io_cb, self->fd, EV_WRITE); |
|
5811
|
0
|
|
|
|
|
|
self->wio.data = (void *)self; |
|
5812
|
|
|
|
|
|
|
|
|
5813
|
0
|
|
|
|
|
|
start_writing(self); |
|
5814
|
|
|
|
|
|
|
|
|
5815
|
0
|
0
|
|
|
|
|
if (self->connect_timeout > 0) { |
|
5816
|
0
|
|
|
|
|
|
ev_timer_set(&self->timer, (ev_tstamp)self->connect_timeout, 0.0); |
|
5817
|
0
|
|
|
|
|
|
ev_timer_start(self->loop, &self->timer); |
|
5818
|
0
|
|
|
|
|
|
self->timing = 1; |
|
5819
|
|
|
|
|
|
|
} |
|
5820
|
|
|
|
|
|
|
} |
|
5821
|
|
|
|
|
|
|
|
|
5822
|
0
|
|
|
|
|
|
static void on_connect_done(ev_clickhouse_t *self) { |
|
5823
|
0
|
|
|
|
|
|
self->connecting = 0; |
|
5824
|
0
|
|
|
|
|
|
self->reconnect_attempts = 0; |
|
5825
|
|
|
|
|
|
|
|
|
5826
|
0
|
|
|
|
|
|
stop_writing(self); |
|
5827
|
0
|
0
|
|
|
|
|
if (self->timing) { |
|
5828
|
0
|
|
|
|
|
|
ev_timer_stop(self->loop, &self->timer); |
|
5829
|
0
|
|
|
|
|
|
self->timing = 0; |
|
5830
|
|
|
|
|
|
|
} |
|
5831
|
|
|
|
|
|
|
|
|
5832
|
|
|
|
|
|
|
#ifdef HAVE_OPENSSL |
|
5833
|
0
|
0
|
|
|
|
|
if (self->tls_enabled) { |
|
5834
|
|
|
|
|
|
|
int ret; |
|
5835
|
0
|
|
|
|
|
|
self->ssl_ctx = SSL_CTX_new(TLS_client_method()); |
|
5836
|
0
|
0
|
|
|
|
|
if (!self->ssl_ctx) { |
|
5837
|
0
|
|
|
|
|
|
self->callback_depth++; |
|
5838
|
0
|
|
|
|
|
|
emit_error(self, "SSL_CTX_new failed"); |
|
5839
|
0
|
|
|
|
|
|
self->callback_depth--; |
|
5840
|
0
|
0
|
|
|
|
|
if (check_destroyed(self)) return; |
|
5841
|
0
|
0
|
|
|
|
|
if (cancel_pending(self, "SSL_CTX_new failed")) return; |
|
5842
|
0
|
|
|
|
|
|
cleanup_connection(self); |
|
5843
|
0
|
|
|
|
|
|
return; |
|
5844
|
|
|
|
|
|
|
} |
|
5845
|
0
|
|
|
|
|
|
SSL_CTX_set_default_verify_paths(self->ssl_ctx); |
|
5846
|
0
|
0
|
|
|
|
|
if (self->tls_skip_verify) |
|
5847
|
0
|
|
|
|
|
|
SSL_CTX_set_verify(self->ssl_ctx, SSL_VERIFY_NONE, NULL); |
|
5848
|
|
|
|
|
|
|
else |
|
5849
|
0
|
|
|
|
|
|
SSL_CTX_set_verify(self->ssl_ctx, SSL_VERIFY_PEER, NULL); |
|
5850
|
0
|
0
|
|
|
|
|
if (self->tls_ca_file) { |
|
5851
|
0
|
0
|
|
|
|
|
if (SSL_CTX_load_verify_locations(self->ssl_ctx, self->tls_ca_file, NULL) != 1) { |
|
5852
|
0
|
|
|
|
|
|
self->callback_depth++; |
|
5853
|
0
|
|
|
|
|
|
emit_error(self, "SSL_CTX_load_verify_locations failed"); |
|
5854
|
0
|
|
|
|
|
|
self->callback_depth--; |
|
5855
|
0
|
0
|
|
|
|
|
if (check_destroyed(self)) return; |
|
5856
|
0
|
0
|
|
|
|
|
if (cancel_pending(self, "SSL_CTX_load_verify_locations failed")) return; |
|
5857
|
0
|
|
|
|
|
|
cleanup_connection(self); |
|
5858
|
0
|
|
|
|
|
|
return; |
|
5859
|
|
|
|
|
|
|
} |
|
5860
|
|
|
|
|
|
|
} |
|
5861
|
0
|
|
|
|
|
|
self->ssl = SSL_new(self->ssl_ctx); |
|
5862
|
0
|
0
|
|
|
|
|
if (!self->ssl) { |
|
5863
|
0
|
|
|
|
|
|
self->callback_depth++; |
|
5864
|
0
|
|
|
|
|
|
emit_error(self, "SSL_new failed"); |
|
5865
|
0
|
|
|
|
|
|
self->callback_depth--; |
|
5866
|
0
|
0
|
|
|
|
|
if (check_destroyed(self)) return; |
|
5867
|
0
|
0
|
|
|
|
|
if (cancel_pending(self, "SSL_new failed")) return; |
|
5868
|
0
|
|
|
|
|
|
cleanup_connection(self); |
|
5869
|
0
|
|
|
|
|
|
return; |
|
5870
|
|
|
|
|
|
|
} |
|
5871
|
0
|
|
|
|
|
|
SSL_set_fd(self->ssl, self->fd); |
|
5872
|
|
|
|
|
|
|
|
|
5873
|
|
|
|
|
|
|
/* SNI must not be sent for IP address literals (RFC 6066 s3) */ |
|
5874
|
0
|
0
|
|
|
|
|
if (!is_ip_literal(self->host)) |
|
5875
|
0
|
|
|
|
|
|
SSL_set_tlsext_host_name(self->ssl, self->host); |
|
5876
|
|
|
|
|
|
|
|
|
5877
|
|
|
|
|
|
|
/* Verify server certificate matches hostname or IP */ |
|
5878
|
0
|
0
|
|
|
|
|
if (!self->tls_skip_verify) { |
|
5879
|
0
|
|
|
|
|
|
X509_VERIFY_PARAM *param = SSL_get0_param(self->ssl); |
|
5880
|
0
|
|
|
|
|
|
X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); |
|
5881
|
0
|
0
|
|
|
|
|
if (is_ip_literal(self->host)) |
|
5882
|
0
|
|
|
|
|
|
X509_VERIFY_PARAM_set1_ip_asc(param, self->host); |
|
5883
|
|
|
|
|
|
|
else |
|
5884
|
0
|
|
|
|
|
|
X509_VERIFY_PARAM_set1_host(param, self->host, 0); |
|
5885
|
|
|
|
|
|
|
} |
|
5886
|
|
|
|
|
|
|
|
|
5887
|
0
|
|
|
|
|
|
ret = SSL_connect(self->ssl); |
|
5888
|
0
|
0
|
|
|
|
|
if (ret == 1) { |
|
5889
|
|
|
|
|
|
|
/* handshake done immediately */ |
|
5890
|
0
|
|
|
|
|
|
goto handshake_done; |
|
5891
|
|
|
|
|
|
|
} else { |
|
5892
|
0
|
|
|
|
|
|
int err = SSL_get_error(self->ssl, ret); |
|
5893
|
0
|
0
|
|
|
|
|
if (err == SSL_ERROR_WANT_READ) { |
|
5894
|
0
|
|
|
|
|
|
start_reading(self); |
|
5895
|
0
|
0
|
|
|
|
|
} else if (err == SSL_ERROR_WANT_WRITE) { |
|
5896
|
0
|
|
|
|
|
|
start_writing(self); |
|
5897
|
|
|
|
|
|
|
} else { |
|
5898
|
0
|
|
|
|
|
|
self->callback_depth++; |
|
5899
|
0
|
|
|
|
|
|
emit_error(self, "SSL_connect failed"); |
|
5900
|
0
|
|
|
|
|
|
self->callback_depth--; |
|
5901
|
0
|
0
|
|
|
|
|
if (check_destroyed(self)) return; |
|
5902
|
0
|
0
|
|
|
|
|
if (cancel_pending(self, "SSL_connect failed")) return; |
|
5903
|
0
|
|
|
|
|
|
cleanup_connection(self); |
|
5904
|
0
|
|
|
|
|
|
return; |
|
5905
|
|
|
|
|
|
|
} |
|
5906
|
|
|
|
|
|
|
/* continue TLS handshake in io_cb */ |
|
5907
|
0
|
|
|
|
|
|
return; |
|
5908
|
|
|
|
|
|
|
} |
|
5909
|
|
|
|
|
|
|
} |
|
5910
|
0
|
|
|
|
|
|
handshake_done: |
|
5911
|
|
|
|
|
|
|
#endif |
|
5912
|
|
|
|
|
|
|
|
|
5913
|
0
|
0
|
|
|
|
|
if (self->protocol == PROTO_NATIVE) { |
|
5914
|
|
|
|
|
|
|
/* Send ClientHello and wait for ServerHello */ |
|
5915
|
|
|
|
|
|
|
size_t hello_len; |
|
5916
|
0
|
|
|
|
|
|
char *hello = build_native_hello(self, &hello_len); |
|
5917
|
0
|
|
|
|
|
|
ensure_send_cap(self, hello_len); |
|
5918
|
0
|
|
|
|
|
|
Copy(hello, self->send_buf, hello_len, char); |
|
5919
|
0
|
|
|
|
|
|
self->send_len = hello_len; |
|
5920
|
0
|
|
|
|
|
|
self->send_pos = 0; |
|
5921
|
0
|
|
|
|
|
|
Safefree(hello); |
|
5922
|
|
|
|
|
|
|
|
|
5923
|
0
|
|
|
|
|
|
self->native_state = NATIVE_WAIT_HELLO; |
|
5924
|
0
|
|
|
|
|
|
start_writing(self); |
|
5925
|
0
|
|
|
|
|
|
return; |
|
5926
|
|
|
|
|
|
|
} |
|
5927
|
|
|
|
|
|
|
|
|
5928
|
|
|
|
|
|
|
/* HTTP protocol: connection is ready */ |
|
5929
|
0
|
|
|
|
|
|
self->connected = 1; |
|
5930
|
|
|
|
|
|
|
|
|
5931
|
0
|
0
|
|
|
|
|
if (NULL != self->on_connect) { |
|
5932
|
0
|
|
|
|
|
|
self->callback_depth++; |
|
5933
|
|
|
|
|
|
|
{ |
|
5934
|
0
|
|
|
|
|
|
dSP; |
|
5935
|
0
|
|
|
|
|
|
ENTER; |
|
5936
|
0
|
|
|
|
|
|
SAVETMPS; |
|
5937
|
0
|
0
|
|
|
|
|
PUSHMARK(SP); |
|
5938
|
0
|
|
|
|
|
|
PUTBACK; |
|
5939
|
0
|
|
|
|
|
|
call_sv(self->on_connect, G_DISCARD | G_EVAL); |
|
5940
|
0
|
0
|
|
|
|
|
if (SvTRUE(ERRSV)) { |
|
|
|
0
|
|
|
|
|
|
|
5941
|
0
|
0
|
|
|
|
|
warn("EV::ClickHouse: exception in connect handler: %s", SvPV_nolen(ERRSV)); |
|
5942
|
|
|
|
|
|
|
} |
|
5943
|
0
|
0
|
|
|
|
|
FREETMPS; |
|
5944
|
0
|
|
|
|
|
|
LEAVE; |
|
5945
|
|
|
|
|
|
|
} |
|
5946
|
0
|
|
|
|
|
|
self->callback_depth--; |
|
5947
|
0
|
0
|
|
|
|
|
if (check_destroyed(self)) return; |
|
5948
|
|
|
|
|
|
|
} |
|
5949
|
|
|
|
|
|
|
|
|
5950
|
|
|
|
|
|
|
/* start pipeline if queries were queued during connect */ |
|
5951
|
0
|
0
|
|
|
|
|
if (!ngx_queue_empty(&self->send_queue)) |
|
5952
|
0
|
|
|
|
|
|
pipeline_advance(self); |
|
5953
|
|
|
|
|
|
|
} |
|
5954
|
|
|
|
|
|
|
|
|
5955
|
|
|
|
|
|
|
/* --- I/O callbacks --- */ |
|
5956
|
|
|
|
|
|
|
|
|
5957
|
|
|
|
|
|
|
/* Returns 1 if self was freed (caller must not access self). */ |
|
5958
|
0
|
|
|
|
|
|
static int try_write(ev_clickhouse_t *self) { |
|
5959
|
0
|
0
|
|
|
|
|
while (self->send_pos < self->send_len) { |
|
5960
|
0
|
|
|
|
|
|
ssize_t n = ch_write(self, self->send_buf + self->send_pos, |
|
5961
|
0
|
|
|
|
|
|
self->send_len - self->send_pos); |
|
5962
|
0
|
0
|
|
|
|
|
if (n < 0) { |
|
5963
|
0
|
0
|
|
|
|
|
if (errno == EAGAIN || errno == EWOULDBLOCK) { |
|
|
|
0
|
|
|
|
|
|
|
5964
|
0
|
|
|
|
|
|
start_writing(self); |
|
5965
|
0
|
|
|
|
|
|
return 0; |
|
5966
|
|
|
|
|
|
|
} |
|
5967
|
|
|
|
|
|
|
/* write error */ |
|
5968
|
0
|
|
|
|
|
|
self->callback_depth++; |
|
5969
|
0
|
|
|
|
|
|
emit_error(self, strerror(errno)); |
|
5970
|
0
|
|
|
|
|
|
self->callback_depth--; |
|
5971
|
0
|
0
|
|
|
|
|
if (check_destroyed(self)) return 1; |
|
5972
|
0
|
0
|
|
|
|
|
if (cancel_pending(self, "write error")) return 1; |
|
5973
|
0
|
|
|
|
|
|
cleanup_connection(self); |
|
5974
|
0
|
|
|
|
|
|
return 0; |
|
5975
|
|
|
|
|
|
|
} |
|
5976
|
0
|
0
|
|
|
|
|
if (n == 0) { |
|
5977
|
0
|
|
|
|
|
|
self->callback_depth++; |
|
5978
|
0
|
|
|
|
|
|
emit_error(self, "connection closed during write"); |
|
5979
|
0
|
|
|
|
|
|
self->callback_depth--; |
|
5980
|
0
|
0
|
|
|
|
|
if (check_destroyed(self)) return 1; |
|
5981
|
0
|
0
|
|
|
|
|
if (cancel_pending(self, "connection closed")) return 1; |
|
5982
|
0
|
|
|
|
|
|
cleanup_connection(self); |
|
5983
|
0
|
|
|
|
|
|
return 0; |
|
5984
|
|
|
|
|
|
|
} |
|
5985
|
0
|
|
|
|
|
|
self->send_pos += n; |
|
5986
|
|
|
|
|
|
|
} |
|
5987
|
|
|
|
|
|
|
|
|
5988
|
|
|
|
|
|
|
/* all sent */ |
|
5989
|
0
|
|
|
|
|
|
stop_writing(self); |
|
5990
|
0
|
|
|
|
|
|
self->send_len = 0; |
|
5991
|
0
|
|
|
|
|
|
self->send_pos = 0; |
|
5992
|
|
|
|
|
|
|
|
|
5993
|
|
|
|
|
|
|
/* start reading responses */ |
|
5994
|
0
|
|
|
|
|
|
start_reading(self); |
|
5995
|
|
|
|
|
|
|
|
|
5996
|
|
|
|
|
|
|
/* check if more to send */ |
|
5997
|
0
|
0
|
|
|
|
|
if (!ngx_queue_empty(&self->send_queue)) |
|
5998
|
0
|
|
|
|
|
|
return pipeline_advance(self); |
|
5999
|
0
|
|
|
|
|
|
return 0; |
|
6000
|
|
|
|
|
|
|
} |
|
6001
|
|
|
|
|
|
|
|
|
6002
|
0
|
|
|
|
|
|
static void on_readable(ev_clickhouse_t *self) { |
|
6003
|
|
|
|
|
|
|
ssize_t n; |
|
6004
|
|
|
|
|
|
|
|
|
6005
|
0
|
|
|
|
|
|
ensure_recv_cap(self, self->recv_len + 4096); |
|
6006
|
0
|
|
|
|
|
|
n = ch_read(self, self->recv_buf + self->recv_len, |
|
6007
|
0
|
|
|
|
|
|
self->recv_cap - self->recv_len); |
|
6008
|
|
|
|
|
|
|
|
|
6009
|
0
|
0
|
|
|
|
|
if (n < 0) { |
|
6010
|
0
|
0
|
|
|
|
|
if (errno == EAGAIN || errno == EWOULDBLOCK) return; |
|
|
|
0
|
|
|
|
|
|
|
6011
|
0
|
|
|
|
|
|
self->callback_depth++; |
|
6012
|
0
|
|
|
|
|
|
emit_error(self, strerror(errno)); |
|
6013
|
0
|
|
|
|
|
|
self->callback_depth--; |
|
6014
|
0
|
0
|
|
|
|
|
if (check_destroyed(self)) return; |
|
6015
|
0
|
0
|
|
|
|
|
if (cancel_pending(self, "read error")) return; |
|
6016
|
0
|
|
|
|
|
|
cleanup_connection(self); |
|
6017
|
0
|
|
|
|
|
|
return; |
|
6018
|
|
|
|
|
|
|
} |
|
6019
|
|
|
|
|
|
|
|
|
6020
|
0
|
0
|
|
|
|
|
if (n == 0) { |
|
6021
|
|
|
|
|
|
|
/* connection closed — fire on_error and drain pending if we |
|
6022
|
|
|
|
|
|
|
* have an in-flight request or haven't finished handshake */ |
|
6023
|
0
|
0
|
|
|
|
|
int had_inflight = (self->send_count > 0 || !self->connected); |
|
|
|
0
|
|
|
|
|
|
|
6024
|
0
|
|
|
|
|
|
int has_queued = !ngx_queue_empty(&self->send_queue); |
|
6025
|
|
|
|
|
|
|
|
|
6026
|
0
|
0
|
|
|
|
|
if (had_inflight) { |
|
6027
|
0
|
|
|
|
|
|
self->callback_depth++; |
|
6028
|
0
|
|
|
|
|
|
emit_error(self, "connection closed by server"); |
|
6029
|
0
|
|
|
|
|
|
self->callback_depth--; |
|
6030
|
0
|
0
|
|
|
|
|
if (check_destroyed(self)) return; |
|
6031
|
|
|
|
|
|
|
/* Only cancel in-flight cb_queue (irrecoverable). |
|
6032
|
|
|
|
|
|
|
* Keep send_queue if auto_reconnect — those haven't been sent yet. */ |
|
6033
|
0
|
0
|
|
|
|
|
if (!self->auto_reconnect || !has_queued) { |
|
|
|
0
|
|
|
|
|
|
|
6034
|
0
|
0
|
|
|
|
|
if (cancel_pending(self, "connection closed")) return; |
|
6035
|
|
|
|
|
|
|
} else { |
|
6036
|
|
|
|
|
|
|
/* Cancel only the in-flight cb_queue entries */ |
|
6037
|
0
|
0
|
|
|
|
|
while (!ngx_queue_empty(&self->cb_queue)) { |
|
6038
|
0
|
|
|
|
|
|
SV *cb = pop_cb(self); |
|
6039
|
0
|
0
|
|
|
|
|
if (cb == NULL) break; |
|
6040
|
0
|
|
|
|
|
|
self->callback_depth++; |
|
6041
|
|
|
|
|
|
|
{ |
|
6042
|
0
|
|
|
|
|
|
dSP; |
|
6043
|
0
|
|
|
|
|
|
ENTER; SAVETMPS; |
|
6044
|
0
|
0
|
|
|
|
|
PUSHMARK(SP); |
|
6045
|
0
|
|
|
|
|
|
PUSHs(&PL_sv_undef); |
|
6046
|
0
|
|
|
|
|
|
PUSHs(sv_2mortal(newSVpv("connection closed", 0))); |
|
6047
|
0
|
|
|
|
|
|
PUTBACK; |
|
6048
|
0
|
|
|
|
|
|
invoke_cb(cb); |
|
6049
|
0
|
0
|
|
|
|
|
FREETMPS; LEAVE; |
|
6050
|
|
|
|
|
|
|
} |
|
6051
|
0
|
|
|
|
|
|
self->callback_depth--; |
|
6052
|
0
|
0
|
|
|
|
|
if (check_destroyed(self)) return; |
|
6053
|
|
|
|
|
|
|
} |
|
6054
|
0
|
|
|
|
|
|
self->send_count = 0; |
|
6055
|
|
|
|
|
|
|
} |
|
6056
|
|
|
|
|
|
|
} |
|
6057
|
0
|
|
|
|
|
|
cleanup_connection(self); |
|
6058
|
|
|
|
|
|
|
|
|
6059
|
|
|
|
|
|
|
/* Auto-reconnect if we have queued requests or flag is set */ |
|
6060
|
0
|
0
|
|
|
|
|
if (self->auto_reconnect && self->host && self->magic == EV_CH_MAGIC) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
6061
|
0
|
|
|
|
|
|
schedule_reconnect(self); |
|
6062
|
|
|
|
|
|
|
} |
|
6063
|
0
|
|
|
|
|
|
return; |
|
6064
|
|
|
|
|
|
|
} |
|
6065
|
|
|
|
|
|
|
|
|
6066
|
0
|
|
|
|
|
|
self->recv_len += n; |
|
6067
|
|
|
|
|
|
|
|
|
6068
|
0
|
0
|
|
|
|
|
if (self->protocol == PROTO_HTTP) { |
|
6069
|
0
|
|
|
|
|
|
process_http_response(self); |
|
6070
|
|
|
|
|
|
|
} else { |
|
6071
|
0
|
|
|
|
|
|
process_native_response(self); |
|
6072
|
|
|
|
|
|
|
} |
|
6073
|
|
|
|
|
|
|
} |
|
6074
|
|
|
|
|
|
|
|
|
6075
|
0
|
|
|
|
|
|
static void io_cb(EV_P_ ev_io *w, int revents) { |
|
6076
|
0
|
|
|
|
|
|
ev_clickhouse_t *self = (ev_clickhouse_t *)w->data; |
|
6077
|
|
|
|
|
|
|
(void)loop; |
|
6078
|
|
|
|
|
|
|
|
|
6079
|
0
|
0
|
|
|
|
|
if (self == NULL || self->magic != EV_CH_MAGIC) return; |
|
|
|
0
|
|
|
|
|
|
|
6080
|
|
|
|
|
|
|
|
|
6081
|
0
|
0
|
|
|
|
|
if (self->connecting) { |
|
6082
|
|
|
|
|
|
|
/* check connect result */ |
|
6083
|
0
|
|
|
|
|
|
int err = 0; |
|
6084
|
0
|
|
|
|
|
|
socklen_t errlen = sizeof(err); |
|
6085
|
|
|
|
|
|
|
|
|
6086
|
0
|
0
|
|
|
|
|
if (self->timing) { |
|
6087
|
0
|
|
|
|
|
|
ev_timer_stop(self->loop, &self->timer); |
|
6088
|
0
|
|
|
|
|
|
self->timing = 0; |
|
6089
|
|
|
|
|
|
|
} |
|
6090
|
0
|
|
|
|
|
|
stop_writing(self); |
|
6091
|
|
|
|
|
|
|
|
|
6092
|
0
|
0
|
|
|
|
|
if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) < 0) |
|
6093
|
0
|
|
|
|
|
|
err = errno; |
|
6094
|
0
|
0
|
|
|
|
|
if (err != 0) { |
|
6095
|
|
|
|
|
|
|
char errbuf[256]; |
|
6096
|
0
|
|
|
|
|
|
snprintf(errbuf, sizeof(errbuf), "connect: %s", strerror(err)); |
|
6097
|
0
|
|
|
|
|
|
self->callback_depth++; |
|
6098
|
0
|
|
|
|
|
|
emit_error(self, errbuf); |
|
6099
|
0
|
|
|
|
|
|
self->callback_depth--; |
|
6100
|
0
|
0
|
|
|
|
|
if (check_destroyed(self)) return; |
|
6101
|
0
|
0
|
|
|
|
|
if (cancel_pending(self, errbuf)) return; |
|
6102
|
0
|
|
|
|
|
|
cleanup_connection(self); |
|
6103
|
0
|
|
|
|
|
|
return; |
|
6104
|
|
|
|
|
|
|
} |
|
6105
|
|
|
|
|
|
|
|
|
6106
|
0
|
|
|
|
|
|
on_connect_done(self); |
|
6107
|
0
|
|
|
|
|
|
return; |
|
6108
|
|
|
|
|
|
|
} |
|
6109
|
|
|
|
|
|
|
|
|
6110
|
|
|
|
|
|
|
#ifdef HAVE_OPENSSL |
|
6111
|
0
|
0
|
|
|
|
|
if (self->ssl && !self->connected && self->native_state != NATIVE_WAIT_HELLO |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
6112
|
0
|
0
|
|
|
|
|
&& self->native_state != NATIVE_WAIT_RESULT |
|
6113
|
0
|
0
|
|
|
|
|
&& self->native_state != NATIVE_WAIT_INSERT_META) { |
|
6114
|
|
|
|
|
|
|
/* TLS handshake in progress */ |
|
6115
|
0
|
|
|
|
|
|
int ret = SSL_connect(self->ssl); |
|
6116
|
0
|
0
|
|
|
|
|
if (ret == 1) { |
|
6117
|
0
|
|
|
|
|
|
stop_reading(self); |
|
6118
|
0
|
|
|
|
|
|
stop_writing(self); |
|
6119
|
|
|
|
|
|
|
|
|
6120
|
0
|
0
|
|
|
|
|
if (self->protocol == PROTO_NATIVE) { |
|
6121
|
|
|
|
|
|
|
/* Send ClientHello over TLS, then wait for ServerHello */ |
|
6122
|
|
|
|
|
|
|
size_t hello_len; |
|
6123
|
0
|
|
|
|
|
|
char *hello = build_native_hello(self, &hello_len); |
|
6124
|
0
|
|
|
|
|
|
ensure_send_cap(self, hello_len); |
|
6125
|
0
|
|
|
|
|
|
Copy(hello, self->send_buf, hello_len, char); |
|
6126
|
0
|
|
|
|
|
|
self->send_len = hello_len; |
|
6127
|
0
|
|
|
|
|
|
self->send_pos = 0; |
|
6128
|
0
|
|
|
|
|
|
Safefree(hello); |
|
6129
|
0
|
|
|
|
|
|
self->native_state = NATIVE_WAIT_HELLO; |
|
6130
|
0
|
|
|
|
|
|
start_writing(self); |
|
6131
|
0
|
|
|
|
|
|
return; |
|
6132
|
|
|
|
|
|
|
} |
|
6133
|
|
|
|
|
|
|
|
|
6134
|
|
|
|
|
|
|
/* HTTP protocol: fire on_connect */ |
|
6135
|
0
|
|
|
|
|
|
self->connected = 1; |
|
6136
|
0
|
0
|
|
|
|
|
if (NULL != self->on_connect) { |
|
6137
|
0
|
|
|
|
|
|
self->callback_depth++; |
|
6138
|
|
|
|
|
|
|
{ |
|
6139
|
0
|
|
|
|
|
|
dSP; |
|
6140
|
0
|
|
|
|
|
|
ENTER; |
|
6141
|
0
|
|
|
|
|
|
SAVETMPS; |
|
6142
|
0
|
0
|
|
|
|
|
PUSHMARK(SP); |
|
6143
|
0
|
|
|
|
|
|
PUTBACK; |
|
6144
|
0
|
|
|
|
|
|
call_sv(self->on_connect, G_DISCARD | G_EVAL); |
|
6145
|
0
|
0
|
|
|
|
|
if (SvTRUE(ERRSV)) { |
|
|
|
0
|
|
|
|
|
|
|
6146
|
0
|
0
|
|
|
|
|
warn("EV::ClickHouse: exception in connect handler: %s", SvPV_nolen(ERRSV)); |
|
6147
|
|
|
|
|
|
|
} |
|
6148
|
0
|
0
|
|
|
|
|
FREETMPS; |
|
6149
|
0
|
|
|
|
|
|
LEAVE; |
|
6150
|
|
|
|
|
|
|
} |
|
6151
|
0
|
|
|
|
|
|
self->callback_depth--; |
|
6152
|
0
|
0
|
|
|
|
|
if (check_destroyed(self)) return; |
|
6153
|
|
|
|
|
|
|
} |
|
6154
|
0
|
0
|
|
|
|
|
if (!ngx_queue_empty(&self->send_queue)) |
|
6155
|
0
|
|
|
|
|
|
pipeline_advance(self); |
|
6156
|
0
|
|
|
|
|
|
return; |
|
6157
|
|
|
|
|
|
|
} else { |
|
6158
|
0
|
|
|
|
|
|
int err = SSL_get_error(self->ssl, ret); |
|
6159
|
0
|
|
|
|
|
|
stop_reading(self); |
|
6160
|
0
|
|
|
|
|
|
stop_writing(self); |
|
6161
|
0
|
0
|
|
|
|
|
if (err == SSL_ERROR_WANT_READ) { |
|
6162
|
0
|
|
|
|
|
|
start_reading(self); |
|
6163
|
0
|
0
|
|
|
|
|
} else if (err == SSL_ERROR_WANT_WRITE) { |
|
6164
|
0
|
|
|
|
|
|
start_writing(self); |
|
6165
|
|
|
|
|
|
|
} else { |
|
6166
|
0
|
|
|
|
|
|
self->callback_depth++; |
|
6167
|
0
|
|
|
|
|
|
emit_error(self, "SSL handshake failed"); |
|
6168
|
0
|
|
|
|
|
|
self->callback_depth--; |
|
6169
|
0
|
0
|
|
|
|
|
if (check_destroyed(self)) return; |
|
6170
|
0
|
0
|
|
|
|
|
if (cancel_pending(self, "SSL handshake failed")) return; |
|
6171
|
0
|
|
|
|
|
|
cleanup_connection(self); |
|
6172
|
|
|
|
|
|
|
} |
|
6173
|
0
|
|
|
|
|
|
return; |
|
6174
|
|
|
|
|
|
|
} |
|
6175
|
|
|
|
|
|
|
} |
|
6176
|
|
|
|
|
|
|
#endif |
|
6177
|
|
|
|
|
|
|
|
|
6178
|
0
|
0
|
|
|
|
|
if (revents & EV_WRITE) { |
|
6179
|
0
|
0
|
|
|
|
|
if (try_write(self)) return; |
|
6180
|
0
|
0
|
|
|
|
|
if (self->fd < 0) return; |
|
6181
|
|
|
|
|
|
|
} |
|
6182
|
|
|
|
|
|
|
|
|
6183
|
0
|
0
|
|
|
|
|
if (revents & EV_READ) { |
|
6184
|
0
|
|
|
|
|
|
on_readable(self); |
|
6185
|
|
|
|
|
|
|
} |
|
6186
|
|
|
|
|
|
|
} |
|
6187
|
|
|
|
|
|
|
|
|
6188
|
0
|
|
|
|
|
|
static void timer_cb(EV_P_ ev_timer *w, int revents) { |
|
6189
|
0
|
|
|
|
|
|
ev_clickhouse_t *self = (ev_clickhouse_t *)w->data; |
|
6190
|
|
|
|
|
|
|
(void)loop; |
|
6191
|
|
|
|
|
|
|
(void)revents; |
|
6192
|
|
|
|
|
|
|
|
|
6193
|
0
|
0
|
|
|
|
|
if (self == NULL || self->magic != EV_CH_MAGIC) return; |
|
|
|
0
|
|
|
|
|
|
|
6194
|
|
|
|
|
|
|
|
|
6195
|
0
|
|
|
|
|
|
self->timing = 0; |
|
6196
|
|
|
|
|
|
|
|
|
6197
|
0
|
0
|
|
|
|
|
if (self->connecting) { |
|
6198
|
0
|
|
|
|
|
|
stop_writing(self); |
|
6199
|
0
|
|
|
|
|
|
self->callback_depth++; |
|
6200
|
0
|
|
|
|
|
|
emit_error(self, "connect timeout"); |
|
6201
|
0
|
|
|
|
|
|
self->callback_depth--; |
|
6202
|
0
|
0
|
|
|
|
|
if (check_destroyed(self)) return; |
|
6203
|
0
|
0
|
|
|
|
|
if (cancel_pending(self, "connect timeout")) return; |
|
6204
|
0
|
|
|
|
|
|
cleanup_connection(self); |
|
6205
|
|
|
|
|
|
|
} else { |
|
6206
|
|
|
|
|
|
|
/* query timeout */ |
|
6207
|
0
|
0
|
|
|
|
|
if (self->native_rows) { |
|
6208
|
0
|
|
|
|
|
|
SvREFCNT_dec((SV*)self->native_rows); |
|
6209
|
0
|
|
|
|
|
|
self->native_rows = NULL; |
|
6210
|
|
|
|
|
|
|
} |
|
6211
|
0
|
0
|
|
|
|
|
if (self->native_col_names) { |
|
6212
|
0
|
|
|
|
|
|
SvREFCNT_dec((SV*)self->native_col_names); |
|
6213
|
0
|
|
|
|
|
|
self->native_col_names = NULL; |
|
6214
|
|
|
|
|
|
|
} |
|
6215
|
0
|
0
|
|
|
|
|
if (self->native_col_types) { |
|
6216
|
0
|
|
|
|
|
|
SvREFCNT_dec((SV*)self->native_col_types); |
|
6217
|
0
|
|
|
|
|
|
self->native_col_types = NULL; |
|
6218
|
|
|
|
|
|
|
} |
|
6219
|
0
|
|
|
|
|
|
lc_free_dicts(self); |
|
6220
|
0
|
0
|
|
|
|
|
if (self->insert_data) { |
|
6221
|
0
|
|
|
|
|
|
Safefree(self->insert_data); |
|
6222
|
0
|
|
|
|
|
|
self->insert_data = NULL; |
|
6223
|
0
|
|
|
|
|
|
self->insert_data_len = 0; |
|
6224
|
|
|
|
|
|
|
} |
|
6225
|
0
|
0
|
|
|
|
|
if (self->insert_av) { |
|
6226
|
0
|
|
|
|
|
|
SvREFCNT_dec(self->insert_av); |
|
6227
|
0
|
|
|
|
|
|
self->insert_av = NULL; |
|
6228
|
|
|
|
|
|
|
} |
|
6229
|
0
|
0
|
|
|
|
|
if (self->insert_err) { |
|
6230
|
0
|
|
|
|
|
|
Safefree(self->insert_err); |
|
6231
|
0
|
|
|
|
|
|
self->insert_err = NULL; |
|
6232
|
|
|
|
|
|
|
} |
|
6233
|
0
|
|
|
|
|
|
self->native_state = NATIVE_IDLE; |
|
6234
|
0
|
0
|
|
|
|
|
if (self->send_count > 0) self->send_count--; |
|
6235
|
|
|
|
|
|
|
|
|
6236
|
0
|
0
|
|
|
|
|
if (deliver_error(self, "query timeout")) return; |
|
6237
|
|
|
|
|
|
|
|
|
6238
|
|
|
|
|
|
|
/* Must reconnect — server may still be processing */ |
|
6239
|
0
|
0
|
|
|
|
|
if (cancel_pending(self, "query timeout")) return; |
|
6240
|
0
|
|
|
|
|
|
cleanup_connection(self); |
|
6241
|
0
|
0
|
|
|
|
|
if (self->auto_reconnect && self->host) |
|
|
|
0
|
|
|
|
|
|
|
6242
|
0
|
|
|
|
|
|
schedule_reconnect(self); |
|
6243
|
|
|
|
|
|
|
} |
|
6244
|
|
|
|
|
|
|
} |
|
6245
|
|
|
|
|
|
|
|
|
6246
|
|
|
|
|
|
|
/* --- Keepalive timer callback --- */ |
|
6247
|
|
|
|
|
|
|
|
|
6248
|
0
|
|
|
|
|
|
static void ka_timer_cb(EV_P_ ev_timer *w, int revents) { |
|
6249
|
0
|
|
|
|
|
|
ev_clickhouse_t *self = (ev_clickhouse_t *)((char *)w - |
|
6250
|
|
|
|
|
|
|
offsetof(ev_clickhouse_t, ka_timer)); |
|
6251
|
|
|
|
|
|
|
(void)revents; |
|
6252
|
|
|
|
|
|
|
|
|
6253
|
0
|
0
|
|
|
|
|
if (self->magic != EV_CH_MAGIC) return; |
|
6254
|
0
|
0
|
|
|
|
|
if (!self->connected || self->send_count > 0) return; |
|
|
|
0
|
|
|
|
|
|
|
6255
|
|
|
|
|
|
|
|
|
6256
|
|
|
|
|
|
|
/* Send a ping to keep the connection alive */ |
|
6257
|
0
|
0
|
|
|
|
|
if (self->protocol == PROTO_NATIVE) { |
|
6258
|
|
|
|
|
|
|
native_buf_t pkt; |
|
6259
|
0
|
|
|
|
|
|
nbuf_init(&pkt); |
|
6260
|
0
|
|
|
|
|
|
nbuf_varuint(&pkt, CLIENT_PING); |
|
6261
|
0
|
|
|
|
|
|
ensure_send_cap(self, self->send_len + pkt.len); |
|
6262
|
0
|
|
|
|
|
|
Copy(pkt.data, self->send_buf + self->send_len, pkt.len, char); |
|
6263
|
0
|
|
|
|
|
|
self->send_len += pkt.len; |
|
6264
|
0
|
|
|
|
|
|
Safefree(pkt.data); |
|
6265
|
0
|
0
|
|
|
|
|
if (!self->writing) start_writing(self); |
|
6266
|
|
|
|
|
|
|
} |
|
6267
|
|
|
|
|
|
|
/* HTTP: no-op ping — just rely on TCP keepalive or let the |
|
6268
|
|
|
|
|
|
|
* connection drop and auto-reconnect handles it. */ |
|
6269
|
|
|
|
|
|
|
} |
|
6270
|
|
|
|
|
|
|
|
|
6271
|
0
|
|
|
|
|
|
static void start_keepalive(ev_clickhouse_t *self) { |
|
6272
|
0
|
0
|
|
|
|
|
if (self->keepalive > 0 && !self->ka_timing && self->connected) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
6273
|
0
|
|
|
|
|
|
ev_timer_init(&self->ka_timer, ka_timer_cb, self->keepalive, self->keepalive); |
|
6274
|
0
|
|
|
|
|
|
ev_timer_start(self->loop, &self->ka_timer); |
|
6275
|
0
|
|
|
|
|
|
self->ka_timing = 1; |
|
6276
|
|
|
|
|
|
|
} |
|
6277
|
0
|
|
|
|
|
|
} |
|
6278
|
|
|
|
|
|
|
|
|
6279
|
0
|
|
|
|
|
|
static void stop_keepalive(ev_clickhouse_t *self) { |
|
6280
|
0
|
0
|
|
|
|
|
if (self->ka_timing) { |
|
6281
|
0
|
|
|
|
|
|
ev_timer_stop(self->loop, &self->ka_timer); |
|
6282
|
0
|
|
|
|
|
|
self->ka_timing = 0; |
|
6283
|
|
|
|
|
|
|
} |
|
6284
|
0
|
|
|
|
|
|
} |
|
6285
|
|
|
|
|
|
|
|
|
6286
|
|
|
|
|
|
|
/* --- Reconnect with backoff --- */ |
|
6287
|
|
|
|
|
|
|
|
|
6288
|
0
|
|
|
|
|
|
static void reconnect_timer_cb(EV_P_ ev_timer *w, int revents) { |
|
6289
|
0
|
|
|
|
|
|
ev_clickhouse_t *self = (ev_clickhouse_t *)((char *)w - |
|
6290
|
|
|
|
|
|
|
offsetof(ev_clickhouse_t, reconnect_timer)); |
|
6291
|
|
|
|
|
|
|
(void)revents; (void)loop; |
|
6292
|
0
|
|
|
|
|
|
self->reconnect_timing = 0; |
|
6293
|
0
|
0
|
|
|
|
|
if (self->magic != EV_CH_MAGIC || self->connected || self->connecting) return; |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
6294
|
0
|
|
|
|
|
|
start_connect(self); |
|
6295
|
|
|
|
|
|
|
} |
|
6296
|
|
|
|
|
|
|
|
|
6297
|
0
|
|
|
|
|
|
static void schedule_reconnect(ev_clickhouse_t *self) { |
|
6298
|
0
|
0
|
|
|
|
|
if (!self->auto_reconnect || !self->host || self->magic != EV_CH_MAGIC) return; |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
6299
|
0
|
0
|
|
|
|
|
if (self->reconnect_delay <= 0) { |
|
6300
|
0
|
|
|
|
|
|
self->reconnect_attempts = 0; |
|
6301
|
0
|
|
|
|
|
|
start_connect(self); |
|
6302
|
0
|
|
|
|
|
|
return; |
|
6303
|
|
|
|
|
|
|
} |
|
6304
|
0
|
|
|
|
|
|
double delay = self->reconnect_delay; |
|
6305
|
|
|
|
|
|
|
int i; |
|
6306
|
0
|
0
|
|
|
|
|
for (i = 0; i < self->reconnect_attempts && i < 20; i++) |
|
|
|
0
|
|
|
|
|
|
|
6307
|
0
|
|
|
|
|
|
delay *= 2; |
|
6308
|
0
|
0
|
|
|
|
|
if (self->reconnect_max_delay > 0 && delay > self->reconnect_max_delay) |
|
|
|
0
|
|
|
|
|
|
|
6309
|
0
|
|
|
|
|
|
delay = self->reconnect_max_delay; |
|
6310
|
0
|
|
|
|
|
|
self->reconnect_attempts++; |
|
6311
|
0
|
0
|
|
|
|
|
if (self->reconnect_timing) { |
|
6312
|
0
|
|
|
|
|
|
ev_timer_stop(self->loop, &self->reconnect_timer); |
|
6313
|
0
|
|
|
|
|
|
self->reconnect_timing = 0; |
|
6314
|
|
|
|
|
|
|
} |
|
6315
|
0
|
|
|
|
|
|
ev_timer_init(&self->reconnect_timer, reconnect_timer_cb, delay, 0); |
|
6316
|
0
|
|
|
|
|
|
ev_timer_start(self->loop, &self->reconnect_timer); |
|
6317
|
0
|
|
|
|
|
|
self->reconnect_timing = 1; |
|
6318
|
|
|
|
|
|
|
} |
|
6319
|
|
|
|
|
|
|
|
|
6320
|
|
|
|
|
|
|
/* Free LowCardinality cross-block dictionary state */ |
|
6321
|
0
|
|
|
|
|
|
static void lc_free_dicts(ev_clickhouse_t *self) { |
|
6322
|
0
|
0
|
|
|
|
|
if (self->lc_dicts) { |
|
6323
|
|
|
|
|
|
|
int c; |
|
6324
|
0
|
0
|
|
|
|
|
for (c = 0; c < self->lc_num_cols; c++) { |
|
6325
|
0
|
0
|
|
|
|
|
if (self->lc_dicts[c]) { |
|
6326
|
|
|
|
|
|
|
uint64_t j; |
|
6327
|
0
|
0
|
|
|
|
|
for (j = 0; j < self->lc_dict_sizes[c]; j++) |
|
6328
|
0
|
|
|
|
|
|
SvREFCNT_dec(self->lc_dicts[c][j]); |
|
6329
|
0
|
|
|
|
|
|
Safefree(self->lc_dicts[c]); |
|
6330
|
|
|
|
|
|
|
} |
|
6331
|
|
|
|
|
|
|
} |
|
6332
|
0
|
|
|
|
|
|
Safefree(self->lc_dicts); |
|
6333
|
0
|
|
|
|
|
|
Safefree(self->lc_dict_sizes); |
|
6334
|
0
|
|
|
|
|
|
self->lc_dicts = NULL; |
|
6335
|
0
|
|
|
|
|
|
self->lc_dict_sizes = NULL; |
|
6336
|
0
|
|
|
|
|
|
self->lc_num_cols = 0; |
|
6337
|
|
|
|
|
|
|
} |
|
6338
|
0
|
|
|
|
|
|
} |
|
6339
|
|
|
|
|
|
|
|
|
6340
|
|
|
|
|
|
|
/* --- Pipeline orchestrator --- */ |
|
6341
|
|
|
|
|
|
|
|
|
6342
|
|
|
|
|
|
|
/* |
|
6343
|
|
|
|
|
|
|
* ClickHouse HTTP does not support true HTTP pipelining. |
|
6344
|
|
|
|
|
|
|
* We send one request at a time, wait for the response, then send the next. |
|
6345
|
|
|
|
|
|
|
*/ |
|
6346
|
|
|
|
|
|
|
/* Returns 1 if self was freed (caller must not access self). */ |
|
6347
|
0
|
|
|
|
|
|
static int pipeline_advance(ev_clickhouse_t *self) { |
|
6348
|
0
|
0
|
|
|
|
|
if (!self->connected) return 0; |
|
6349
|
|
|
|
|
|
|
|
|
6350
|
|
|
|
|
|
|
/* if we're still waiting for a response, just ensure reading */ |
|
6351
|
0
|
0
|
|
|
|
|
if (self->send_count > 0) { |
|
6352
|
0
|
|
|
|
|
|
start_reading(self); |
|
6353
|
0
|
|
|
|
|
|
return 0; |
|
6354
|
|
|
|
|
|
|
} |
|
6355
|
|
|
|
|
|
|
|
|
6356
|
|
|
|
|
|
|
/* Check drain callback when all pending work is done */ |
|
6357
|
0
|
0
|
|
|
|
|
if (ngx_queue_empty(&self->send_queue) && self->pending_count == 0 |
|
|
|
0
|
|
|
|
|
|
|
6358
|
0
|
0
|
|
|
|
|
&& self->on_drain) { |
|
6359
|
0
|
|
|
|
|
|
SV *drain_cb = self->on_drain; |
|
6360
|
0
|
|
|
|
|
|
self->on_drain = NULL; |
|
6361
|
|
|
|
|
|
|
{ |
|
6362
|
0
|
|
|
|
|
|
dSP; |
|
6363
|
0
|
|
|
|
|
|
ENTER; |
|
6364
|
0
|
|
|
|
|
|
SAVETMPS; |
|
6365
|
0
|
0
|
|
|
|
|
PUSHMARK(SP); |
|
6366
|
0
|
|
|
|
|
|
PUTBACK; |
|
6367
|
0
|
|
|
|
|
|
self->callback_depth++; |
|
6368
|
0
|
|
|
|
|
|
call_sv(drain_cb, G_DISCARD | G_EVAL); |
|
6369
|
0
|
|
|
|
|
|
self->callback_depth--; |
|
6370
|
0
|
0
|
|
|
|
|
if (SvTRUE(ERRSV)) |
|
|
|
0
|
|
|
|
|
|
|
6371
|
0
|
0
|
|
|
|
|
warn("EV::ClickHouse: drain callback died: %s", |
|
6372
|
|
|
|
|
|
|
SvPV_nolen(ERRSV)); |
|
6373
|
0
|
0
|
|
|
|
|
FREETMPS; |
|
6374
|
0
|
|
|
|
|
|
LEAVE; |
|
6375
|
|
|
|
|
|
|
} |
|
6376
|
0
|
|
|
|
|
|
SvREFCNT_dec(drain_cb); |
|
6377
|
0
|
0
|
|
|
|
|
if (check_destroyed(self)) return 1; |
|
6378
|
|
|
|
|
|
|
} |
|
6379
|
|
|
|
|
|
|
|
|
6380
|
|
|
|
|
|
|
/* Restart keepalive timer when idle */ |
|
6381
|
0
|
0
|
|
|
|
|
if (ngx_queue_empty(&self->send_queue) && self->pending_count == 0 |
|
|
|
0
|
|
|
|
|
|
|
6382
|
0
|
0
|
|
|
|
|
&& self->keepalive > 0 && !self->ka_timing) { |
|
|
|
0
|
|
|
|
|
|
|
6383
|
0
|
|
|
|
|
|
start_keepalive(self); |
|
6384
|
|
|
|
|
|
|
} |
|
6385
|
|
|
|
|
|
|
|
|
6386
|
|
|
|
|
|
|
/* send next request from queue */ |
|
6387
|
0
|
0
|
|
|
|
|
if (!ngx_queue_empty(&self->send_queue)) { |
|
6388
|
0
|
|
|
|
|
|
ngx_queue_t *q = ngx_queue_head(&self->send_queue); |
|
6389
|
0
|
|
|
|
|
|
ev_ch_send_t *send = ngx_queue_data(q, ev_ch_send_t, queue); |
|
6390
|
|
|
|
|
|
|
|
|
6391
|
|
|
|
|
|
|
/* Stop keepalive during active query */ |
|
6392
|
0
|
|
|
|
|
|
stop_keepalive(self); |
|
6393
|
0
|
|
|
|
|
|
emit_trace(self, "dispatch query (pending=%d)", self->pending_count); |
|
6394
|
|
|
|
|
|
|
|
|
6395
|
|
|
|
|
|
|
/* set up send buffer */ |
|
6396
|
0
|
|
|
|
|
|
ensure_send_cap(self, send->data_len); |
|
6397
|
0
|
|
|
|
|
|
Copy(send->data, self->send_buf, send->data_len, char); |
|
6398
|
0
|
|
|
|
|
|
self->send_len = send->data_len; |
|
6399
|
0
|
|
|
|
|
|
self->send_pos = 0; |
|
6400
|
|
|
|
|
|
|
|
|
6401
|
|
|
|
|
|
|
/* move cb to recv queue */ |
|
6402
|
0
|
|
|
|
|
|
ngx_queue_remove(q); |
|
6403
|
0
|
|
|
|
|
|
push_cb_owned_ex(self, send->cb, send->raw, |
|
6404
|
|
|
|
|
|
|
send->on_data, send->query_timeout); |
|
6405
|
0
|
0
|
|
|
|
|
if (send->on_data) { SvREFCNT_dec(send->on_data); send->on_data = NULL; } |
|
6406
|
|
|
|
|
|
|
/* Track query_id */ |
|
6407
|
0
|
0
|
|
|
|
|
if (self->last_query_id) { Safefree(self->last_query_id); self->last_query_id = NULL; } |
|
6408
|
0
|
0
|
|
|
|
|
if (send->query_id) { self->last_query_id = send->query_id; send->query_id = NULL; } |
|
6409
|
|
|
|
|
|
|
|
|
6410
|
|
|
|
|
|
|
/* transfer deferred insert data from send entry to self */ |
|
6411
|
0
|
0
|
|
|
|
|
if (send->insert_data) { |
|
6412
|
0
|
|
|
|
|
|
self->insert_data = send->insert_data; |
|
6413
|
0
|
|
|
|
|
|
self->insert_data_len = send->insert_data_len; |
|
6414
|
0
|
|
|
|
|
|
send->insert_data = NULL; |
|
6415
|
|
|
|
|
|
|
} |
|
6416
|
0
|
0
|
|
|
|
|
if (send->insert_av) { |
|
6417
|
0
|
|
|
|
|
|
self->insert_av = send->insert_av; |
|
6418
|
0
|
|
|
|
|
|
send->insert_av = NULL; |
|
6419
|
|
|
|
|
|
|
} |
|
6420
|
|
|
|
|
|
|
|
|
6421
|
0
|
|
|
|
|
|
Safefree(send->data); |
|
6422
|
|
|
|
|
|
|
{ |
|
6423
|
0
|
|
|
|
|
|
double qt = send->query_timeout; |
|
6424
|
0
|
|
|
|
|
|
release_send(send); |
|
6425
|
0
|
|
|
|
|
|
self->send_count++; |
|
6426
|
|
|
|
|
|
|
|
|
6427
|
|
|
|
|
|
|
/* Start query timeout timer */ |
|
6428
|
|
|
|
|
|
|
{ |
|
6429
|
0
|
0
|
|
|
|
|
double timeout = qt > 0 ? qt : self->query_timeout; |
|
6430
|
0
|
0
|
|
|
|
|
if (timeout > 0 && !self->timing) { |
|
|
|
0
|
|
|
|
|
|
|
6431
|
0
|
|
|
|
|
|
ev_timer_set(&self->timer, (ev_tstamp)timeout, 0.0); |
|
6432
|
0
|
|
|
|
|
|
ev_timer_start(self->loop, &self->timer); |
|
6433
|
0
|
|
|
|
|
|
self->timing = 1; |
|
6434
|
|
|
|
|
|
|
} |
|
6435
|
|
|
|
|
|
|
} |
|
6436
|
|
|
|
|
|
|
} |
|
6437
|
|
|
|
|
|
|
|
|
6438
|
0
|
0
|
|
|
|
|
if (self->protocol == PROTO_NATIVE) { |
|
6439
|
0
|
0
|
|
|
|
|
if (self->insert_data || self->insert_av) |
|
|
|
0
|
|
|
|
|
|
|
6440
|
0
|
|
|
|
|
|
self->native_state = NATIVE_WAIT_INSERT_META; |
|
6441
|
|
|
|
|
|
|
else |
|
6442
|
0
|
|
|
|
|
|
self->native_state = NATIVE_WAIT_RESULT; |
|
6443
|
|
|
|
|
|
|
} |
|
6444
|
|
|
|
|
|
|
|
|
6445
|
0
|
|
|
|
|
|
return try_write(self); |
|
6446
|
|
|
|
|
|
|
} |
|
6447
|
0
|
|
|
|
|
|
return 0; |
|
6448
|
|
|
|
|
|
|
} |
|
6449
|
|
|
|
|
|
|
|
|
6450
|
|
|
|
|
|
|
/* --- OpenSSL init (must be in plain C, not inside XS BOOT) --- */ |
|
6451
|
|
|
|
|
|
|
|
|
6452
|
15
|
|
|
|
|
|
static void ch_openssl_init(void) { |
|
6453
|
|
|
|
|
|
|
#ifdef HAVE_OPENSSL |
|
6454
|
|
|
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L |
|
6455
|
15
|
|
|
|
|
|
OPENSSL_init_ssl(0, NULL); |
|
6456
|
|
|
|
|
|
|
#else |
|
6457
|
|
|
|
|
|
|
SSL_library_init(); |
|
6458
|
|
|
|
|
|
|
SSL_load_error_strings(); |
|
6459
|
|
|
|
|
|
|
OpenSSL_add_all_algorithms(); |
|
6460
|
|
|
|
|
|
|
#endif |
|
6461
|
|
|
|
|
|
|
#endif |
|
6462
|
15
|
|
|
|
|
|
} |
|
6463
|
|
|
|
|
|
|
|
|
6464
|
|
|
|
|
|
|
/* --- XS interface --- */ |
|
6465
|
|
|
|
|
|
|
|
|
6466
|
|
|
|
|
|
|
MODULE = EV::ClickHouse PACKAGE = EV::ClickHouse |
|
6467
|
|
|
|
|
|
|
|
|
6468
|
|
|
|
|
|
|
BOOT: |
|
6469
|
|
|
|
|
|
|
{ |
|
6470
|
15
|
50
|
|
|
|
|
I_EV_API("EV::ClickHouse"); |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
6471
|
15
|
|
|
|
|
|
ch_openssl_init(); |
|
6472
|
|
|
|
|
|
|
} |
|
6473
|
|
|
|
|
|
|
|
|
6474
|
|
|
|
|
|
|
EV::ClickHouse |
|
6475
|
|
|
|
|
|
|
_new(char *class, EV::Loop loop) |
|
6476
|
|
|
|
|
|
|
CODE: |
|
6477
|
|
|
|
|
|
|
{ |
|
6478
|
|
|
|
|
|
|
PERL_UNUSED_VAR(class); |
|
6479
|
0
|
|
|
|
|
|
Newxz(RETVAL, 1, ev_clickhouse_t); |
|
6480
|
0
|
|
|
|
|
|
RETVAL->magic = EV_CH_MAGIC; |
|
6481
|
0
|
|
|
|
|
|
RETVAL->loop = loop; |
|
6482
|
0
|
|
|
|
|
|
RETVAL->fd = -1; |
|
6483
|
0
|
|
|
|
|
|
RETVAL->protocol = PROTO_HTTP; |
|
6484
|
0
|
|
|
|
|
|
ngx_queue_init(&RETVAL->cb_queue); |
|
6485
|
0
|
|
|
|
|
|
ngx_queue_init(&RETVAL->send_queue); |
|
6486
|
|
|
|
|
|
|
|
|
6487
|
0
|
|
|
|
|
|
Newx(RETVAL->recv_buf, RECV_BUF_INIT, char); |
|
6488
|
0
|
|
|
|
|
|
RETVAL->recv_cap = RECV_BUF_INIT; |
|
6489
|
0
|
|
|
|
|
|
Newx(RETVAL->send_buf, SEND_BUF_INIT, char); |
|
6490
|
0
|
|
|
|
|
|
RETVAL->send_cap = SEND_BUF_INIT; |
|
6491
|
|
|
|
|
|
|
|
|
6492
|
0
|
|
|
|
|
|
ev_init(&RETVAL->timer, timer_cb); |
|
6493
|
0
|
|
|
|
|
|
RETVAL->timer.data = (void *)RETVAL; |
|
6494
|
|
|
|
|
|
|
} |
|
6495
|
|
|
|
|
|
|
OUTPUT: |
|
6496
|
|
|
|
|
|
|
RETVAL |
|
6497
|
|
|
|
|
|
|
|
|
6498
|
|
|
|
|
|
|
void |
|
6499
|
|
|
|
|
|
|
DESTROY(EV::ClickHouse self) |
|
6500
|
|
|
|
|
|
|
CODE: |
|
6501
|
|
|
|
|
|
|
{ |
|
6502
|
0
|
0
|
|
|
|
|
if (self->magic != EV_CH_MAGIC) return; |
|
6503
|
|
|
|
|
|
|
|
|
6504
|
0
|
|
|
|
|
|
stop_reading(self); |
|
6505
|
0
|
|
|
|
|
|
stop_writing(self); |
|
6506
|
0
|
0
|
|
|
|
|
if (self->timing) { |
|
6507
|
0
|
|
|
|
|
|
ev_timer_stop(self->loop, &self->timer); |
|
6508
|
0
|
|
|
|
|
|
self->timing = 0; |
|
6509
|
|
|
|
|
|
|
} |
|
6510
|
0
|
0
|
|
|
|
|
if (self->ka_timing) { |
|
6511
|
0
|
|
|
|
|
|
ev_timer_stop(self->loop, &self->ka_timer); |
|
6512
|
0
|
|
|
|
|
|
self->ka_timing = 0; |
|
6513
|
|
|
|
|
|
|
} |
|
6514
|
0
|
0
|
|
|
|
|
if (self->reconnect_timing) { |
|
6515
|
0
|
|
|
|
|
|
ev_timer_stop(self->loop, &self->reconnect_timer); |
|
6516
|
0
|
|
|
|
|
|
self->reconnect_timing = 0; |
|
6517
|
|
|
|
|
|
|
} |
|
6518
|
|
|
|
|
|
|
|
|
6519
|
0
|
0
|
|
|
|
|
if (PL_dirty) { |
|
6520
|
0
|
|
|
|
|
|
self->magic = EV_CH_FREED; |
|
6521
|
0
|
0
|
|
|
|
|
while (!ngx_queue_empty(&self->send_queue)) { |
|
6522
|
0
|
|
|
|
|
|
ngx_queue_t *q = ngx_queue_head(&self->send_queue); |
|
6523
|
0
|
|
|
|
|
|
ev_ch_send_t *send = ngx_queue_data(q, ev_ch_send_t, queue); |
|
6524
|
0
|
|
|
|
|
|
ngx_queue_remove(q); |
|
6525
|
0
|
|
|
|
|
|
Safefree(send->data); |
|
6526
|
0
|
0
|
|
|
|
|
if (send->insert_data) Safefree(send->insert_data); |
|
6527
|
0
|
0
|
|
|
|
|
if (send->insert_av) SvREFCNT_dec(send->insert_av); |
|
6528
|
0
|
0
|
|
|
|
|
if (send->on_data) SvREFCNT_dec(send->on_data); |
|
6529
|
0
|
|
|
|
|
|
SvREFCNT_dec(send->cb); |
|
6530
|
0
|
|
|
|
|
|
release_send(send); |
|
6531
|
|
|
|
|
|
|
} |
|
6532
|
0
|
0
|
|
|
|
|
while (!ngx_queue_empty(&self->cb_queue)) { |
|
6533
|
0
|
|
|
|
|
|
ngx_queue_t *q = ngx_queue_head(&self->cb_queue); |
|
6534
|
0
|
|
|
|
|
|
ev_ch_cb_t *cbt = ngx_queue_data(q, ev_ch_cb_t, queue); |
|
6535
|
0
|
|
|
|
|
|
ngx_queue_remove(q); |
|
6536
|
0
|
0
|
|
|
|
|
if (cbt->on_data) SvREFCNT_dec(cbt->on_data); |
|
6537
|
0
|
|
|
|
|
|
SvREFCNT_dec(cbt->cb); |
|
6538
|
0
|
|
|
|
|
|
release_cbt(cbt); |
|
6539
|
|
|
|
|
|
|
} |
|
6540
|
|
|
|
|
|
|
|
|
6541
|
0
|
0
|
|
|
|
|
#ifdef HAVE_OPENSSL |
|
6542
|
0
|
0
|
|
|
|
|
if (self->ssl) { SSL_free(self->ssl); self->ssl = NULL; } |
|
6543
|
0
|
0
|
|
|
|
|
if (self->ssl_ctx) { SSL_CTX_free(self->ssl_ctx); self->ssl_ctx = NULL; } |
|
6544
|
0
|
0
|
|
|
|
|
#endif |
|
6545
|
0
|
0
|
|
|
|
|
if (self->fd >= 0) close(self->fd); |
|
6546
|
0
|
0
|
|
|
|
|
if (self->host) Safefree(self->host); |
|
6547
|
0
|
0
|
|
|
|
|
if (self->user) Safefree(self->user); |
|
6548
|
0
|
0
|
|
|
|
|
if (self->password) Safefree(self->password); |
|
6549
|
0
|
0
|
|
|
|
|
if (self->database) Safefree(self->database); |
|
6550
|
0
|
0
|
|
|
|
|
if (self->session_id) Safefree(self->session_id); |
|
6551
|
0
|
0
|
|
|
|
|
if (self->tls_ca_file) Safefree(self->tls_ca_file); |
|
6552
|
0
|
0
|
|
|
|
|
if (self->server_name) Safefree(self->server_name); |
|
6553
|
0
|
0
|
|
|
|
|
if (self->server_display_name) Safefree(self->server_display_name); |
|
6554
|
0
|
0
|
|
|
|
|
if (self->server_timezone) Safefree(self->server_timezone); |
|
6555
|
0
|
0
|
|
|
|
|
if (self->native_rows) { SvREFCNT_dec((SV*)self->native_rows); self->native_rows = NULL; } |
|
6556
|
0
|
0
|
|
|
|
|
if (self->native_col_names) { SvREFCNT_dec((SV*)self->native_col_names); self->native_col_names = NULL; } |
|
6557
|
0
|
0
|
|
|
|
|
if (self->native_col_types) { SvREFCNT_dec((SV*)self->native_col_types); self->native_col_types = NULL; } |
|
6558
|
0
|
0
|
|
|
|
|
if (self->native_totals) { SvREFCNT_dec((SV*)self->native_totals); self->native_totals = NULL; } |
|
6559
|
0
|
0
|
|
|
|
|
if (self->native_extremes) { SvREFCNT_dec((SV*)self->native_extremes); self->native_extremes = NULL; } |
|
6560
|
0
|
0
|
|
|
|
|
if (self->default_settings) { SvREFCNT_dec((SV*)self->default_settings); self->default_settings = NULL; } |
|
6561
|
0
|
0
|
|
|
|
|
if (self->on_disconnect) { SvREFCNT_dec(self->on_disconnect); self->on_disconnect = NULL; } |
|
6562
|
0
|
0
|
|
|
|
|
if (self->on_drain) { SvREFCNT_dec(self->on_drain); self->on_drain = NULL; } |
|
6563
|
0
|
0
|
|
|
|
|
if (self->on_trace) { SvREFCNT_dec(self->on_trace); self->on_trace = NULL; } |
|
6564
|
0
|
0
|
|
|
|
|
if (self->last_query_id) Safefree(self->last_query_id); |
|
6565
|
0
|
0
|
|
|
|
|
if (self->ka_timing) { ev_timer_stop(self->loop, &self->ka_timer); self->ka_timing = 0; } |
|
6566
|
0
|
0
|
|
|
|
|
if (self->insert_data) Safefree(self->insert_data); |
|
6567
|
0
|
0
|
|
|
|
|
if (self->insert_av) SvREFCNT_dec(self->insert_av); |
|
6568
|
0
|
0
|
|
|
|
|
if (self->insert_err) Safefree(self->insert_err); |
|
6569
|
0
|
|
|
|
|
|
if (self->recv_buf) Safefree(self->recv_buf); |
|
6570
|
0
|
|
|
|
|
|
if (self->send_buf) Safefree(self->send_buf); |
|
6571
|
|
|
|
|
|
|
Safefree(self); |
|
6572
|
|
|
|
|
|
|
return; |
|
6573
|
0
|
0
|
|
|
|
|
} |
|
6574
|
0
|
|
|
|
|
|
|
|
6575
|
|
|
|
|
|
|
if (cancel_pending(self, "object destroyed")) |
|
6576
|
0
|
0
|
|
|
|
|
return; /* inner DESTROY already freed self */ |
|
6577
|
0
|
|
|
|
|
|
|
|
6578
|
0
|
|
|
|
|
|
#ifdef HAVE_OPENSSL |
|
6579
|
0
|
|
|
|
|
|
if (self->ssl) { |
|
6580
|
|
|
|
|
|
|
SSL_shutdown(self->ssl); |
|
6581
|
0
|
0
|
|
|
|
|
SSL_free(self->ssl); |
|
6582
|
0
|
|
|
|
|
|
self->ssl = NULL; |
|
6583
|
0
|
|
|
|
|
|
} |
|
6584
|
|
|
|
|
|
|
if (self->ssl_ctx) { |
|
6585
|
|
|
|
|
|
|
SSL_CTX_free(self->ssl_ctx); |
|
6586
|
0
|
0
|
|
|
|
|
self->ssl_ctx = NULL; |
|
6587
|
0
|
|
|
|
|
|
} |
|
6588
|
0
|
|
|
|
|
|
#endif |
|
6589
|
|
|
|
|
|
|
|
|
6590
|
|
|
|
|
|
|
if (self->fd >= 0) { |
|
6591
|
0
|
|
|
|
|
|
close(self->fd); |
|
6592
|
0
|
|
|
|
|
|
self->fd = -1; |
|
6593
|
|
|
|
|
|
|
} |
|
6594
|
0
|
0
|
|
|
|
|
|
|
6595
|
0
|
|
|
|
|
|
self->loop = NULL; |
|
6596
|
0
|
|
|
|
|
|
self->connected = 0; |
|
6597
|
|
|
|
|
|
|
|
|
6598
|
0
|
0
|
|
|
|
|
if (NULL != self->on_connect) { |
|
6599
|
0
|
|
|
|
|
|
SvREFCNT_dec(self->on_connect); |
|
6600
|
0
|
|
|
|
|
|
self->on_connect = NULL; |
|
6601
|
|
|
|
|
|
|
} |
|
6602
|
0
|
0
|
|
|
|
|
if (NULL != self->on_error) { |
|
6603
|
0
|
|
|
|
|
|
SvREFCNT_dec(self->on_error); |
|
6604
|
0
|
|
|
|
|
|
self->on_error = NULL; |
|
6605
|
|
|
|
|
|
|
} |
|
6606
|
0
|
0
|
|
|
|
|
if (NULL != self->on_progress) { |
|
6607
|
0
|
|
|
|
|
|
SvREFCNT_dec(self->on_progress); |
|
6608
|
0
|
|
|
|
|
|
self->on_progress = NULL; |
|
6609
|
|
|
|
|
|
|
} |
|
6610
|
0
|
0
|
|
|
|
|
if (NULL != self->on_disconnect) { |
|
6611
|
0
|
|
|
|
|
|
SvREFCNT_dec(self->on_disconnect); |
|
6612
|
0
|
|
|
|
|
|
self->on_disconnect = NULL; |
|
6613
|
|
|
|
|
|
|
} |
|
6614
|
0
|
0
|
|
|
|
|
if (NULL != self->on_drain) { |
|
6615
|
0
|
|
|
|
|
|
SvREFCNT_dec(self->on_drain); |
|
6616
|
0
|
|
|
|
|
|
self->on_drain = NULL; |
|
6617
|
|
|
|
|
|
|
} |
|
6618
|
0
|
0
|
|
|
|
|
if (NULL != self->on_trace) { |
|
6619
|
0
|
0
|
|
|
|
|
SvREFCNT_dec(self->on_trace); |
|
6620
|
0
|
0
|
|
|
|
|
self->on_trace = NULL; |
|
6621
|
0
|
0
|
|
|
|
|
} |
|
6622
|
0
|
0
|
|
|
|
|
if (self->last_query_id) { Safefree(self->last_query_id); self->last_query_id = NULL; } |
|
6623
|
0
|
0
|
|
|
|
|
if (self->host) { Safefree(self->host); self->host = NULL; } |
|
6624
|
0
|
0
|
|
|
|
|
if (self->user) { Safefree(self->user); self->user = NULL; } |
|
6625
|
0
|
0
|
|
|
|
|
if (self->password) { Safefree(self->password); self->password = NULL; } |
|
6626
|
0
|
0
|
|
|
|
|
if (self->database) { Safefree(self->database); self->database = NULL; } |
|
6627
|
0
|
0
|
|
|
|
|
if (self->session_id) { Safefree(self->session_id); self->session_id = NULL; } |
|
6628
|
0
|
0
|
|
|
|
|
if (self->tls_ca_file) { Safefree(self->tls_ca_file); self->tls_ca_file = NULL; } |
|
6629
|
0
|
0
|
|
|
|
|
if (self->server_name) { Safefree(self->server_name); self->server_name = NULL; } |
|
6630
|
0
|
0
|
|
|
|
|
if (self->server_display_name) { Safefree(self->server_display_name); self->server_display_name = NULL; } |
|
6631
|
0
|
0
|
|
|
|
|
if (self->server_timezone) { Safefree(self->server_timezone); self->server_timezone = NULL; } |
|
6632
|
0
|
0
|
|
|
|
|
if (self->native_rows) { SvREFCNT_dec((SV*)self->native_rows); self->native_rows = NULL; } |
|
6633
|
0
|
|
|
|
|
|
if (self->native_col_names) { SvREFCNT_dec((SV*)self->native_col_names); self->native_col_names = NULL; } |
|
6634
|
0
|
0
|
|
|
|
|
if (self->native_col_types) { SvREFCNT_dec((SV*)self->native_col_types); self->native_col_types = NULL; } |
|
6635
|
0
|
0
|
|
|
|
|
if (self->native_totals) { SvREFCNT_dec((SV*)self->native_totals); self->native_totals = NULL; } |
|
6636
|
0
|
0
|
|
|
|
|
if (self->native_extremes) { SvREFCNT_dec((SV*)self->native_extremes); self->native_extremes = NULL; } |
|
6637
|
0
|
0
|
|
|
|
|
lc_free_dicts(self); |
|
6638
|
0
|
0
|
|
|
|
|
if (self->default_settings) { SvREFCNT_dec((SV*)self->default_settings); self->default_settings = NULL; } |
|
6639
|
0
|
0
|
|
|
|
|
if (self->insert_data) { Safefree(self->insert_data); self->insert_data = NULL; } |
|
6640
|
|
|
|
|
|
|
if (self->insert_av) { SvREFCNT_dec(self->insert_av); self->insert_av = NULL; } |
|
6641
|
0
|
|
|
|
|
|
if (self->insert_err) { Safefree(self->insert_err); self->insert_err = NULL; } |
|
6642
|
0
|
0
|
|
|
|
|
if (self->recv_buf) { Safefree(self->recv_buf); self->recv_buf = NULL; } |
|
6643
|
0
|
|
|
|
|
|
if (self->send_buf) { Safefree(self->send_buf); self->send_buf = NULL; } |
|
6644
|
|
|
|
|
|
|
|
|
6645
|
|
|
|
|
|
|
self->magic = EV_CH_FREED; |
|
6646
|
|
|
|
|
|
|
if (self->callback_depth == 0) { |
|
6647
|
|
|
|
|
|
|
Safefree(self); |
|
6648
|
|
|
|
|
|
|
} |
|
6649
|
|
|
|
|
|
|
/* else: check_destroyed() will Safefree when callback_depth reaches 0 */ |
|
6650
|
|
|
|
|
|
|
} |
|
6651
|
|
|
|
|
|
|
|
|
6652
|
|
|
|
|
|
|
void |
|
6653
|
|
|
|
|
|
|
_set_tls_ca_file(EV::ClickHouse self, const char *path) |
|
6654
|
|
|
|
|
|
|
CODE: |
|
6655
|
|
|
|
|
|
|
{ |
|
6656
|
0
|
0
|
|
|
|
|
if (self->tls_ca_file) Safefree(self->tls_ca_file); |
|
6657
|
0
|
|
|
|
|
|
self->tls_ca_file = safe_strdup(path); |
|
6658
|
|
|
|
|
|
|
} |
|
6659
|
|
|
|
|
|
|
|
|
6660
|
|
|
|
|
|
|
void |
|
6661
|
|
|
|
|
|
|
connect(EV::ClickHouse self, const char *host, unsigned int port, const char *user, const char *password, const char *database) |
|
6662
|
|
|
|
|
|
|
CODE: |
|
6663
|
|
|
|
|
|
|
{ |
|
6664
|
0
|
0
|
|
|
|
|
if (self->connected || self->connecting) { |
|
|
|
0
|
|
|
|
|
|
|
6665
|
0
|
|
|
|
|
|
croak("already connected"); |
|
6666
|
|
|
|
|
|
|
} |
|
6667
|
0
|
0
|
|
|
|
|
if (has_http_unsafe_chars(host) || has_http_unsafe_chars(user) || |
|
6668
|
0
|
0
|
|
|
|
|
has_http_unsafe_chars(password) || has_http_unsafe_chars(database)) { |
|
6669
|
0
|
|
|
|
|
|
croak("connection parameters must not contain CR or LF"); |
|
6670
|
|
|
|
|
|
|
} |
|
6671
|
|
|
|
|
|
|
|
|
6672
|
0
|
0
|
|
|
|
|
if (self->host) Safefree(self->host); |
|
6673
|
0
|
0
|
|
|
|
|
if (self->user) Safefree(self->user); |
|
6674
|
0
|
0
|
|
|
|
|
if (self->password) Safefree(self->password); |
|
6675
|
0
|
0
|
|
|
|
|
if (self->database) Safefree(self->database); |
|
6676
|
|
|
|
|
|
|
|
|
6677
|
0
|
|
|
|
|
|
self->host = safe_strdup(host); |
|
6678
|
0
|
|
|
|
|
|
self->port = port; |
|
6679
|
0
|
|
|
|
|
|
self->user = safe_strdup(user); |
|
6680
|
0
|
|
|
|
|
|
self->password = safe_strdup(password); |
|
6681
|
0
|
|
|
|
|
|
self->database = safe_strdup(database); |
|
6682
|
|
|
|
|
|
|
|
|
6683
|
0
|
|
|
|
|
|
start_connect(self); |
|
6684
|
|
|
|
|
|
|
} |
|
6685
|
|
|
|
|
|
|
|
|
6686
|
|
|
|
|
|
|
void |
|
6687
|
|
|
|
|
|
|
reset(EV::ClickHouse self) |
|
6688
|
|
|
|
|
|
|
CODE: |
|
6689
|
|
|
|
|
|
|
{ |
|
6690
|
0
|
0
|
|
|
|
|
if (NULL == self->host) { |
|
6691
|
0
|
|
|
|
|
|
croak("no previous connection to reset"); |
|
6692
|
|
|
|
|
|
|
} |
|
6693
|
|
|
|
|
|
|
|
|
6694
|
0
|
0
|
|
|
|
|
if (cancel_pending(self, "connection reset")) return; |
|
6695
|
0
|
|
|
|
|
|
cleanup_connection(self); |
|
6696
|
0
|
|
|
|
|
|
start_connect(self); |
|
6697
|
|
|
|
|
|
|
} |
|
6698
|
|
|
|
|
|
|
|
|
6699
|
|
|
|
|
|
|
void |
|
6700
|
|
|
|
|
|
|
finish(EV::ClickHouse self) |
|
6701
|
|
|
|
|
|
|
CODE: |
|
6702
|
|
|
|
|
|
|
{ |
|
6703
|
0
|
0
|
|
|
|
|
if (cancel_pending(self, "connection finished")) return; |
|
6704
|
0
|
|
|
|
|
|
cleanup_connection(self); |
|
6705
|
|
|
|
|
|
|
} |
|
6706
|
|
|
|
|
|
|
|
|
6707
|
|
|
|
|
|
|
void |
|
6708
|
|
|
|
|
|
|
query(EV::ClickHouse self, SV *sql_sv, ...) |
|
6709
|
|
|
|
|
|
|
CODE: |
|
6710
|
|
|
|
|
|
|
{ |
|
6711
|
|
|
|
|
|
|
STRLEN sql_len; |
|
6712
|
|
|
|
|
|
|
const char *sql; |
|
6713
|
|
|
|
|
|
|
ev_ch_send_t *s; |
|
6714
|
|
|
|
|
|
|
char *req; |
|
6715
|
|
|
|
|
|
|
size_t req_len; |
|
6716
|
|
|
|
|
|
|
SV *cb; |
|
6717
|
0
|
|
|
|
|
|
HV *settings = NULL; |
|
6718
|
0
|
|
|
|
|
|
int raw = 0; |
|
6719
|
|
|
|
|
|
|
|
|
6720
|
0
|
0
|
|
|
|
|
if (items == 3) { |
|
6721
|
0
|
|
|
|
|
|
cb = ST(2); |
|
6722
|
0
|
0
|
|
|
|
|
} else if (items == 4) { |
|
6723
|
0
|
0
|
|
|
|
|
if (!(SvROK(ST(2)) && SvTYPE(SvRV(ST(2))) == SVt_PVHV)) |
|
|
|
0
|
|
|
|
|
|
|
6724
|
0
|
|
|
|
|
|
croak("settings must be a HASH reference"); |
|
6725
|
0
|
|
|
|
|
|
settings = (HV *)SvRV(ST(2)); |
|
6726
|
0
|
|
|
|
|
|
cb = ST(3); |
|
6727
|
|
|
|
|
|
|
} else { |
|
6728
|
0
|
|
|
|
|
|
croak("Usage: $ch->query($sql, [\\%%settings], $cb)"); |
|
6729
|
|
|
|
|
|
|
} |
|
6730
|
|
|
|
|
|
|
|
|
6731
|
0
|
0
|
|
|
|
|
if (!self->connected && !self->connecting) { |
|
|
|
0
|
|
|
|
|
|
|
6732
|
0
|
|
|
|
|
|
croak("not connected"); |
|
6733
|
|
|
|
|
|
|
} |
|
6734
|
0
|
0
|
|
|
|
|
if (!(SvROK(cb) && SvTYPE(SvRV(cb)) == SVt_PVCV)) { |
|
|
|
0
|
|
|
|
|
|
|
6735
|
0
|
|
|
|
|
|
croak("callback must be a CODE reference"); |
|
6736
|
|
|
|
|
|
|
} |
|
6737
|
|
|
|
|
|
|
|
|
6738
|
0
|
0
|
|
|
|
|
if (self->protocol == PROTO_NATIVE && (self->insert_data || self->insert_av)) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
6739
|
0
|
|
|
|
|
|
croak("cannot queue native query while INSERT is pending"); |
|
6740
|
|
|
|
|
|
|
} |
|
6741
|
|
|
|
|
|
|
|
|
6742
|
|
|
|
|
|
|
/* Extract client-side options from settings */ |
|
6743
|
0
|
|
|
|
|
|
SV *on_data_sv = NULL; |
|
6744
|
0
|
|
|
|
|
|
double query_timeout = 0; |
|
6745
|
0
|
|
|
|
|
|
HV *settings_copy = NULL; /* owned copy if we need to expand params */ |
|
6746
|
0
|
0
|
|
|
|
|
if (settings) { |
|
6747
|
|
|
|
|
|
|
SV **svp; |
|
6748
|
|
|
|
|
|
|
/* Expand params => { x => 1 } to param_x => '1' in a copy */ |
|
6749
|
0
|
|
|
|
|
|
svp = hv_fetch(settings, "params", 6, 0); |
|
6750
|
0
|
0
|
|
|
|
|
if (svp && SvOK(*svp) && SvROK(*svp) && SvTYPE(SvRV(*svp)) == SVt_PVHV) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
6751
|
0
|
|
|
|
|
|
HV *phv = (HV *)SvRV(*svp); |
|
6752
|
|
|
|
|
|
|
HE *pe; |
|
6753
|
|
|
|
|
|
|
/* Copy settings to avoid mutating caller's hashref */ |
|
6754
|
0
|
|
|
|
|
|
settings_copy = newHVhv(settings); |
|
6755
|
0
|
|
|
|
|
|
settings = settings_copy; |
|
6756
|
0
|
|
|
|
|
|
hv_iterinit(phv); |
|
6757
|
0
|
0
|
|
|
|
|
while ((pe = hv_iternext(phv))) { |
|
6758
|
|
|
|
|
|
|
I32 pklen; |
|
6759
|
0
|
|
|
|
|
|
char *pkey = hv_iterkey(pe, &pklen); |
|
6760
|
0
|
|
|
|
|
|
SV *pval = hv_iterval(phv, pe); |
|
6761
|
|
|
|
|
|
|
char *prefixed; |
|
6762
|
0
|
|
|
|
|
|
Newx(prefixed, pklen + 7, char); |
|
6763
|
0
|
|
|
|
|
|
Copy("param_", prefixed, 6, char); |
|
6764
|
0
|
|
|
|
|
|
Copy(pkey, prefixed + 6, pklen, char); |
|
6765
|
0
|
|
|
|
|
|
(void)hv_store(settings, prefixed, pklen + 6, |
|
6766
|
|
|
|
|
|
|
newSVsv(pval), 0); |
|
6767
|
0
|
|
|
|
|
|
Safefree(prefixed); |
|
6768
|
|
|
|
|
|
|
} |
|
6769
|
|
|
|
|
|
|
} |
|
6770
|
0
|
|
|
|
|
|
svp = hv_fetch(settings, "raw", 3, 0); |
|
6771
|
0
|
0
|
|
|
|
|
if (svp) |
|
6772
|
0
|
|
|
|
|
|
raw = SvTRUE(*svp) ? 1 : 0; |
|
6773
|
0
|
|
|
|
|
|
svp = hv_fetch(settings, "on_data", 7, 0); |
|
6774
|
0
|
0
|
|
|
|
|
if (svp && SvOK(*svp) && SvROK(*svp) && SvTYPE(SvRV(*svp)) == SVt_PVCV) |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
6775
|
0
|
|
|
|
|
|
on_data_sv = *svp; |
|
6776
|
0
|
|
|
|
|
|
svp = hv_fetch(settings, "query_timeout", 13, 0); |
|
6777
|
0
|
0
|
|
|
|
|
if (svp && SvOK(*svp)) |
|
|
|
0
|
|
|
|
|
|
|
6778
|
0
|
|
|
|
|
|
query_timeout = SvNV(*svp); |
|
6779
|
|
|
|
|
|
|
} |
|
6780
|
|
|
|
|
|
|
|
|
6781
|
0
|
|
|
|
|
|
sql = SvPV(sql_sv, sql_len); |
|
6782
|
|
|
|
|
|
|
|
|
6783
|
0
|
0
|
|
|
|
|
if (raw && self->protocol == PROTO_NATIVE) { |
|
|
|
0
|
|
|
|
|
|
|
6784
|
0
|
0
|
|
|
|
|
if (settings_copy) SvREFCNT_dec((SV*)settings_copy); |
|
6785
|
0
|
|
|
|
|
|
croak("raw mode is only supported with the HTTP protocol"); |
|
6786
|
|
|
|
|
|
|
} |
|
6787
|
|
|
|
|
|
|
|
|
6788
|
0
|
0
|
|
|
|
|
if (on_data_sv && self->protocol == PROTO_HTTP) { |
|
|
|
0
|
|
|
|
|
|
|
6789
|
0
|
0
|
|
|
|
|
if (settings_copy) SvREFCNT_dec((SV*)settings_copy); |
|
6790
|
0
|
|
|
|
|
|
croak("on_data is only supported with the native protocol"); |
|
6791
|
|
|
|
|
|
|
} |
|
6792
|
|
|
|
|
|
|
|
|
6793
|
0
|
0
|
|
|
|
|
if (self->protocol == PROTO_HTTP) { |
|
6794
|
0
|
|
|
|
|
|
req = build_http_query_request(self, sql, sql_len, self->compress, |
|
6795
|
|
|
|
|
|
|
self->default_settings, settings, |
|
6796
|
|
|
|
|
|
|
&req_len); |
|
6797
|
|
|
|
|
|
|
} else { |
|
6798
|
0
|
|
|
|
|
|
req = build_native_query(self, sql, sql_len, |
|
6799
|
|
|
|
|
|
|
self->default_settings, settings, &req_len); |
|
6800
|
|
|
|
|
|
|
} |
|
6801
|
|
|
|
|
|
|
|
|
6802
|
0
|
|
|
|
|
|
s = alloc_send(); |
|
6803
|
0
|
|
|
|
|
|
s->data = req; |
|
6804
|
0
|
|
|
|
|
|
s->data_len = req_len; |
|
6805
|
0
|
|
|
|
|
|
s->raw = raw; |
|
6806
|
0
|
0
|
|
|
|
|
if (on_data_sv) s->on_data = SvREFCNT_inc(on_data_sv); |
|
6807
|
0
|
|
|
|
|
|
s->query_timeout = query_timeout; |
|
6808
|
0
|
0
|
|
|
|
|
if (settings) { |
|
6809
|
0
|
|
|
|
|
|
SV **qid = hv_fetch(settings, "query_id", 8, 0); |
|
6810
|
0
|
0
|
|
|
|
|
if (qid && SvOK(*qid)) { |
|
|
|
0
|
|
|
|
|
|
|
6811
|
|
|
|
|
|
|
STRLEN qlen; |
|
6812
|
0
|
|
|
|
|
|
const char *qstr = SvPV(*qid, qlen); |
|
6813
|
0
|
|
|
|
|
|
Newx(s->query_id, qlen + 1, char); |
|
6814
|
0
|
|
|
|
|
|
Copy(qstr, s->query_id, qlen, char); |
|
6815
|
0
|
|
|
|
|
|
s->query_id[qlen] = '\0'; |
|
6816
|
|
|
|
|
|
|
} |
|
6817
|
|
|
|
|
|
|
} |
|
6818
|
0
|
|
|
|
|
|
s->cb = SvREFCNT_inc(cb); |
|
6819
|
0
|
0
|
|
|
|
|
if (settings_copy) SvREFCNT_dec((SV*)settings_copy); |
|
6820
|
0
|
|
|
|
|
|
ngx_queue_insert_tail(&self->send_queue, &s->queue); |
|
6821
|
0
|
|
|
|
|
|
self->pending_count++; |
|
6822
|
|
|
|
|
|
|
|
|
6823
|
0
|
0
|
|
|
|
|
if (self->connected && self->callback_depth == 0) |
|
|
|
0
|
|
|
|
|
|
|
6824
|
0
|
|
|
|
|
|
pipeline_advance(self); |
|
6825
|
|
|
|
|
|
|
} |
|
6826
|
|
|
|
|
|
|
|
|
6827
|
|
|
|
|
|
|
void |
|
6828
|
|
|
|
|
|
|
insert(EV::ClickHouse self, SV *table_sv, SV *data_sv, ...) |
|
6829
|
|
|
|
|
|
|
CODE: |
|
6830
|
|
|
|
|
|
|
{ |
|
6831
|
|
|
|
|
|
|
STRLEN table_len; |
|
6832
|
|
|
|
|
|
|
const char *table; |
|
6833
|
|
|
|
|
|
|
ev_ch_send_t *s; |
|
6834
|
|
|
|
|
|
|
char *req; |
|
6835
|
|
|
|
|
|
|
size_t req_len; |
|
6836
|
|
|
|
|
|
|
SV *cb; |
|
6837
|
0
|
|
|
|
|
|
HV *settings = NULL; |
|
6838
|
0
|
|
|
|
|
|
int data_is_av = 0; |
|
6839
|
0
|
|
|
|
|
|
AV *data_av = NULL; |
|
6840
|
|
|
|
|
|
|
|
|
6841
|
0
|
0
|
|
|
|
|
if (items == 4) { |
|
6842
|
0
|
|
|
|
|
|
cb = ST(3); |
|
6843
|
0
|
0
|
|
|
|
|
} else if (items == 5) { |
|
6844
|
0
|
0
|
|
|
|
|
if (!(SvROK(ST(3)) && SvTYPE(SvRV(ST(3))) == SVt_PVHV)) |
|
|
|
0
|
|
|
|
|
|
|
6845
|
0
|
|
|
|
|
|
croak("settings must be a HASH reference"); |
|
6846
|
0
|
|
|
|
|
|
settings = (HV *)SvRV(ST(3)); |
|
6847
|
0
|
|
|
|
|
|
cb = ST(4); |
|
6848
|
|
|
|
|
|
|
} else { |
|
6849
|
0
|
|
|
|
|
|
croak("Usage: $ch->insert($table, $data, [\\%%settings], $cb)"); |
|
6850
|
|
|
|
|
|
|
} |
|
6851
|
|
|
|
|
|
|
|
|
6852
|
0
|
0
|
|
|
|
|
if (!self->connected && !self->connecting) { |
|
|
|
0
|
|
|
|
|
|
|
6853
|
0
|
|
|
|
|
|
croak("not connected"); |
|
6854
|
|
|
|
|
|
|
} |
|
6855
|
0
|
0
|
|
|
|
|
if (!(SvROK(cb) && SvTYPE(SvRV(cb)) == SVt_PVCV)) { |
|
|
|
0
|
|
|
|
|
|
|
6856
|
0
|
|
|
|
|
|
croak("callback must be a CODE reference"); |
|
6857
|
|
|
|
|
|
|
} |
|
6858
|
|
|
|
|
|
|
|
|
6859
|
0
|
|
|
|
|
|
table = SvPV(table_sv, table_len); |
|
6860
|
|
|
|
|
|
|
|
|
6861
|
|
|
|
|
|
|
/* Detect arrayref-of-arrayrefs vs TSV string */ |
|
6862
|
0
|
0
|
|
|
|
|
if (SvROK(data_sv) && SvTYPE(SvRV(data_sv)) == SVt_PVAV) { |
|
|
|
0
|
|
|
|
|
|
|
6863
|
0
|
|
|
|
|
|
data_is_av = 1; |
|
6864
|
0
|
|
|
|
|
|
data_av = (AV *)SvRV(data_sv); |
|
6865
|
|
|
|
|
|
|
} |
|
6866
|
|
|
|
|
|
|
|
|
6867
|
0
|
0
|
|
|
|
|
if (self->protocol == PROTO_HTTP) { |
|
6868
|
|
|
|
|
|
|
STRLEN data_len; |
|
6869
|
|
|
|
|
|
|
const char *data; |
|
6870
|
0
|
|
|
|
|
|
char *tsv_buf = NULL; |
|
6871
|
|
|
|
|
|
|
|
|
6872
|
0
|
0
|
|
|
|
|
if (data_is_av) { |
|
6873
|
0
|
|
|
|
|
|
tsv_buf = serialize_av_to_tsv(aTHX_ data_av, &data_len); |
|
6874
|
0
|
|
|
|
|
|
data = tsv_buf; |
|
6875
|
|
|
|
|
|
|
} else { |
|
6876
|
0
|
|
|
|
|
|
data = SvPV(data_sv, data_len); |
|
6877
|
|
|
|
|
|
|
} |
|
6878
|
|
|
|
|
|
|
|
|
6879
|
0
|
|
|
|
|
|
req = build_http_insert_request(self, table, table_len, |
|
6880
|
|
|
|
|
|
|
data, data_len, self->compress, |
|
6881
|
|
|
|
|
|
|
self->default_settings, settings, |
|
6882
|
|
|
|
|
|
|
&req_len); |
|
6883
|
0
|
0
|
|
|
|
|
if (tsv_buf) Safefree(tsv_buf); |
|
6884
|
|
|
|
|
|
|
} else { |
|
6885
|
|
|
|
|
|
|
/* Native insert: two-phase approach. |
|
6886
|
|
|
|
|
|
|
* Phase 1: send INSERT query without inline data, receive sample block. |
|
6887
|
|
|
|
|
|
|
* Phase 2: encode data as binary columnar Data block, send compressed. */ |
|
6888
|
|
|
|
|
|
|
char *insert_sql; |
|
6889
|
|
|
|
|
|
|
size_t insert_sql_len; |
|
6890
|
|
|
|
|
|
|
|
|
6891
|
|
|
|
|
|
|
/* Only one native INSERT at a time */ |
|
6892
|
0
|
0
|
|
|
|
|
if (self->insert_data || self->insert_av) { |
|
|
|
0
|
|
|
|
|
|
|
6893
|
0
|
|
|
|
|
|
croak("cannot pipeline native INSERT: previous INSERT still pending"); |
|
6894
|
|
|
|
|
|
|
} |
|
6895
|
|
|
|
|
|
|
|
|
6896
|
|
|
|
|
|
|
/* Build: "INSERT INTO table FORMAT TabSeparated" (no inline data) */ |
|
6897
|
0
|
|
|
|
|
|
Newx(insert_sql, table_len + 64, char); |
|
6898
|
0
|
|
|
|
|
|
insert_sql_len = snprintf(insert_sql, table_len + 64, |
|
6899
|
|
|
|
|
|
|
"INSERT INTO %.*s FORMAT TabSeparated", |
|
6900
|
|
|
|
|
|
|
(int)table_len, table); |
|
6901
|
0
|
|
|
|
|
|
req = build_native_query(self, insert_sql, insert_sql_len, |
|
6902
|
|
|
|
|
|
|
self->default_settings, settings, &req_len); |
|
6903
|
0
|
|
|
|
|
|
Safefree(insert_sql); |
|
6904
|
|
|
|
|
|
|
} |
|
6905
|
|
|
|
|
|
|
|
|
6906
|
0
|
|
|
|
|
|
s = alloc_send(); |
|
6907
|
0
|
|
|
|
|
|
s->data = req; |
|
6908
|
0
|
|
|
|
|
|
s->data_len = req_len; |
|
6909
|
0
|
0
|
|
|
|
|
if (settings) { |
|
6910
|
0
|
|
|
|
|
|
SV **qid = hv_fetch(settings, "query_id", 8, 0); |
|
6911
|
0
|
0
|
|
|
|
|
if (qid && SvOK(*qid)) { |
|
|
|
0
|
|
|
|
|
|
|
6912
|
|
|
|
|
|
|
STRLEN qlen; |
|
6913
|
0
|
|
|
|
|
|
const char *qstr = SvPV(*qid, qlen); |
|
6914
|
0
|
|
|
|
|
|
Newx(s->query_id, qlen + 1, char); |
|
6915
|
0
|
|
|
|
|
|
Copy(qstr, s->query_id, qlen, char); |
|
6916
|
0
|
|
|
|
|
|
s->query_id[qlen] = '\0'; |
|
6917
|
|
|
|
|
|
|
} |
|
6918
|
|
|
|
|
|
|
} |
|
6919
|
|
|
|
|
|
|
|
|
6920
|
|
|
|
|
|
|
/* For native INSERT, store data in the send entry (deferred to dispatch). |
|
6921
|
|
|
|
|
|
|
* Even empty data needs the two-phase INSERT protocol to send an empty |
|
6922
|
|
|
|
|
|
|
* DATA block. */ |
|
6923
|
0
|
0
|
|
|
|
|
if (self->protocol == PROTO_NATIVE) { |
|
6924
|
0
|
0
|
|
|
|
|
if (data_is_av) { |
|
6925
|
0
|
|
|
|
|
|
s->insert_av = SvREFCNT_inc(data_sv); |
|
6926
|
|
|
|
|
|
|
} else { |
|
6927
|
|
|
|
|
|
|
STRLEN data_len; |
|
6928
|
0
|
|
|
|
|
|
const char *data = SvPV(data_sv, data_len); |
|
6929
|
0
|
0
|
|
|
|
|
Newx(s->insert_data, data_len > 0 ? data_len : 1, char); |
|
6930
|
0
|
0
|
|
|
|
|
if (data_len > 0) |
|
6931
|
0
|
|
|
|
|
|
Copy(data, s->insert_data, data_len, char); |
|
6932
|
0
|
|
|
|
|
|
s->insert_data_len = data_len; |
|
6933
|
|
|
|
|
|
|
} |
|
6934
|
|
|
|
|
|
|
} |
|
6935
|
|
|
|
|
|
|
|
|
6936
|
0
|
|
|
|
|
|
s->cb = SvREFCNT_inc(cb); |
|
6937
|
0
|
|
|
|
|
|
ngx_queue_insert_tail(&self->send_queue, &s->queue); |
|
6938
|
0
|
|
|
|
|
|
self->pending_count++; |
|
6939
|
|
|
|
|
|
|
|
|
6940
|
0
|
0
|
|
|
|
|
if (self->connected && self->callback_depth == 0) |
|
|
|
0
|
|
|
|
|
|
|
6941
|
0
|
|
|
|
|
|
pipeline_advance(self); |
|
6942
|
|
|
|
|
|
|
} |
|
6943
|
|
|
|
|
|
|
|
|
6944
|
|
|
|
|
|
|
void |
|
6945
|
|
|
|
|
|
|
ping(EV::ClickHouse self, SV *cb) |
|
6946
|
|
|
|
|
|
|
CODE: |
|
6947
|
|
|
|
|
|
|
{ |
|
6948
|
|
|
|
|
|
|
ev_ch_send_t *s; |
|
6949
|
|
|
|
|
|
|
char *req; |
|
6950
|
|
|
|
|
|
|
size_t req_len; |
|
6951
|
|
|
|
|
|
|
|
|
6952
|
0
|
0
|
|
|
|
|
if (!self->connected && !self->connecting) { |
|
|
|
0
|
|
|
|
|
|
|
6953
|
0
|
|
|
|
|
|
croak("not connected"); |
|
6954
|
|
|
|
|
|
|
} |
|
6955
|
0
|
0
|
|
|
|
|
if (!(SvROK(cb) && SvTYPE(SvRV(cb)) == SVt_PVCV)) { |
|
|
|
0
|
|
|
|
|
|
|
6956
|
0
|
|
|
|
|
|
croak("callback must be a CODE reference"); |
|
6957
|
|
|
|
|
|
|
} |
|
6958
|
|
|
|
|
|
|
|
|
6959
|
0
|
0
|
|
|
|
|
if (self->protocol == PROTO_HTTP) { |
|
6960
|
0
|
|
|
|
|
|
req = build_http_ping_request(self, &req_len); |
|
6961
|
|
|
|
|
|
|
} else { |
|
6962
|
0
|
|
|
|
|
|
req = build_native_ping(&req_len); |
|
6963
|
|
|
|
|
|
|
} |
|
6964
|
|
|
|
|
|
|
|
|
6965
|
0
|
|
|
|
|
|
s = alloc_send(); |
|
6966
|
0
|
|
|
|
|
|
s->data = req; |
|
6967
|
0
|
|
|
|
|
|
s->data_len = req_len; |
|
6968
|
0
|
|
|
|
|
|
s->cb = SvREFCNT_inc(cb); |
|
6969
|
0
|
|
|
|
|
|
ngx_queue_insert_tail(&self->send_queue, &s->queue); |
|
6970
|
0
|
|
|
|
|
|
self->pending_count++; |
|
6971
|
|
|
|
|
|
|
|
|
6972
|
0
|
0
|
|
|
|
|
if (self->connected && self->callback_depth == 0) |
|
|
|
0
|
|
|
|
|
|
|
6973
|
0
|
|
|
|
|
|
pipeline_advance(self); |
|
6974
|
|
|
|
|
|
|
} |
|
6975
|
|
|
|
|
|
|
|
|
6976
|
|
|
|
|
|
|
SV* |
|
6977
|
|
|
|
|
|
|
on_connect(EV::ClickHouse self, SV *handler = NULL) |
|
6978
|
|
|
|
|
|
|
CODE: |
|
6979
|
|
|
|
|
|
|
{ |
|
6980
|
0
|
|
|
|
|
|
RETVAL = handler_accessor(&self->on_connect, handler, items > 1); |
|
6981
|
|
|
|
|
|
|
} |
|
6982
|
|
|
|
|
|
|
OUTPUT: |
|
6983
|
|
|
|
|
|
|
RETVAL |
|
6984
|
|
|
|
|
|
|
|
|
6985
|
|
|
|
|
|
|
SV* |
|
6986
|
|
|
|
|
|
|
on_error(EV::ClickHouse self, SV *handler = NULL) |
|
6987
|
|
|
|
|
|
|
CODE: |
|
6988
|
|
|
|
|
|
|
{ |
|
6989
|
0
|
|
|
|
|
|
RETVAL = handler_accessor(&self->on_error, handler, items > 1); |
|
6990
|
|
|
|
|
|
|
} |
|
6991
|
|
|
|
|
|
|
OUTPUT: |
|
6992
|
|
|
|
|
|
|
RETVAL |
|
6993
|
|
|
|
|
|
|
|
|
6994
|
|
|
|
|
|
|
SV* |
|
6995
|
|
|
|
|
|
|
on_progress(EV::ClickHouse self, SV *handler = NULL) |
|
6996
|
|
|
|
|
|
|
CODE: |
|
6997
|
|
|
|
|
|
|
{ |
|
6998
|
0
|
|
|
|
|
|
RETVAL = handler_accessor(&self->on_progress, handler, items > 1); |
|
6999
|
|
|
|
|
|
|
} |
|
7000
|
|
|
|
|
|
|
OUTPUT: |
|
7001
|
|
|
|
|
|
|
RETVAL |
|
7002
|
|
|
|
|
|
|
|
|
7003
|
|
|
|
|
|
|
int |
|
7004
|
|
|
|
|
|
|
is_connected(EV::ClickHouse self) |
|
7005
|
|
|
|
|
|
|
CODE: |
|
7006
|
|
|
|
|
|
|
{ |
|
7007
|
0
|
0
|
|
|
|
|
RETVAL = self->connected ? 1 : 0; |
|
7008
|
|
|
|
|
|
|
} |
|
7009
|
|
|
|
|
|
|
OUTPUT: |
|
7010
|
|
|
|
|
|
|
RETVAL |
|
7011
|
|
|
|
|
|
|
|
|
7012
|
|
|
|
|
|
|
int |
|
7013
|
|
|
|
|
|
|
pending_count(EV::ClickHouse self) |
|
7014
|
|
|
|
|
|
|
CODE: |
|
7015
|
|
|
|
|
|
|
{ |
|
7016
|
0
|
0
|
|
|
|
|
RETVAL = self->pending_count; |
|
7017
|
|
|
|
|
|
|
} |
|
7018
|
|
|
|
|
|
|
OUTPUT: |
|
7019
|
|
|
|
|
|
|
RETVAL |
|
7020
|
|
|
|
|
|
|
|
|
7021
|
|
|
|
|
|
|
SV * |
|
7022
|
|
|
|
|
|
|
server_info(EV::ClickHouse self) |
|
7023
|
|
|
|
|
|
|
CODE: |
|
7024
|
|
|
|
|
|
|
{ |
|
7025
|
0
|
0
|
|
|
|
|
if (self->server_name) { |
|
7026
|
|
|
|
|
|
|
char buf[256]; |
|
7027
|
0
|
|
|
|
|
|
int n = snprintf(buf, sizeof(buf), "%s %u.%u.%u (revision %u)", |
|
7028
|
|
|
|
|
|
|
self->server_name, |
|
7029
|
|
|
|
|
|
|
self->server_version_major, |
|
7030
|
|
|
|
|
|
|
self->server_version_minor, |
|
7031
|
|
|
|
|
|
|
self->server_version_patch, |
|
7032
|
|
|
|
|
|
|
self->server_revision); |
|
7033
|
0
|
0
|
|
|
|
|
if (n >= (int)sizeof(buf)) n = (int)sizeof(buf) - 1; |
|
7034
|
0
|
|
|
|
|
|
RETVAL = newSVpvn(buf, n); |
|
7035
|
|
|
|
|
|
|
} else { |
|
7036
|
0
|
|
|
|
|
|
RETVAL = &PL_sv_undef; |
|
7037
|
|
|
|
|
|
|
} |
|
7038
|
|
|
|
|
|
|
} |
|
7039
|
|
|
|
|
|
|
OUTPUT: |
|
7040
|
|
|
|
|
|
|
RETVAL |
|
7041
|
|
|
|
|
|
|
|
|
7042
|
|
|
|
|
|
|
SV * |
|
7043
|
|
|
|
|
|
|
server_version(EV::ClickHouse self) |
|
7044
|
|
|
|
|
|
|
CODE: |
|
7045
|
|
|
|
|
|
|
{ |
|
7046
|
0
|
0
|
|
|
|
|
if (self->server_name) { |
|
7047
|
|
|
|
|
|
|
char buf[64]; |
|
7048
|
0
|
|
|
|
|
|
int n = snprintf(buf, sizeof(buf), "%u.%u.%u", |
|
7049
|
|
|
|
|
|
|
self->server_version_major, |
|
7050
|
|
|
|
|
|
|
self->server_version_minor, |
|
7051
|
|
|
|
|
|
|
self->server_version_patch); |
|
7052
|
0
|
0
|
|
|
|
|
if (n >= (int)sizeof(buf)) n = (int)sizeof(buf) - 1; |
|
7053
|
0
|
|
|
|
|
|
RETVAL = newSVpvn(buf, n); |
|
7054
|
|
|
|
|
|
|
} else { |
|
7055
|
0
|
|
|
|
|
|
RETVAL = &PL_sv_undef; |
|
7056
|
|
|
|
|
|
|
} |
|
7057
|
|
|
|
|
|
|
} |
|
7058
|
|
|
|
|
|
|
OUTPUT: |
|
7059
|
|
|
|
|
|
|
RETVAL |
|
7060
|
|
|
|
|
|
|
|
|
7061
|
|
|
|
|
|
|
void |
|
7062
|
|
|
|
|
|
|
skip_pending(EV::ClickHouse self) |
|
7063
|
|
|
|
|
|
|
CODE: |
|
7064
|
|
|
|
|
|
|
{ |
|
7065
|
0
|
0
|
|
|
|
|
if (self->send_count > 0) { |
|
7066
|
0
|
|
|
|
|
|
cleanup_connection(self); |
|
7067
|
|
|
|
|
|
|
} |
|
7068
|
0
|
0
|
|
|
|
|
if (self->insert_data) { |
|
7069
|
0
|
|
|
|
|
|
Safefree(self->insert_data); |
|
7070
|
0
|
|
|
|
|
|
self->insert_data = NULL; |
|
7071
|
0
|
|
|
|
|
|
self->insert_data_len = 0; |
|
7072
|
|
|
|
|
|
|
} |
|
7073
|
0
|
0
|
|
|
|
|
if (self->insert_av) { |
|
7074
|
0
|
|
|
|
|
|
SvREFCNT_dec(self->insert_av); |
|
7075
|
0
|
|
|
|
|
|
self->insert_av = NULL; |
|
7076
|
|
|
|
|
|
|
} |
|
7077
|
0
|
0
|
|
|
|
|
if (self->insert_err) { |
|
7078
|
0
|
|
|
|
|
|
Safefree(self->insert_err); |
|
7079
|
0
|
|
|
|
|
|
self->insert_err = NULL; |
|
7080
|
|
|
|
|
|
|
} |
|
7081
|
0
|
0
|
|
|
|
|
if (cancel_pending(self, "skipped")) return; |
|
7082
|
|
|
|
|
|
|
} |
|
7083
|
|
|
|
|
|
|
|
|
7084
|
|
|
|
|
|
|
void |
|
7085
|
|
|
|
|
|
|
_set_protocol(EV::ClickHouse self, int proto) |
|
7086
|
|
|
|
|
|
|
CODE: |
|
7087
|
|
|
|
|
|
|
{ |
|
7088
|
0
|
|
|
|
|
|
self->protocol = proto; |
|
7089
|
|
|
|
|
|
|
} |
|
7090
|
|
|
|
|
|
|
|
|
7091
|
|
|
|
|
|
|
void |
|
7092
|
|
|
|
|
|
|
_set_compress(EV::ClickHouse self, int val) |
|
7093
|
|
|
|
|
|
|
CODE: |
|
7094
|
|
|
|
|
|
|
{ |
|
7095
|
0
|
|
|
|
|
|
self->compress = val; |
|
7096
|
|
|
|
|
|
|
} |
|
7097
|
|
|
|
|
|
|
|
|
7098
|
|
|
|
|
|
|
void |
|
7099
|
|
|
|
|
|
|
_set_session_id(EV::ClickHouse self, const char *sid) |
|
7100
|
|
|
|
|
|
|
CODE: |
|
7101
|
|
|
|
|
|
|
{ |
|
7102
|
0
|
0
|
|
|
|
|
if (self->session_id) Safefree(self->session_id); |
|
7103
|
0
|
|
|
|
|
|
self->session_id = safe_strdup(sid); |
|
7104
|
|
|
|
|
|
|
} |
|
7105
|
|
|
|
|
|
|
|
|
7106
|
|
|
|
|
|
|
void |
|
7107
|
|
|
|
|
|
|
_set_connect_timeout(EV::ClickHouse self, NV val) |
|
7108
|
|
|
|
|
|
|
CODE: |
|
7109
|
|
|
|
|
|
|
{ |
|
7110
|
0
|
|
|
|
|
|
self->connect_timeout = val; |
|
7111
|
|
|
|
|
|
|
} |
|
7112
|
|
|
|
|
|
|
|
|
7113
|
|
|
|
|
|
|
void |
|
7114
|
|
|
|
|
|
|
_set_tls(EV::ClickHouse self, int val) |
|
7115
|
|
|
|
|
|
|
CODE: |
|
7116
|
|
|
|
|
|
|
{ |
|
7117
|
0
|
|
|
|
|
|
#ifdef HAVE_OPENSSL |
|
7118
|
0
|
0
|
|
|
|
|
self->tls_enabled = val; |
|
7119
|
|
|
|
|
|
|
#else |
|
7120
|
|
|
|
|
|
|
if (val) croak("TLS support not compiled in (OpenSSL not found)"); |
|
7121
|
|
|
|
|
|
|
#endif |
|
7122
|
|
|
|
|
|
|
} |
|
7123
|
|
|
|
|
|
|
|
|
7124
|
|
|
|
|
|
|
void |
|
7125
|
|
|
|
|
|
|
_set_settings(EV::ClickHouse self, SV *href) |
|
7126
|
|
|
|
|
|
|
CODE: |
|
7127
|
|
|
|
|
|
|
{ |
|
7128
|
0
|
0
|
|
|
|
|
if (!(SvROK(href) && SvTYPE(SvRV(href)) == SVt_PVHV)) |
|
|
|
0
|
|
|
|
|
|
|
7129
|
0
|
|
|
|
|
|
croak("settings must be a HASH reference"); |
|
7130
|
0
|
0
|
|
|
|
|
if (self->default_settings) |
|
7131
|
0
|
|
|
|
|
|
SvREFCNT_dec((SV *)self->default_settings); |
|
7132
|
0
|
|
|
|
|
|
self->default_settings = (HV *)SvRV(href); |
|
7133
|
0
|
|
|
|
|
|
SvREFCNT_inc((SV *)self->default_settings); |
|
7134
|
|
|
|
|
|
|
} |
|
7135
|
|
|
|
|
|
|
|
|
7136
|
|
|
|
|
|
|
SV* |
|
7137
|
|
|
|
|
|
|
on_disconnect(EV::ClickHouse self, SV *handler = NULL) |
|
7138
|
|
|
|
|
|
|
CODE: |
|
7139
|
|
|
|
|
|
|
{ |
|
7140
|
0
|
|
|
|
|
|
RETVAL = handler_accessor(&self->on_disconnect, handler, items > 1); |
|
7141
|
|
|
|
|
|
|
} |
|
7142
|
|
|
|
|
|
|
OUTPUT: |
|
7143
|
|
|
|
|
|
|
RETVAL |
|
7144
|
|
|
|
|
|
|
|
|
7145
|
|
|
|
|
|
|
SV * |
|
7146
|
|
|
|
|
|
|
server_timezone(EV::ClickHouse self) |
|
7147
|
|
|
|
|
|
|
CODE: |
|
7148
|
|
|
|
|
|
|
{ |
|
7149
|
0
|
0
|
|
|
|
|
if (self->server_timezone) |
|
7150
|
0
|
|
|
|
|
|
RETVAL = newSVpv(self->server_timezone, 0); |
|
7151
|
|
|
|
|
|
|
else |
|
7152
|
0
|
|
|
|
|
|
RETVAL = &PL_sv_undef; |
|
7153
|
|
|
|
|
|
|
} |
|
7154
|
|
|
|
|
|
|
OUTPUT: |
|
7155
|
|
|
|
|
|
|
RETVAL |
|
7156
|
|
|
|
|
|
|
|
|
7157
|
|
|
|
|
|
|
void |
|
7158
|
|
|
|
|
|
|
_set_tls_skip_verify(EV::ClickHouse self, int val) |
|
7159
|
|
|
|
|
|
|
CODE: |
|
7160
|
|
|
|
|
|
|
{ |
|
7161
|
0
|
|
|
|
|
|
self->tls_skip_verify = val; |
|
7162
|
|
|
|
|
|
|
} |
|
7163
|
|
|
|
|
|
|
|
|
7164
|
|
|
|
|
|
|
void |
|
7165
|
|
|
|
|
|
|
_set_query_timeout(EV::ClickHouse self, NV val) |
|
7166
|
|
|
|
|
|
|
CODE: |
|
7167
|
|
|
|
|
|
|
{ |
|
7168
|
0
|
|
|
|
|
|
self->query_timeout = val; |
|
7169
|
|
|
|
|
|
|
} |
|
7170
|
|
|
|
|
|
|
|
|
7171
|
|
|
|
|
|
|
void |
|
7172
|
|
|
|
|
|
|
_set_auto_reconnect(EV::ClickHouse self, int val) |
|
7173
|
|
|
|
|
|
|
CODE: |
|
7174
|
|
|
|
|
|
|
{ |
|
7175
|
0
|
|
|
|
|
|
self->auto_reconnect = val; |
|
7176
|
|
|
|
|
|
|
} |
|
7177
|
|
|
|
|
|
|
|
|
7178
|
|
|
|
|
|
|
void |
|
7179
|
|
|
|
|
|
|
_set_decode_flags(EV::ClickHouse self, unsigned int flags) |
|
7180
|
|
|
|
|
|
|
CODE: |
|
7181
|
|
|
|
|
|
|
{ |
|
7182
|
0
|
|
|
|
|
|
self->decode_flags = (uint32_t)flags; |
|
7183
|
|
|
|
|
|
|
} |
|
7184
|
|
|
|
|
|
|
|
|
7185
|
|
|
|
|
|
|
SV * |
|
7186
|
|
|
|
|
|
|
column_names(EV::ClickHouse self) |
|
7187
|
|
|
|
|
|
|
CODE: |
|
7188
|
|
|
|
|
|
|
{ |
|
7189
|
0
|
0
|
|
|
|
|
if (self->native_col_names) |
|
7190
|
0
|
|
|
|
|
|
RETVAL = newRV_inc((SV*)self->native_col_names); |
|
7191
|
|
|
|
|
|
|
else |
|
7192
|
0
|
|
|
|
|
|
RETVAL = &PL_sv_undef; |
|
7193
|
|
|
|
|
|
|
} |
|
7194
|
|
|
|
|
|
|
OUTPUT: |
|
7195
|
|
|
|
|
|
|
RETVAL |
|
7196
|
|
|
|
|
|
|
|
|
7197
|
|
|
|
|
|
|
SV * |
|
7198
|
|
|
|
|
|
|
last_query_id(EV::ClickHouse self) |
|
7199
|
|
|
|
|
|
|
CODE: |
|
7200
|
|
|
|
|
|
|
{ |
|
7201
|
0
|
0
|
|
|
|
|
if (self->last_query_id) |
|
7202
|
0
|
|
|
|
|
|
RETVAL = newSVpv(self->last_query_id, 0); |
|
7203
|
|
|
|
|
|
|
else |
|
7204
|
0
|
|
|
|
|
|
RETVAL = &PL_sv_undef; |
|
7205
|
|
|
|
|
|
|
} |
|
7206
|
|
|
|
|
|
|
OUTPUT: |
|
7207
|
|
|
|
|
|
|
RETVAL |
|
7208
|
|
|
|
|
|
|
|
|
7209
|
|
|
|
|
|
|
SV * |
|
7210
|
|
|
|
|
|
|
last_error_code(EV::ClickHouse self) |
|
7211
|
|
|
|
|
|
|
CODE: |
|
7212
|
|
|
|
|
|
|
{ |
|
7213
|
0
|
|
|
|
|
|
RETVAL = newSViv(self->last_error_code); |
|
7214
|
|
|
|
|
|
|
} |
|
7215
|
|
|
|
|
|
|
OUTPUT: |
|
7216
|
|
|
|
|
|
|
RETVAL |
|
7217
|
|
|
|
|
|
|
|
|
7218
|
|
|
|
|
|
|
SV * |
|
7219
|
|
|
|
|
|
|
column_types(EV::ClickHouse self) |
|
7220
|
|
|
|
|
|
|
CODE: |
|
7221
|
|
|
|
|
|
|
{ |
|
7222
|
0
|
0
|
|
|
|
|
if (self->native_col_types) |
|
7223
|
0
|
|
|
|
|
|
RETVAL = newRV_inc((SV*)self->native_col_types); |
|
7224
|
|
|
|
|
|
|
else |
|
7225
|
0
|
|
|
|
|
|
RETVAL = &PL_sv_undef; |
|
7226
|
|
|
|
|
|
|
} |
|
7227
|
|
|
|
|
|
|
OUTPUT: |
|
7228
|
|
|
|
|
|
|
RETVAL |
|
7229
|
|
|
|
|
|
|
|
|
7230
|
|
|
|
|
|
|
SV * |
|
7231
|
|
|
|
|
|
|
last_totals(EV::ClickHouse self) |
|
7232
|
|
|
|
|
|
|
CODE: |
|
7233
|
|
|
|
|
|
|
{ |
|
7234
|
0
|
0
|
|
|
|
|
if (self->native_totals) |
|
7235
|
0
|
|
|
|
|
|
RETVAL = newRV_inc((SV*)self->native_totals); |
|
7236
|
|
|
|
|
|
|
else |
|
7237
|
0
|
|
|
|
|
|
RETVAL = &PL_sv_undef; |
|
7238
|
|
|
|
|
|
|
} |
|
7239
|
|
|
|
|
|
|
OUTPUT: |
|
7240
|
|
|
|
|
|
|
RETVAL |
|
7241
|
|
|
|
|
|
|
|
|
7242
|
|
|
|
|
|
|
SV * |
|
7243
|
|
|
|
|
|
|
last_extremes(EV::ClickHouse self) |
|
7244
|
|
|
|
|
|
|
CODE: |
|
7245
|
|
|
|
|
|
|
{ |
|
7246
|
0
|
0
|
|
|
|
|
if (self->native_extremes) |
|
7247
|
0
|
|
|
|
|
|
RETVAL = newRV_inc((SV*)self->native_extremes); |
|
7248
|
|
|
|
|
|
|
else |
|
7249
|
0
|
|
|
|
|
|
RETVAL = &PL_sv_undef; |
|
7250
|
|
|
|
|
|
|
} |
|
7251
|
|
|
|
|
|
|
OUTPUT: |
|
7252
|
|
|
|
|
|
|
RETVAL |
|
7253
|
|
|
|
|
|
|
|
|
7254
|
|
|
|
|
|
|
SV * |
|
7255
|
|
|
|
|
|
|
profile_rows_before_limit(EV::ClickHouse self) |
|
7256
|
|
|
|
|
|
|
CODE: |
|
7257
|
|
|
|
|
|
|
{ |
|
7258
|
0
|
|
|
|
|
|
RETVAL = newSVuv(self->profile_rows_before_limit); |
|
7259
|
|
|
|
|
|
|
} |
|
7260
|
|
|
|
|
|
|
OUTPUT: |
|
7261
|
|
|
|
|
|
|
RETVAL |
|
7262
|
|
|
|
|
|
|
|
|
7263
|
|
|
|
|
|
|
SV * |
|
7264
|
|
|
|
|
|
|
profile_rows(EV::ClickHouse self) |
|
7265
|
|
|
|
|
|
|
CODE: |
|
7266
|
|
|
|
|
|
|
{ |
|
7267
|
0
|
|
|
|
|
|
RETVAL = newSVuv(self->profile_rows); |
|
7268
|
|
|
|
|
|
|
} |
|
7269
|
|
|
|
|
|
|
OUTPUT: |
|
7270
|
|
|
|
|
|
|
RETVAL |
|
7271
|
|
|
|
|
|
|
|
|
7272
|
|
|
|
|
|
|
SV * |
|
7273
|
|
|
|
|
|
|
profile_bytes(EV::ClickHouse self) |
|
7274
|
|
|
|
|
|
|
CODE: |
|
7275
|
|
|
|
|
|
|
{ |
|
7276
|
0
|
|
|
|
|
|
RETVAL = newSVuv(self->profile_bytes); |
|
7277
|
|
|
|
|
|
|
} |
|
7278
|
|
|
|
|
|
|
OUTPUT: |
|
7279
|
|
|
|
|
|
|
RETVAL |
|
7280
|
|
|
|
|
|
|
|
|
7281
|
|
|
|
|
|
|
SV* |
|
7282
|
|
|
|
|
|
|
on_trace(EV::ClickHouse self, SV *handler = NULL) |
|
7283
|
|
|
|
|
|
|
CODE: |
|
7284
|
|
|
|
|
|
|
{ |
|
7285
|
0
|
|
|
|
|
|
RETVAL = handler_accessor(&self->on_trace, handler, items > 1); |
|
7286
|
|
|
|
|
|
|
} |
|
7287
|
|
|
|
|
|
|
OUTPUT: |
|
7288
|
|
|
|
|
|
|
RETVAL |
|
7289
|
|
|
|
|
|
|
|
|
7290
|
|
|
|
|
|
|
void |
|
7291
|
|
|
|
|
|
|
_set_keepalive(EV::ClickHouse self, double val) |
|
7292
|
|
|
|
|
|
|
CODE: |
|
7293
|
|
|
|
|
|
|
{ |
|
7294
|
0
|
|
|
|
|
|
self->keepalive = val; |
|
7295
|
|
|
|
|
|
|
} |
|
7296
|
|
|
|
|
|
|
|
|
7297
|
|
|
|
|
|
|
void |
|
7298
|
|
|
|
|
|
|
_set_reconnect_delay(EV::ClickHouse self, double val) |
|
7299
|
|
|
|
|
|
|
CODE: |
|
7300
|
|
|
|
|
|
|
{ |
|
7301
|
0
|
|
|
|
|
|
self->reconnect_delay = val; |
|
7302
|
|
|
|
|
|
|
} |
|
7303
|
|
|
|
|
|
|
|
|
7304
|
|
|
|
|
|
|
void |
|
7305
|
|
|
|
|
|
|
_set_reconnect_max_delay(EV::ClickHouse self, double val) |
|
7306
|
|
|
|
|
|
|
CODE: |
|
7307
|
|
|
|
|
|
|
{ |
|
7308
|
0
|
|
|
|
|
|
self->reconnect_max_delay = val; |
|
7309
|
|
|
|
|
|
|
} |
|
7310
|
|
|
|
|
|
|
|
|
7311
|
|
|
|
|
|
|
void |
|
7312
|
|
|
|
|
|
|
drain(EV::ClickHouse self, SV *cb) |
|
7313
|
|
|
|
|
|
|
CODE: |
|
7314
|
|
|
|
|
|
|
{ |
|
7315
|
0
|
0
|
|
|
|
|
if (!(SvROK(cb) && SvTYPE(SvRV(cb)) == SVt_PVCV)) |
|
|
|
0
|
|
|
|
|
|
|
7316
|
0
|
|
|
|
|
|
croak("drain callback must be a CODE reference"); |
|
7317
|
0
|
0
|
|
|
|
|
if (self->on_drain) SvREFCNT_dec(self->on_drain); |
|
7318
|
0
|
0
|
|
|
|
|
if (self->pending_count == 0 && ngx_queue_empty(&self->send_queue)) { |
|
|
|
0
|
|
|
|
|
|
|
7319
|
|
|
|
|
|
|
/* Nothing pending — fire immediately */ |
|
7320
|
0
|
|
|
|
|
|
self->on_drain = NULL; |
|
7321
|
|
|
|
|
|
|
{ |
|
7322
|
0
|
|
|
|
|
|
dSP; |
|
7323
|
0
|
|
|
|
|
|
ENTER; |
|
7324
|
0
|
|
|
|
|
|
SAVETMPS; |
|
7325
|
0
|
0
|
|
|
|
|
PUSHMARK(SP); |
|
7326
|
0
|
|
|
|
|
|
PUTBACK; |
|
7327
|
0
|
|
|
|
|
|
self->callback_depth++; |
|
7328
|
0
|
|
|
|
|
|
call_sv(cb, G_DISCARD | G_EVAL); |
|
7329
|
0
|
|
|
|
|
|
self->callback_depth--; |
|
7330
|
0
|
0
|
|
|
|
|
if (SvTRUE(ERRSV)) |
|
|
|
0
|
|
|
|
|
|
|
7331
|
0
|
0
|
|
|
|
|
warn("EV::ClickHouse: drain callback died: %s", |
|
7332
|
|
|
|
|
|
|
SvPV_nolen(ERRSV)); |
|
7333
|
0
|
0
|
|
|
|
|
FREETMPS; |
|
7334
|
0
|
|
|
|
|
|
LEAVE; |
|
7335
|
|
|
|
|
|
|
} |
|
7336
|
0
|
|
|
|
|
|
check_destroyed(self); |
|
7337
|
|
|
|
|
|
|
} else { |
|
7338
|
0
|
|
|
|
|
|
self->on_drain = SvREFCNT_inc(cb); |
|
7339
|
|
|
|
|
|
|
} |
|
7340
|
|
|
|
|
|
|
} |
|
7341
|
|
|
|
|
|
|
|
|
7342
|
|
|
|
|
|
|
void |
|
7343
|
|
|
|
|
|
|
cancel(EV::ClickHouse self) |
|
7344
|
|
|
|
|
|
|
CODE: |
|
7345
|
|
|
|
|
|
|
{ |
|
7346
|
0
|
0
|
|
|
|
|
if (self->protocol == PROTO_NATIVE && self->send_count > 0) { |
|
|
|
0
|
|
|
|
|
|
|
7347
|
|
|
|
|
|
|
/* Send CLIENT_CANCEL packet */ |
|
7348
|
|
|
|
|
|
|
native_buf_t pkt; |
|
7349
|
0
|
|
|
|
|
|
nbuf_init(&pkt); |
|
7350
|
0
|
|
|
|
|
|
nbuf_varuint(&pkt, CLIENT_CANCEL); |
|
7351
|
0
|
|
|
|
|
|
ensure_send_cap(self, self->send_len + pkt.len); |
|
7352
|
0
|
|
|
|
|
|
Copy(pkt.data, self->send_buf + self->send_len, pkt.len, char); |
|
7353
|
0
|
|
|
|
|
|
self->send_len += pkt.len; |
|
7354
|
0
|
|
|
|
|
|
Safefree(pkt.data); |
|
7355
|
0
|
0
|
|
|
|
|
if (!self->writing) start_writing(self); |
|
7356
|
|
|
|
|
|
|
/* We still need to wait for EndOfStream or Exception from server */ |
|
7357
|
0
|
0
|
|
|
|
|
} else if (self->protocol == PROTO_HTTP && self->send_count > 0) { |
|
|
|
0
|
|
|
|
|
|
|
7358
|
|
|
|
|
|
|
/* HTTP: close connection to cancel */ |
|
7359
|
0
|
0
|
|
|
|
|
if (self->native_rows) { |
|
7360
|
0
|
|
|
|
|
|
SvREFCNT_dec((SV*)self->native_rows); |
|
7361
|
0
|
|
|
|
|
|
self->native_rows = NULL; |
|
7362
|
|
|
|
|
|
|
} |
|
7363
|
0
|
0
|
|
|
|
|
if (cancel_pending(self, "query cancelled")) return; |
|
7364
|
0
|
|
|
|
|
|
cleanup_connection(self); |
|
7365
|
0
|
0
|
|
|
|
|
if (self->auto_reconnect && self->host) |
|
|
|
0
|
|
|
|
|
|
|
7366
|
0
|
|
|
|
|
|
schedule_reconnect(self); |
|
7367
|
|
|
|
|
|
|
} |
|
7368
|
|
|
|
|
|
|
} |