File Coverage

Client.xs
Criterion Covered Total %
statement 78 127 61.4
branch 34 80 42.5
condition n/a
subroutine n/a
pod n/a
total 112 207 54.1


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT
2             #include "EXTERN.h"
3             #include "perl.h"
4             #include "XSUB.h"
5             #include "ppport.h"
6              
7             #include
8             #include "define.h"
9             #include "type.h"
10             #include "proto.h"
11             #include "decode.h"
12             #include "encode.h"
13              
14             typedef struct {
15             int column_count;
16             int uniq_column_count;
17             struct cc_column *columns;
18             } Cassandra__Client__RowMeta;
19              
20             MODULE = Cassandra::Client PACKAGE = Cassandra::Client::Protocol
21             PROTOTYPES: DISABLE
22              
23             void
24             unpack_metadata(protocol_version, is_result, data)
25             int protocol_version
26             int is_result
27             SV *data
28             PPCODE:
29             STRLEN pos, size;
30             unsigned char *ptr;
31             int32_t flags, column_count, uniq_column_count;
32             Cassandra__Client__RowMeta *row_meta;
33              
34 96           ST(0) = &PL_sv_undef; /* Will have our RowMeta instance */
35 96           ST(1) = &PL_sv_undef; /* Will have our paging state */
36              
37 96           ptr = (unsigned char*)SvPV(data, size);
38 96           pos = 0;
39              
40 96 50         if (UNLIKELY(!ptr))
41 0           croak("Missing data argument to unpack_metadata");
42 96 50         if (UNLIKELY(protocol_version != 3 && protocol_version != 4))
    50          
43 0           croak("Invalid protocol version");
44              
45 96           flags = unpack_int(aTHX_ ptr, size, &pos);
46 96           column_count = unpack_int(aTHX_ ptr, size, &pos);
47              
48 96 50         if (protocol_version >= 4 && !is_result) {
    50          
49             int i, pk_count;
50              
51 0           pk_count = unpack_int(aTHX_ ptr, size, &pos);
52 0 0         if (UNLIKELY(pk_count < 0))
53 0           croak("Protocol error: pk_count<0");
54              
55 0 0         for (i = 0; i < pk_count; i++) {
56             // Read the short, but throw it away for now.
57 0           unpack_short(aTHX_ ptr, size, &pos);
58             }
59             }
60              
61 96 50         if (UNLIKELY(flags < 0 || flags > 7))
    50          
62 0           croak("Invalid protocol data passed to unpack_metadata (reason: invalid flags)");
63 96 50         if (UNLIKELY(column_count < 0))
64 0           croak("Invalid protocol data passed to unpack_metadata (reason: invalid column count)");
65              
66 96 50         if (flags & CC_METADATA_FLAG_HAS_MORE_PAGES) {
67 0           ST(1) = unpack_bytes_sv(aTHX_ ptr, size, &pos);
68 0           sv_2mortal(ST(1));
69             }
70              
71 96 50         if (!(flags & CC_METADATA_FLAG_NO_METADATA)) {
72             int i, have_global_spec;
73             SV *global_keyspace, *global_table;
74             HV *name_hash;
75              
76 96           have_global_spec = flags & CC_METADATA_FLAG_GLOBAL_TABLES_SPEC;
77              
78 96 50         if (have_global_spec) {
79 0           global_keyspace = unpack_string_sv(aTHX_ ptr, size, &pos);
80 0           sv_2mortal(global_keyspace);
81 0           global_table = unpack_string_sv(aTHX_ ptr, size, &pos);
82 0           sv_2mortal(global_table);
83             }
84              
85 96           Newxz(row_meta, 1, Cassandra__Client__RowMeta);
86 96           ST(0) = sv_newmortal();
87 96           sv_setref_pv(ST(0), "Cassandra::Client::RowMetaPtr", (void*)row_meta);
88              
89 96 50         if (UNLIKELY(column_count > size))
90 0           croak("Invalid protocol data passed to unpack_metadata (reason: column count unlikely)");
91              
92 96           row_meta->column_count = column_count;
93 96           Newxz(row_meta->columns, column_count, struct cc_column);
94              
95 96           name_hash = (HV*)sv_2mortal( (SV*)newHV() );
96 96           uniq_column_count = 0;
97              
98 407 100         for (i = 0; i < column_count; i++) {
99 311           struct cc_column *column = &(row_meta->columns[i]);
100 311 50         if (have_global_spec) {
101 0           column->keyspace = global_keyspace;
102 0           SvREFCNT_inc(column->keyspace);
103 0           column->table = global_table;
104 0           SvREFCNT_inc(column->table);
105             } else {
106 311           column->keyspace = unpack_string_sv(aTHX_ ptr, size, &pos);
107 311           column->table = unpack_string_sv(aTHX_ ptr, size, &pos);
108             }
109              
110 311           column->name = unpack_string_sv_hash(aTHX_ ptr, size, &pos, &column->name_hash);
111 311           unpack_type(aTHX_ ptr, size, &pos, &column->type);
112 311 50         if (!hv_exists_ent(name_hash, column->name, column->name_hash)) {
113 311           uniq_column_count++;
114 311           hv_store_ent(name_hash, column->name, &PL_sv_undef, column->name_hash);
115             }
116             }
117              
118 96           row_meta->uniq_column_count = uniq_column_count;
119             }
120              
121 96           sv_chop(data, (char*)ptr+pos);
122              
123 96           XSRETURN(2);
124              
125             MODULE = Cassandra::Client PACKAGE = Cassandra::Client::RowMetaPtr
126              
127             AV*
128             decode(self, data, use_hashes)
129             Cassandra::Client::RowMeta *self
130             SV *data
131             int use_hashes
132             CODE:
133             STRLEN size, pos;
134             unsigned char *ptr;
135             int32_t row_count;
136             int i, j, col_count;
137             struct cc_column *columns;
138              
139 30           RETVAL = newAV();
140 30           sv_2mortal((SV*)RETVAL); /* work around a bug in perl */
141              
142 30           ptr = (unsigned char*)SvPV(data, size);
143 30           pos = 0;
144              
145 30 50         if (UNLIKELY(!ptr))
146 0           croak("Invalid input to decode()");
147              
148 30           col_count = self->column_count;
149 30           columns = self->columns;
150              
151 30           row_count = unpack_int(aTHX_ ptr, size, &pos);
152              
153             /* This came up while fuzzing: when we have 1000000 rows but no columns, we
154             * just flood the memory with empty arrays/hashes. Let's just reject this
155             * corner case. If you need this, please contact the author! */
156 30 50         if (UNLIKELY(row_count > 1000 && !col_count))
    0          
157 0           croak("Refusing to decode %d rows without known column information", row_count);
158              
159 60 100         for (i = 0; i < row_count; i++) {
160 30 50         if (use_hashes) {
161 0           HV *this_row = newHV();
162 0           av_push(RETVAL, newRV_noinc((SV*)this_row));
163              
164 0 0         for (j = 0; j < col_count; j++) {
165 0           SV *decoded = newSV(0);
166 0           hv_store_ent(this_row, columns[j].name, decoded, columns[j].name_hash);
167              
168 0           decode_cell(aTHX_ ptr, size, &pos, &columns[j].type, decoded);
169             }
170              
171             } else {
172 30           AV *this_row = newAV();
173 30           av_push(RETVAL, newRV_noinc((SV*)this_row));
174              
175 275 100         for (j = 0; j < col_count; j++) {
176 245           SV *decoded = newSV(0);
177 245           av_push(this_row, decoded);
178              
179 245           decode_cell(aTHX_ ptr, size, &pos, &columns[j].type, decoded);
180             }
181             }
182             }
183              
184             OUTPUT:
185             RETVAL
186              
187             SV*
188             encode(self, row)
189             Cassandra::Client::RowMeta *self
190             SV* row
191             CODE:
192             int column_count, i, use_hash;
193             STRLEN size_estimate;
194             AV *row_a;
195             HV *row_h;
196              
197 96 50         if (UNLIKELY(row == NULL))
198 0           croak("row must be passed");
199 96 50         if (UNLIKELY(!SvROK(row)))
200 0           croak("encode: argument must be a reference");
201              
202 96           column_count = self->column_count;
203              
204 96 50         if (SvTYPE(SvRV(row)) == SVt_PVAV) {
205 96           row_a = (AV*)SvRV(row);
206 96           use_hash = 0;
207 96 50         if (UNLIKELY((av_len(row_a)+1) != column_count))
208 0           croak("row encoder expected %d column(s), but got %d", column_count, ((int)av_len(row_a))+1);
209              
210 0 0         } else if (SvTYPE(SvRV(row)) == SVt_PVHV) {
211 0           row_h = (HV*)SvRV(row);
212 0           use_hash = 1;
213 0 0         if (UNLIKELY(HvUSEDKEYS(row_h) != self->uniq_column_count))
    0          
214 0 0         croak("row encoder expected %d column(s), but got %d", self->uniq_column_count, (int)HvUSEDKEYS(row_h));
215              
216             } else {
217 0           croak("encode: argument must be an ARRAY or HASH reference");
218             }
219              
220             /* Rough estimate. We only use it to predict Sv size, we don't rely on it being accurate.
221             If we overshoot, we waste some memory, and if we undershoot we copy a bit too often. */
222 96           size_estimate = 2 + (column_count * 12);
223 96 50         if (size_estimate <= 0) /* overflows aren't impossible, I guess */
224 0           size_estimate = 0; /* wing it */
225              
226 96           RETVAL = newSV(size_estimate);
227 96           sv_setpvn(RETVAL, "", 0);
228 96           pack_short(aTHX_ RETVAL, column_count);
229              
230 96 50         if (!use_hash) {
231 407 100         for (i = 0; i < column_count; i++) {
232 311           SV **maybe_cell = av_fetch(row_a, i, 0);
233 311 50         if (UNLIKELY(maybe_cell == NULL))
234 0           croak("row encoder error. bailing out");
235 311           encode_cell(aTHX_ RETVAL, *maybe_cell, &self->columns[i].type);
236             }
237              
238             } else {
239 0 0         for (i = 0; i < column_count; i++) {
240             struct cc_column *column;
241             HE *ent;
242              
243 0           column = &self->columns[i];
244 0           ent = hv_fetch_ent(row_h, column->name, 0, column->name_hash);
245 0 0         if (UNLIKELY(!ent)) {
246 0           croak("missing value for required entry <%s>", SvPV_nolen(column->name));
247             }
248              
249 0           encode_cell(aTHX_ RETVAL, HeVAL(ent), &column->type);
250             }
251             }
252              
253             OUTPUT:
254             RETVAL
255              
256             AV*
257             column_names(self)
258             Cassandra::Client::RowMeta *self
259             CODE:
260             int i;
261              
262 0           RETVAL = newAV();
263 0           sv_2mortal((SV*)RETVAL); /* work around a bug in perl */
264              
265 0 0         for (i = 0; i < self->column_count; i++) {
266 0           av_push(RETVAL, SvREFCNT_inc(self->columns[i].name));
267             }
268             OUTPUT:
269             RETVAL
270              
271             void
272             DESTROY(self)
273             Cassandra::Client::RowMeta *self
274             CODE:
275             int i;
276 407 100         for (i = 0; i < self->column_count; i++) {
277 311           struct cc_column *column = &(self->columns[i]);
278 311           SvREFCNT_dec(column->keyspace);
279 311           SvREFCNT_dec(column->table);
280 311           SvREFCNT_dec(column->name);
281 311           cc_type_destroy(aTHX_ &column->type);
282             }
283 96           Safefree(self->columns);
284 96           Safefree(self);