1 2 /* 3 * array.c 4 * Type handler for the array data type. 5 * 6 * Copyright (c) 2009 eSilo, LLC. All rights reserved. 7 * This is free software; see the source for copying conditions. There is 8 * NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR 9 * PURPOSE. 10 */ 11 12 #include "libpqtypes-int.h" 13 14 int 15 pqt_put_array(PGtypeArgs *args) 16 { 17 int i; 18 int hasnull=0; 19 int ndims; 20 int nitems; 21 int arrsize; 22 char *out; 23 int lbound[MAXDIM]; 24 int dims[MAXDIM]; 25 PGarray *arr = va_arg(args->ap, PGarray *); 26 27 PUTNULLCHK(args, arr); 28 29 if (arr->ndims < 0) 30 return args->errorf(args, "arr.ndims is invalid - %d", arr->ndims); 31 32 /* auto configure when ndims is 0 to 1d array */ 33 if (arr->ndims == 0) 34 { 35 ndims = 1; 36 dims[0] = arr->param->vcnt; 37 lbound[0] = 1; 38 } 39 else 40 { 41 ndims = arr->ndims; 42 memcpy(lbound, arr->lbound, sizeof(lbound)); 43 memcpy(dims, arr->dims, sizeof(dims)); 44 } 45 46 nitems = 1; 47 for (i=0; i < ndims; i++) 48 nitems *= dims[i]; 49 50 /* make sure array is on the same page as the param */ 51 if (nitems != arr->param->vcnt) 52 return args->errorf(args, 53 "param element count %d is different than array's %d", 54 arr->param->vcnt, nitems); 55 56 /* header: ndims + hasnull + elemtype + ((dims + lbound) * ndims) */ 57 arrsize = 4 + 4 + 4 + (8 * ndims); 58 59 /* compute data length, also get the hasnull flag */ 60 for (i=0; i < arr->param->vcnt; i++) 61 { 62 if (arr->param->vals[i].format == 0) 63 return args->errorf(args, "Cannot put array elements in text format"); 64 65 arrsize += 4; 66 if (arr->param->vals[i].datal == NULL_LEN) 67 hasnull = 1; 68 else 69 arrsize += arr->param->vals[i].datal; 70 } 71 72 /* make sure args->put.out is large enough */ 73 if (args->put.expandBuffer(args, arrsize) == -1) 74 return -1; 75 76 out = args->put.out; 77 78 /* number od dimensions */ 79 pqt_buf_putint4(out, ndims); 80 out += 4; 81 82 /* array hasnull flag */ 83 pqt_buf_putint4(out, hasnull); 84 out += 4; 85 86 /* array element oid */ 87 pqt_buf_putint4(out, args->typhandler->typoid); 88 out += 4; 89 90 /* dims and lbound */ 91 for (i=0; i < ndims; i++) 92 { 93 pqt_buf_putint4(out, dims[i]); 94 out += 4; 95 96 pqt_buf_putint4(out, lbound[i]); 97 out += 4; 98 } 99 100 /* write the element lengths and data */ 101 for (i=0; i < arr->param->vcnt; i++) 102 { 103 pqt_buf_putint4(out, arr->param->vals[i].datal); 104 out += 4; 105 106 if (arr->param->vals[i].datal > 0) 107 { 108 memcpy(out, arr->param->vals[i].data, arr->param->vals[i].datal); 109 out += arr->param->vals[i].datal; 110 } 111 } 112 113 return arrsize; 114 } 115 116 int 117 pqt_get_array(PGtypeArgs *args) 118 { 119 int i,t; 120 int vlen; 121 int ntups; 122 int nattrs; 123 Oid elemoid; 124 DECLVALUE(args); 125 PGresult *res; 126 int first_tup; 127 PGarray *arr = va_arg(args->ap, PGarray *); 128 129 CHKGETVALS(args, arr); 130 131 if (args->format == TEXTFMT) 132 return args->errorf(args, "array does not support text results"); 133 134 /* number of dims */ 135 arr->ndims = pqt_buf_getint4(value); 136 value += 4; 137 138 /* skip NULL flag */ 139 value += 4; 140 141 /* check the element oid */ 142 elemoid = (Oid)pqt_buf_getint4(value); 143 if (elemoid != args->typhandler->typoid) 144 return args->errorf(args, 145 "array element type %u is different than what server says %u", 146 args->typhandler->typoid, elemoid); 147 value += 4; 148 149 /* arr dims and lbounds */ 150 first_tup = 1; 151 for (i=0, ntups=1; i < arr->ndims; i++) 152 { 153 arr->dims[i] = pqt_buf_getint4(value); 154 value += 4; 155 156 arr->lbound[i] = pqt_buf_getint4(value); 157 value += 4; 158 159 ntups *= arr->dims[i]; 160 } 161 162 /* This means ndims is zero because the above loop never iterated. */ 163 if (i == 0) 164 ntups = 0; 165 166 /* numTuples is the number of array items 167 * and numAttributes is 1 for non-composites. 168 */ 169 nattrs = (args->typhandler->nattrs > 0) ? args->typhandler->nattrs : 1; 170 171 if (!(res = pqt_copyresult(args, nattrs))) 172 RERR_MEM(args); 173 174 for (t=0; t < ntups; t++) 175 { 176 /* get the value len */ 177 vlen = pqt_buf_getint4(value); 178 value += 4; 179 180 /* Regular Array with 1 attr per tuple */ 181 if (args->typhandler->nattrs == 0) 182 { 183 /* set the field value */ 184 if (!PQsetvalue(res, t, 0, value, vlen)) 185 { 186 PQclear(res); 187 return -1; 188 } 189 190 if (vlen > 0) 191 value += vlen; 192 193 continue; 194 } 195 196 /* ------------------------ 197 * COMPOSITE/RECORD 198 */ 199 200 /* NULL compsoite array item, fill in attrs with NULL */ 201 if (vlen == NULL_LEN) 202 { 203 int x; 204 205 for (x=0; x < nattrs; x++) 206 { 207 if (!PQsetvalue(res, t, x, NULL, NULL_LEN)) 208 { 209 PQclear(res); 210 return -1; 211 } 212 } 213 214 /* move on to next tuple, done here. */ 215 continue; 216 } 217 218 /* verify that server's attr count matches ours */ 219 if (first_tup) 220 { 221 int attcnt = pqt_buf_getint4(value); 222 223 /* watch for invalidation issues */ 224 if (attcnt != nattrs) 225 { 226 PQclear(res); 227 return args->errorf(args, 228 "type handler attribute count is %d but server says it's %d", 229 args->typhandler->nattrs, attcnt); 230 } 231 } 232 233 /* skip attr count */ 234 value += 4; 235 236 /* composite attributes (record columns) */ 237 for (i=0; i < nattrs; i++) 238 { 239 /* watch for invalidation issues */ 240 if (first_tup && 241 (Oid) pqt_buf_getint4(value) != args->typhandler->attDescs[i].attoid) 242 { 243 Oid server_oid = (Oid) pqt_buf_getint4(value); 244 245 args->errorf(args, 246 "type handler attribute OID is %u but server says it's %u", 247 args->typhandler->attDescs[i].attoid, server_oid); 248 249 PQclear(res); 250 return -1; 251 } 252 253 /* skip oid */ 254 value += 4; 255 256 /* get the value length */ 257 vlen = pqt_buf_getint4(value); 258 value += 4; 259 260 /* set the field value */ 261 if (!PQsetvalue(res, t, i, value, vlen)) 262 { 263 PQclear(res); 264 return -1; 265 } 266 267 if (vlen > 0) 268 value += vlen; 269 } 270 271 first_tup = 0; 272 } 273 274 arr->res = res; 275 return 0; 276 } |