Class: ChConnect::NativeClient
- Inherits:
-
Object
- Object
- ChConnect::NativeClient
- Defined in:
- ext/ch_connect_native/ch_connect_native.c
Constant Summary collapse
- LZ4_AVAILABLE =
Qfalse- ZSTD_AVAILABLE =
Qfalse
Instance Method Summary collapse
- #broken? ⇒ Boolean
- #close ⇒ Object
- #feed(bytes) ⇒ Object
- #handshake_step ⇒ Object
- #initialize(database, username, password, compression) ⇒ Object constructor
- #recv_step ⇒ Object
- #send_query(sql, params, settings) ⇒ Object
- #take_output ⇒ Object
- #take_result ⇒ Object
Constructor Details
#initialize(database, username, password, compression) ⇒ Object
569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 |
# File 'ext/ch_connect_native/ch_connect_native.c', line 569
static VALUE
native_client_initialize(VALUE self, VALUE database, VALUE username, VALUE password, VALUE compression)
{
native_client_t *nc = get_client(self);
Check_Type(database, T_STRING);
if (!NIL_P(username)) Check_Type(username, T_STRING);
if (!NIL_P(password)) Check_Type(password, T_STRING);
chc_compression compression_code;
if (NIL_P(compression)) compression_code = CHC_COMP_NONE;
else if (compression == sym_lz4) compression_code = CHC_COMP_LZ4;
else if (compression == sym_zstd) compression_code = CHC_COMP_ZSTD;
else rb_raise(rb_eArgError, "unknown native compression");
nc->alloc = chc_alloc_stdlib();
/* init copies the strings and performs no I/O */
chc_client_opts opts = {
.client_name = "ch_connect",
.database = StringValueCStr(database),
.user = NIL_P(username) ? NULL : StringValueCStr(username),
.password = NIL_P(password) ? NULL : StringValueCStr(password),
.codec = &g_codec,
.compression = compression_code,
};
chc_err err = {0};
if (chc_async_client_init(&nc->ac, &opts, &nc->alloc, &err) != CHC_OK) {
rb_raise(eConnectionError, "%s", err.msg);
}
nc->state = NATIVE_ACTIVE; /* handshake has not completed yet */
return self;
}
|
Instance Method Details
#broken? ⇒ Boolean
913 914 915 916 917 918 |
# File 'ext/ch_connect_native/ch_connect_native.c', line 913
static VALUE
native_client_broken_p(VALUE self)
{
native_client_t *nc = get_client(self);
return (!nc->ac || nc->state != NATIVE_READY) ? Qtrue : Qfalse;
}
|
#close ⇒ Object
920 921 922 923 924 925 926 927 928 929 930 |
# File 'ext/ch_connect_native/ch_connect_native.c', line 920
static VALUE
native_client_close(VALUE self)
{
native_client_t *nc = get_client(self);
clear_pending(nc);
if (nc->ac) {
chc_async_client_free(nc->ac);
nc->ac = NULL;
}
return Qnil;
}
|
#feed(bytes) ⇒ Object
620 621 622 623 624 625 626 627 628 629 630 631 632 |
# File 'ext/ch_connect_native/ch_connect_native.c', line 620
static VALUE
native_client_feed(VALUE self, VALUE bytes)
{
native_client_t *nc = get_live_client(self);
Check_Type(bytes, T_STRING);
chc_err err = {0};
if (chc_async_submit(nc->ac, RSTRING_PTR(bytes), (size_t)RSTRING_LEN(bytes), &err) != CHC_OK) {
nc->state = NATIVE_BROKEN;
rb_raise(eConnectionError, "%s", err.msg);
}
return Qnil;
}
|
#handshake_step ⇒ Object
634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 |
# File 'ext/ch_connect_native/ch_connect_native.c', line 634
static VALUE
native_client_handshake_step(VALUE self)
{
native_client_t *nc = get_live_client(self);
nc->state = NATIVE_ACTIVE;
chc_err err = {0};
int rc = chc_async_handshake(nc->ac, &err);
if (rc == CHC_OK) {
nc->state = NATIVE_READY;
return sym_done;
}
if (rc == CHC_WOULD_BLOCK) return sym_want_read;
nc->state = NATIVE_BROKEN;
rb_raise(eConnectionError, "%s", err.msg);
}
|
#recv_step ⇒ Object
825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 |
# File 'ext/ch_connect_native/ch_connect_native.c', line 825
static VALUE
native_client_recv_step(VALUE self)
{
native_client_t *nc = get_live_client(self);
for (;;) {
clear_pending(nc);
chc_err err = {0};
int rc = chc_async_recv_packet(nc->ac, &nc->pending_pkt, &err);
if (rc == CHC_WOULD_BLOCK) return sym_want_read;
if (rc == CHC_ERR_TYPE) {
nc->state = NATIVE_BROKEN;
rb_raise(eUnsupportedTypeError, "Unsupported column type: %s", err.msg);
}
if (rc != CHC_OK) {
nc->state = NATIVE_BROKEN;
rb_raise(eConnectionError, "%s",
err.msg[0] ? err.msg : "connection lost while reading response");
}
chc_packet pkt = nc->pending_pkt; /* value alias; freed via clear */
if (pkt.kind == CHC_PKT_EXCEPTION) {
VALUE msg = rb_utf8_str_new(pkt.exception->display_text,
(long)pkt.exception->display_text_len);
clear_pending(nc);
rb_iv_set(self, "@result", Qnil);
/* A complete server exception terminates this query but leaves the
* native protocol synchronized and ready for the next query. */
nc->state = NATIVE_READY;
rb_exc_raise(rb_exc_new_str(eQueryError, msg));
}
if (pkt.kind == CHC_PKT_PROGRESS) {
nc->read_rows += pkt.progress.rows;
nc->read_bytes += pkt.progress.bytes;
if (pkt.progress.total_rows > nc->total_rows) nc->total_rows = pkt.progress.total_rows;
nc->written_rows += pkt.progress.written_rows;
nc->written_bytes += pkt.progress.written_bytes;
continue;
}
if (pkt.kind == CHC_PKT_PROFILE_INFO) {
nc->result_bytes = pkt.profile.bytes;
continue;
}
if (pkt.kind == CHC_PKT_DATA) {
native_client_decode_block(self, nc, pkt.block);
continue;
}
if (pkt.kind == CHC_PKT_END_OF_STREAM) {
clear_pending(nc);
nc->state = NATIVE_READY;
return sym_done;
}
/* PONG / TOTALS / EXTREMES / LOG / PROFILE_EVENTS — skip. */
}
}
|
#send_query(sql, params, settings) ⇒ Object
671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 |
# File 'ext/ch_connect_native/ch_connect_native.c', line 671
static VALUE
native_client_send_query(VALUE self, VALUE sql, VALUE params, VALUE settings)
{
native_client_t *nc = get_live_client(self);
if (nc->state != NATIVE_READY)
rb_raise(eConnectionError, "native connection is not ready for a query");
Check_Type(sql, T_STRING);
/* reset per-query state */
clear_pending(nc);
nc->state = NATIVE_ACTIVE;
nc->have_header = 0;
nc->read_rows = nc->read_bytes = nc->total_rows = 0;
nc->written_rows = nc->written_bytes = 0;
nc->result_bytes = 0;
VALUE result = rb_ary_new_capa(3);
rb_ary_push(result, rb_ary_new());
rb_ary_push(result, rb_ary_new());
rb_ary_push(result, rb_ary_new());
rb_iv_set(self, "@result", result);
if (!NIL_P(settings)) Check_Type(settings, T_ARRAY);
long n_settings = NIL_P(settings) ? 0 : RARRAY_LEN(settings);
if (!NIL_P(params)) Check_Type(params, T_ARRAY);
long n_params = NIL_P(params) ? 0 : RARRAY_LEN(params);
/* Coerce and validate every Ruby string before retaining any RSTRING_PTR.
* String coercion and rb_ary_push can allocate and trigger compacting GC;
* once this pass finishes, the pointer-building pass performs no Ruby
* allocation before chc_client_send_query_ex consumes the bytes. */
VALUE flat = rb_ary_new_capa(2 * (n_settings + n_params));
append_string_pairs(settings, n_settings, flat);
append_string_pairs(params, n_params, flat);
/* These counts come from public hashes and can be large. ALLOCV_N uses a
* small stack buffer or Ruby's heap without risking the native stack. Both
* buffers are allocated before retaining any movable Ruby string pointer. */
VALUE csettings_buf = 0, cparams_buf = 0;
chc_query_setting *csettings = ALLOCV_N(chc_query_setting, csettings_buf,
n_settings + 1);
chc_query_param *cparams = n_params > 0
? ALLOCV_N(chc_query_param, cparams_buf, n_params)
: NULL;
long n_total = 0;
for (long i = 0; i < n_settings; i++) {
VALUE sname = RARRAY_AREF(flat, 2 * i);
VALUE sval = RARRAY_AREF(flat, 2 * i + 1);
csettings[n_total++] = (chc_query_setting){
.name = RSTRING_PTR(sname), .value = RSTRING_PTR(sval)
};
}
/* Keep the decoder invariant last so a duplicate supplied through the
* low-level NativeClient API cannot override it. */
csettings[n_total++] = (chc_query_setting){
.name = "output_format_native_encode_types_in_binary_format", .value = "0"
};
chc_query_opts qopts = { .settings = csettings, .n_settings = (size_t)n_total };
if (n_params > 0) {
long base = 2 * n_settings;
for (long i = 0; i < n_params; i++) {
VALUE pname = RARRAY_AREF(flat, base + 2 * i);
VALUE pval = RARRAY_AREF(flat, base + 2 * i + 1);
cparams[i].name = RSTRING_PTR(pname);
cparams[i].value = RSTRING_PTR(pval);
}
qopts.params = cparams;
qopts.n_params = (size_t)n_params;
}
chc_err err = {0};
/* the async wrapper has no send_query_ex; call it on the embedded client
* (same out-sink io) so settings and params are included */
int rc = chc_client_send_query_ex(&nc->ac->cli, RSTRING_PTR(sql), (size_t)RSTRING_LEN(sql),
&qopts, &err);
ALLOCV_END(cparams_buf);
ALLOCV_END(csettings_buf);
RB_GC_GUARD(flat);
RB_GC_GUARD(sql);
if (rc != CHC_OK) {
nc->state = NATIVE_BROKEN;
rb_raise(eConnectionError, "%s", err.msg);
}
return Qnil;
}
|
#take_output ⇒ Object
604 605 606 607 608 609 610 611 612 613 614 615 616 617 |
# File 'ext/ch_connect_native/ch_connect_native.c', line 604
static VALUE
native_client_take_output(VALUE self)
{
native_client_t *nc = get_live_client(self);
const uint8_t *buf = NULL;
size_t len = 0;
chc_async_pending_out(nc->ac, &buf, &len);
if (len == 0) return Qnil;
VALUE out = rb_str_new((const char *)buf, (long)len);
chc_async_consume_out(nc->ac, len);
return out;
}
|
#take_result ⇒ Object
887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 |
# File 'ext/ch_connect_native/ch_connect_native.c', line 887
static VALUE
native_client_take_result(VALUE self)
{
native_client_t *nc = get_client(self);
VALUE result = rb_iv_get(self, "@result");
if (NIL_P(result))
rb_raise(eConnectionError, "no completed native result is available");
VALUE rows = RARRAY_AREF(result, 2);
VALUE summary = rb_hash_new();
rb_hash_aset(summary, sym_read_rows, ULL2NUM(nc->read_rows));
rb_hash_aset(summary, sym_read_bytes, ULL2NUM(nc->read_bytes));
rb_hash_aset(summary, sym_written_rows, ULL2NUM(nc->written_rows));
rb_hash_aset(summary, sym_written_bytes, ULL2NUM(nc->written_bytes));
rb_hash_aset(summary, sym_total_rows_to_read, ULL2NUM(nc->total_rows));
rb_hash_aset(summary, sym_result_rows, LONG2NUM(RARRAY_LEN(rows)));
rb_hash_aset(summary, sym_result_bytes, ULL2NUM(nc->result_bytes));
rb_ary_push(result, summary);
/* drop the references so a pooled idle connection doesn't pin the last
* result set until its next query */
rb_iv_set(self, "@result", Qnil);
return result;
}
|