int PQclearSpecs(PGconn *conn) { PGtypeData *typeData; if (!conn) { PQseterror("PGConn cannot be NULL"); return FALSE; } typeData = PQinstanceData(conn, pqt_eventproc); if (!typeData) { PQseterror("No type data exists for PGconn at %p", conn); return FALSE; } pqt_freespecs(typeData->typspecs, typeData->typspeccnt); typeData->typspecs = NULL; typeData->typspeccnt = 0; typeData->typspecmax = 0; return TRUE; }
/* wraps pqt_parsetype to toggle out illegal flags during a register */ static char *parseType(const char *spec, char *typschema, char *typname, int typpos) { char *s; int flags; if (!(s = pqt_parsetype(spec, typschema, typname, &flags, typpos))) return NULL; if (flags & TYPFLAG_INVALID) return NULL; if (flags & TYPFLAG_ARRAY) { PQseterror("Cannot use an array[] during a type handler registration."); return NULL; } if (flags & TYPFLAG_POINTER) { PQseterror("Cannot use a type* during a type handler registration."); return NULL; } return s; }
static int performRegisterQuery(PGconn *conn, int which, PGregisterType *types, int count, PGresult **res) { int n = FALSE; PGtypeData *connData; PGarray names; PGarray schemas; int want_attrs = which == PQT_COMPOSITE; if (!(connData = (PGtypeData *) PQinstanceData(conn, pqt_eventproc))) { PQseterror("PGconn is missing event data"); return FALSE; } if (!getTypeParams(conn, types, count, &names, &schemas)) return FALSE; if (res) *res = execLookupTypes(conn, connData, &schemas, &names, want_attrs); else n = sendLookupTypes(conn, connData, &schemas, &names, want_attrs); PQparamClear(names.param); PQparamClear(schemas.param); if (res) return *res ? TRUE : FALSE; return n; }
static int checkTypeLookups(PGresult *res, PGregisterType *types, int count) { int i; int ntups = PQntuples(res); /* The tuple count must match the requested count. The server omits * tuples for types it did not find. For those it did find, it returns * a sequenctial index. The first gap found is our missing type. This * only reports about the first missing type. */ if (ntups == count) return TRUE; for (i=0; i < ntups; i++) { int idx; if (!PQgetf(res, i, "%int4", 0, &idx)) return FALSE; /* 'i' should always match idx-1, postgresql arrays are 1-based. * This is a missing type, first gap in the sequence. */ if (i != idx-1) break; } PQseterror("server type lookup failed: could not find '%s'", types[i].typname); return FALSE; }
/* This is part of a performance enhancement for getting arrays * and/or composites. They require generating PGresults which * causes pqt_duphandlers() to run. Its amazing how much a simple * malloc+memcpy costs after around 10000 or so. The common case * avoids this by using a fixed length PGrecordAttDesc buffer. If * there are a large number of attributes, the slower path is used. * Again this is small, maybe 10% of the overall 63% win. */ static PGrecordAttDesc *initAttDescs(PGtypeHandler *h, char *attrs) { char *p; int nattrs = 1; PGrecordAttDesc *attDescs; for(p = attrs; *p; nattrs++, ++p) if(!(p = strchr(p, ' '))) break; if (nattrs < (int) (sizeof(h->attDescsBuf) / sizeof(h->attDescsBuf[0]))) { h->freeAttDescs = 0; attDescs = h->attDescsBuf; } else { attDescs = (PGrecordAttDesc *) malloc(nattrs * sizeof(PGrecordAttDesc)); if (!attDescs) { PQseterror(PQT_OUTOFMEMORY); return NULL; } h->freeAttDescs = 1; } return attDescs; }
static int expandSpecs(PGtypeData *typeData) { int n; PGtypeSpec *specs; if (typeData->typspeccnt < typeData->typspecmax) return TRUE; n = typeData->typspecmax ? (typeData->typspecmax * 3) / 2 : 8; specs = (PGtypeSpec *) pqt_realloc( typeData->typspecs, sizeof(PGtypeSpec) * n); if (!specs) { PQseterror(PQT_OUTOFMEMORY); return FALSE; } memset(specs + typeData->typspeccnt, 0, (n - typeData->typspeccnt) * sizeof(PGtypeSpec)); typeData->typspecs = specs; typeData->typspecmax = n; return TRUE; }
/* skip quoted strings. Doesn't need to account for E'' syntax. The * E is copied over prior to the quoted string. * * Returns a pointer to the next character after the closing quote or * NULL if there was an error. */ static char * skipQuotes(char *s) { char *end; if (*s != '\'') return s; end = s; while (*++end) { /* If we see a backslash, skip an extra char. No need to dig any * further since this method works with \digits and \hex. */ if (*end == '\\') end++; else if (*end == '\'') break; } /* unterminated quote */ if (!*end) { PQseterror("unterminated single quoted string"); return NULL; } return ++end; /* skip ending quote */ }
int PQclearTypes(PGconn *conn) { PGtypeData *connData; if (!conn) { PQseterror("PGconn cannot be NULL"); return FALSE; } if (!(connData = (PGtypeData *) PQinstanceData(conn, pqt_eventproc))) { PQseterror("PGconn is missing event data"); return FALSE; } pqt_cleartypes(connData); return TRUE; }
/* errorf() callback for PGtypeArgs, see PQputf() and PQgetf(). * Always returns -1. */ int pqt_argserrorf(PGtypeArgs *args, const char *format, ...) { va_list ap; char fqtn[200]; if (!args || !format || !*format) return -1; pqt_fqtn(fqtn, sizeof(fqtn), args->typhandler->typschema, args->typhandler->typname); /* put the header */ PQseterror("%s[pos:%d] - ", fqtn, args->typpos); /* append message from type handler */ va_start(ap, format); vseterror(format, ap, TRUE); return -1; }
static int expandHandlers(PGtypeData *typeData) { int hmax; PGtypeHandler *h; if (typeData->typhcnt < typeData->typhmax) return TRUE; hmax = typeData->typhmax ? (typeData->typhmax * 3) / 2 : 8; h = (PGtypeHandler *) pqt_realloc( typeData->typhandlers, sizeof(PGtypeHandler) * hmax); if (!h) { PQseterror(PQT_OUTOFMEMORY); return FALSE; } typeData->typhandlers = h; typeData->typhmax = hmax; return TRUE; }
PGresult * pqt_copyresult(PGtypeArgs *args, int nattrs) { int i; PGresult *res; int tableid, columnid, format; PGresAttDesc *ad = (PGresAttDesc *) malloc(nattrs * sizeof(PGresAttDesc)); if (!ad) { PQseterror(args->err, PQT_OUTOFMEMORY); return NULL; } tableid = PQftable(args->get.result, args->get.field_num); columnid = PQftablecol(args->get.result, args->get.field_num); format = PQfformat(args->get.result, args->get.field_num); for (i=0; i < nattrs; i++) { ad[i].tableid = tableid; ad[i].columnid = columnid; ad[i].format = format; /* simple array */ if (args->typhandler->nattrs == 0) { ad[i].typid = args->typhandler->typoid; ad[i].typlen = args->typhandler->typlen; ad[i].name = NULL; ad[i].atttypmod = -1; } /* composite/record */ else { ad[i].typid = args->typhandler->attDescs[i].attoid; ad[i].typlen = args->typhandler->attDescs[i].attlen; ad[i].name = args->typhandler->attDescs[i].attname; ad[i].atttypmod = args->typhandler->attDescs[i].atttypmod; } } res = PQcopyResult(args->get.result, PG_COPYRES_EVENTS | PG_COPYRES_NOTICEHOOKS); if (!res) { free(ad); PQseterror(args->err, PQT_OUTOFMEMORY); return NULL; } if (!PQsetResultAttrs(res, nattrs, ad)) { PQclear(res); PQseterror(args->err, PQT_OUTOFMEMORY); res = NULL; } free(ad); return res; }
int PQputvf(PGparam *param, char *stmtBuf, size_t stmtBufLen, const char *format, va_list ap) { int n=0; int flags; size_t stmtPos = 0; int save_vcnt; int typpos = 0; PGtypeHandler *h; PGtypeArgs args; char args_outbuf[4096]; PGtypeSpec *spec = NULL; PQseterror(NULL); if (!param) { PQseterror("PGparam cannot be NULL"); return FALSE; } if (!format || !*format) { PQseterror("param 'format' cannot be NULL or an empty string"); return FALSE; } if (stmtBuf && stmtBufLen < 1) { PQseterror("Invalid argument: stmtBufLen must be >= 1"); return FALSE; } save_vcnt = param->vcnt; va_copy(args.ap, ap); /* "@name" format, lookup typeSpec in cache */ if(format && *format == '@') { spec = pqt_getspec(param->typspecs, param->typspeccnt, format + 1); /* If we didn't find a type spec, this is an error. A format string * with a '@' as its first character is reserved. */ if (!spec) { PQseterror("No such prepared specifier name: '%s'", format + 1); return FALSE; } } while (format && *format) { if (spec) { /* done, no more handlers in cached spec string. */ if (typpos == spec->idcnt) break; h = pqt_gethandlerbyid(param->typhandlers, param->typhcnt, spec->idlist[typpos]); /* should be an unusual, or a "will never happen", situation */ if (!h) { va_end(args.ap); PQseterror("Unknown type handler id at position %d", typpos+1); param->vcnt = save_vcnt; return FALSE; } flags = (int) spec->flags[typpos]; typpos++; } else { format = pqt_parse(format, param->typhandlers, param->typhcnt, stmtBuf, stmtBufLen, &h, &stmtPos, &typpos, &flags); if (!format) { param->vcnt = save_vcnt; return FALSE; } if (!h) continue; } args.is_put = 1; args.put.param = param; args.fmtinfo = ¶m->fmtinfo; args.put.out = args_outbuf; args.put.__allocated_out = NULL; args.put.outl = (int) sizeof(args_outbuf); args.is_ptr = (flags & TYPFLAG_POINTER) ? 1 : 0; args.format = BINARYFMT; args.put.expandBuffer = argsExpandBuffer; args.typpos = typpos; args.typhandler = h; args.errorf = pqt_argserrorf; args.super = pqt_argssuper; *args.put.out = 0; if (flags & TYPFLAG_ARRAY) n = pqt_put_array(&args); else n = h->typput(&args); if (n == -1) { if (args.put.__allocated_out && args.put.__allocated_out != args_outbuf) free(args.put.__allocated_out); param->vcnt = save_vcnt; return FALSE; } if (args.put.out == NULL) { args.format = BINARYFMT; n = -1; } n = pqt_putparam(param, args.put.out, n, flags, args.format, (flags & TYPFLAG_ARRAY) ? h->typoid_array : h->typoid); if (args.put.__allocated_out && args.put.__allocated_out != args_outbuf) free(args.put.__allocated_out); if (!n) { param->vcnt = save_vcnt; return FALSE; } } if (stmtBuf) stmtBuf[stmtPos] = 0; return TRUE; }
int pqt_putparam(PGparam *param, const void *data, int datal, int flags, int format, Oid typoid) { PGvalue *v; if (!param) return FALSE; if (!data) datal = -1; /* need to grow param vals array */ if (param->vcnt == param->vmax) { PGvalue *vals; int vmax = param->vmax ? (param->vmax * 3) / 2 : 16; vals = (PGvalue *) pqt_realloc(param->vals, sizeof(PGvalue) * vmax); if (!vals) { PQseterror(PQT_OUTOFMEMORY); return FALSE; } /* zero out the new array elements */ memset(vals + param->vcnt, 0, (vmax - param->vcnt) * sizeof(PGvalue)); param->vmax = vmax; param->vals = vals; } /* grab next param value */ v = ¶m->vals[param->vcnt]; if (datal == -1) { v->data = NULL; } /* wants to put a direct pointer */ else if (flags & TYPFLAG_POINTER) { v->data = (char *) data; } else { /* need more mem for param value ptr */ if (v->ptrl < datal) { char *ptr = (char *) pqt_realloc(v->ptr, datal); if (!ptr) { PQseterror(PQT_OUTOFMEMORY); return FALSE; } v->ptrl = datal; v->ptr = ptr; } memcpy(v->ptr, data, datal); v->data = v->ptr; } v->datal = datal; v->format = format; v->oid = typoid; param->vcnt++; return TRUE; }
PQT_EXPORT PGparam * PQparamDup(PGparam *param) { PGparam *new_param; PQseterror(NULL); if (!param) { PQseterror("PGparam to duplicate cannot be NULL"); return NULL; } new_param = (PGparam *) malloc(sizeof(PGparam)); if (!new_param) { PQseterror(PQT_OUTOFMEMORY); return NULL; } memset(new_param, 0, sizeof(PGparam)); /* copy any handlers from conn object */ if (param->typhcnt > 0) { new_param->typhandlers = pqt_duphandlers( param->typhandlers, param->typhcnt); if (!new_param->typhandlers) { PQparamClear(new_param); PQseterror(PQT_OUTOFMEMORY); return NULL; } new_param->typhcnt = param->typhcnt; } /* copy any typespecs from conn object */ if (param->typspeccnt > 0) { new_param->typspecs = pqt_dupspecs( param->typspecs, param->typspeccnt); if (!new_param->typspecs) { PQparamClear(new_param); PQseterror(PQT_OUTOFMEMORY); return NULL; } new_param->typspeccnt = param->typspeccnt; } memcpy(&new_param->fmtinfo, ¶m->fmtinfo, sizeof(PGtypeFormatInfo)); /* copy any values, don't bother if array is empty. */ if (param->vcnt > 0) { int i; for (i=0; i < param->vcnt; i++) { int flags = 0; PGvalue *val = ¶m->vals[i]; /* Is val->data is direct user pointer? If so, set pointer flag * so pqt_putparam doesn't deep copy it. */ if (val->ptr != val->data) flags |= TYPFLAG_POINTER; if (!pqt_putparam(new_param, val->data, val->datal, flags, val->format, val->oid)) { PQparamClear(new_param); return NULL; } } } return new_param; }
char *pqt_parse(const char *format, PGtypeHandler *h, int hcnt, char *stmtBuf, size_t stmtBufLen, PGtypeHandler **out, size_t *stmtPos, int *typpos, int *flags) { int specMark; char *s = skipQuotes((char *) format); char typname[PQT_MAXIDLEN + 1]; char schema[PQT_MAXIDLEN + 1]; char tmp[200]; *out = NULL; if (!s) return NULL; /* found quotes to skip */ if (s != format) { if (stmtBuf) { size_t n = s - format; CHKSTMTBUF(n); memcpy(stmtBuf + *stmtPos, format, n); (*stmtPos) += n; } return s; } specMark = *format; if (specMark != '%' && specMark != '#') { if (stmtBuf) { CHKSTMTBUF(1); stmtBuf[*stmtPos] = *format; (*stmtPos)++; } format++; return (char *) format; } /* spec skips % or # */ if (!(s = pqt_parsetype(format + 1, schema, typname, flags, *typpos + 1))) return NULL; if (*flags & TYPFLAG_INVALID) { if (stmtBuf) { CHKSTMTBUF(1); stmtBuf[*stmtPos] = *format++; (*stmtPos)++; PQseterror(NULL); /* set by pqt_parsetype */ return (char *) format; } return NULL; } (*typpos)++; if (!(*out = pqt_gethandler(h, hcnt, schema, typname))) { PQseterror("Uknown type '%s' (position %d)", pqt_fqtn(tmp, sizeof(tmp), schema, typname), *typpos); return NULL; } if (stmtBuf) { int n = pqt_snprintf(tmp, sizeof(tmp), "$%d", *typpos); CHKSTMTBUF(n); memcpy(stmtBuf + *stmtPos, tmp, n); (*stmtPos) += n; } if (!(*out)->typput) { PGtypeHandler *o = pqt_gethandlerbyid(h, hcnt, h->base_id); if (!o || !o->typput) { PQseterror( "Type '%s' doesn't support put operations (position %d)", pqt_fqtn(tmp, sizeof(tmp), (*out)->typschema, (*out)->typname), *typpos); *out = NULL; return NULL; } *out = o; } if ((*flags & TYPFLAG_POINTER) && !pqt_allowsptr(*out)) { PQseterror( "Type '%s' doesn't support putting pointers (position %d)", pqt_fqtn(tmp, sizeof(tmp), (*out)->typschema, (*out)->typname), *typpos); *out = NULL; return NULL; } if (specMark == '#') (*flags) |= TYPFLAG_BYNAME; return s; }
static int registerSubClass(PGtypeData *connData, const char *type_name, PGtypeProc typput, PGtypeProc typget) { char *s; PGtypeHandler *h_sub; PGtypeHandler *h_base; char sub_typschema[PQT_MAXIDLEN + 1]; char sub_typname[PQT_MAXIDLEN + 1]; char base_typschema[PQT_MAXIDLEN + 1]; char base_typname[PQT_MAXIDLEN + 1]; char sub_fqtn[200]; char base_fqtn[200]; if (!(s = parseType(type_name, sub_typschema, sub_typname, 1))) return FALSE; if (*s != '=') { PQseterror("Missing inheritence operator '=': %s", type_name); return FALSE; } if (!parseType(s + 1, base_typschema, base_typname, 1)) return FALSE; /* lookup the base handler */ h_base = pqt_gethandler(connData->typhandlers, connData->typhcnt, base_typschema, base_typname); if (!h_base) { PQseterror("typname '%s' does not exist, '%s' cannot sub-class it", pqt_fqtn(base_fqtn, sizeof(base_fqtn), base_typschema, base_typname), pqt_fqtn(sub_fqtn, sizeof(sub_fqtn), sub_typschema, sub_typname)); return FALSE; } /* cannot sub-class a record type */ if (h_base->typoid == RECORDOID) { PQseterror("Cannot sub-class pg_catalog.record '%s'", pqt_fqtn(sub_fqtn, sizeof(sub_fqtn), sub_typschema, sub_typname)); return FALSE; } if (!expandHandlers(connData)) return FALSE; h_sub = &connData->typhandlers[connData->typhcnt]; memset(h_sub, 0, sizeof(PGtypeHandler)); h_sub->id = connData->typhcnt + countof(pg_handlers); h_sub->typlen = h_base->typlen; h_sub->typoid = h_base->typoid; h_sub->typoid_array = h_base->typoid_array; h_sub->typput = typput; h_sub->typget = typget; h_sub->base_id = h_base->id; pqt_strcpy(h_sub->typschema, sizeof(h_sub->typschema), sub_typschema); pqt_strcpy(h_sub->typname, sizeof(h_sub->typname), sub_typname); connData->typhcnt++; return TRUE; }
int PQregisterResult(PGconn *conn, int which, PGregisterType *types, int count, PGresult *res) { int i; PGtypeData *connData; char typname[PQT_MAXIDLEN + 1]; char typschema[PQT_MAXIDLEN + 1]; /* inherit typput and typget from record type */ PGtypeHandler *h_rec = pqt_gethandler(NULL, 0, "pg_catalog", "record"); PQseterror(NULL); if (!conn) { PQseterror("PGconn cannot be NULL"); return FALSE; } if (!res) { PQseterror("PGresult cannot be NULL"); return FALSE; } if (which == PQT_SUBCLASS) { PQseterror("Cannot call PQregisterResult for a subclass registration."); return FALSE; } if (!(connData = (PGtypeData *) PQinstanceData(conn, pqt_eventproc))) { PQseterror("PGconn is missing event data"); return FALSE; } if (!types) { PQseterror("PGregisterType[] cannot be NULL"); return FALSE; } if (count < 0) { PQseterror("PGregisterType[] count cannot be less than zero"); return FALSE; } if(!checkTypeLookups(res, types, count)) return FALSE; for (i=0; i < PQntuples(res); i++) { int flags; PGint2 typlen; PGtypeHandler *h; if (which == PQT_USERDEFINED && !types[i].typput && !types[i].typget) { PQseterror("Must provide a put and/or a get routine: '%s'", types[i].typname); return FALSE; } /* make sure conn's type handlers array is large enough */ if (!expandHandlers(connData)) return FALSE; /* create the handler */ h = &connData->typhandlers[connData->typhcnt]; memset(h, 0, sizeof(PGtypeHandler)); if (!PQgetf(res, i, "%oid %oid %int2", 1, &h->typoid, 2, &h->typoid_array, 3, &typlen)) { return FALSE; } h->id = connData->typhcnt + countof(pg_handlers); h->typlen = (int) typlen; h->base_id = -1; if (which == PQT_USERDEFINED) { h->typput = types[i].typput; h->typget = types[i].typget; } else { h->typput = h_rec->typput; h->typget = h_rec->typget; } /* parse out type and schema names again */ (void ) pqt_parsetype(types[i].typname, typschema, typname, &flags, 1); pqt_strcpy(h->typschema, sizeof(h->typschema), typschema); pqt_strcpy(h->typname, sizeof(h->typname), typname); /* Process composite attributes */ if(which == PQT_COMPOSITE) { PGtext attrs; int nattrs; PGrecordAttDesc *attDescs; if (!PQgetf(res, i, "%text", 4, &attrs)) { return FALSE; } if (!(attDescs = initAttDescs(h, attrs))) return FALSE; for (nattrs=0; *attrs; nattrs++) { char *p; char *name; int len; /* Attribute Text Encoding: * "attoid,attlen,atttypmod,name_hex attoid,etc..." */ attDescs[nattrs].attoid = (int) strtol(attrs, &attrs, 10); attDescs[nattrs].attlen = (int) strtol(attrs + 1, &attrs, 10); attDescs[nattrs].atttypmod = (int) strtol(attrs + 1, &attrs, 10); /* skip comma before name */ attrs++; /* attribute name in hex */ if (!(p = strchr(attrs, ' '))) p = attrs + strlen(attrs); /* last attr, point at NUL */ /* truncate name if it exceeds buffer */ len = (int) (p - attrs); if (len >= (int) sizeof(attDescs[nattrs].attname)) len = (int) (sizeof(attDescs[nattrs].attname) - 1); /* hex decode and copy */ for (name = attDescs[nattrs].attname; attrs < p; attrs += 2) *name++ = (char) (pqt_hex_to_dec(attrs[0]) << 4) | pqt_hex_to_dec(attrs[1]); *name = 0; } h->nattrs = nattrs; h->attDescs = attDescs; } connData->typhcnt++; } return TRUE; }
int PQregisterTypes(PGconn *conn, int which, PGregisterType *types, int count, int async) { int n = FALSE; PQseterror(NULL); if (!conn) { PQseterror("PGconn cannot be NULL"); return FALSE; } if (!types) { PQseterror("PGregisterType[] cannot be NULL"); return FALSE; } if (count < 0) { PQseterror("PGregisterType[] count cannot be less than zero"); return FALSE; } /* nothing to do, silently ignore it */ if (count == 0) return TRUE; if (which == PQT_SUBCLASS) { int i; PGtypeData *connData; if (!(connData = (PGtypeData *) PQinstanceData(conn, pqt_eventproc))) { PQseterror("PGconn is missing event data"); return FALSE; } for (i=0; i < count; i++) { n = registerSubClass(connData, types[i].typname, types[i].typput, types[i].typget); if (!n) return FALSE; } } else { PGresult *res = NULL; n = performRegisterQuery(conn, which, types, count, async ? NULL : &res); /* If not async, register the result and clear it. */ if (n && !async) { n = PQregisterResult(conn, which, types, count, res); PQclear(res); } } return n; }
int PQspecPrepare(PGconn *conn, const char *name, const char *format, int is_stmt) { int flags; int typpos = 0; int idmax = 0; size_t stmtPos = 0; PGtypeHandler *h; PGtypeData *typeData; PGtypeSpec *spec; size_t stmtBufLen = 0; char *stmtBuf = NULL; char buffer[8192]; if (!conn) { PQseterror("PGConn cannot be NULL"); return FALSE; } if (!name || !*name) { PQseterror("Prepared specifier name cannot be NULL or an empty string"); return FALSE; } if (format && !*format) { PQseterror("Specifier format string cannot be empty"); return FALSE; } if (!isalnum(*name) && *name != '_') { PQseterror("Prepared specifier name must begin with an alpha, " "number or underscore."); return FALSE; } typeData = PQinstanceData(conn, pqt_eventproc); if (!typeData) { PQseterror("No type data exists for PGconn at %p", conn); return FALSE; } /* This is a removal request */ if (!format) { int i; for (i=0; i < typeData->typspeccnt; i++) { if (strcmp(typeData->typspecs[i].name, name) == 0) { /* clear it */ pqt_clearspec(&typeData->typspecs[i]); /* remove from list, not needed if its the last element */ if (i != typeData->typspeccnt - 1) memmove(typeData->typspecs + i, typeData->typspecs + i + 1, (typeData->typspeccnt - i - 1) * sizeof(PGtypeSpec)); typeData->typspeccnt--; break; } } /* always return TRUE, an error is not useful here */ return TRUE; } /* Already exists case */ spec = pqt_getspec(typeData->typspecs, typeData->typspeccnt, name); if (spec) { PQseterror("Prepared spec already exists '%s'", name); return FALSE; } /* Make sure specs array is large enough */ if (!expandSpecs(typeData)) return FALSE; spec = &typeData->typspecs[typeData->typspeccnt]; /* cache statement along with prepared type spec */ if (is_stmt) { stmtBufLen = strlen(format) + 1; /* no room in stack, use heap */ if (stmtBufLen > sizeof(buffer)) { stmtBuf = (char *) malloc(stmtBufLen); if (!stmtBuf) { PQseterror(PQT_OUTOFMEMORY); return FALSE; } } else { stmtBuf = buffer; stmtBufLen = sizeof(buffer); } } while (format && *format) { format = pqt_parse(format, typeData->typhandlers, typeData->typhcnt, stmtBuf, stmtBufLen, &h, &stmtPos, &typpos, &flags); if (!format) { pqt_clearspec(spec); FREESTMTBUF; return FALSE; } /* skipped harmless chars in format, like quoted sections. */ if(!h) continue; if (!spec->idlist || spec->idcnt == idmax) { int c = idmax ? idmax * 2 : 8; void *p = pqt_realloc(spec->idlist, c * sizeof(int)); if (!p) { PQseterror(PQT_OUTOFMEMORY); pqt_clearspec(spec); FREESTMTBUF; return FALSE; } spec->idlist = (int *) p; p = pqt_realloc(spec->flags, c * sizeof(char)); if (!p) { PQseterror(PQT_OUTOFMEMORY); pqt_clearspec(spec); FREESTMTBUF; return FALSE; } spec->flags = (unsigned char *) p; idmax = c; } /* Parallel arrays, every handler needs type flags */ spec->idlist[spec->idcnt] = h->id; spec->flags[spec->idcnt++] = (unsigned char) flags; } /* terminate stmtBuf, guarenteed to have room for NUL */ if (stmtBuf) stmtBuf[stmtPos] = 0; /* copy name string */ spec->name = strdup(name); if (!spec->name) { pqt_clearspec(spec); PQseterror(PQT_OUTOFMEMORY); FREESTMTBUF; return FALSE; } /* copy the parameterized stmt string */ if (stmtBuf) { spec->stmt = strdup(stmtBuf); if (!spec->stmt) { pqt_clearspec(spec); PQseterror(PQT_OUTOFMEMORY); FREESTMTBUF; return FALSE; } } FREESTMTBUF; /* Success, increment type spec count */ typeData->typspeccnt++; return TRUE; }
PGparam * PQparamCreate(const PGconn *conn) { PGparam *param; PGtypeData *connData; PQseterror(NULL); if (!conn) { PQseterror("PGconn cannot be NULL"); return NULL; } param = (PGparam *) malloc(sizeof(PGparam)); if (!param) { PQseterror(PQT_OUTOFMEMORY); return NULL; } memset(param, 0, sizeof(PGparam)); connData = (PGtypeData *) PQinstanceData(conn, pqt_eventproc); if (!connData) { PQparamClear(param); PQseterror("No type data exists for PGconn at %p", conn); return NULL; } /* copy any handlers from conn object */ if (connData->typhcnt > 0) { param->typhandlers = pqt_duphandlers( connData->typhandlers, connData->typhcnt); if (!param->typhandlers) { PQparamClear(param); PQseterror(PQT_OUTOFMEMORY); return NULL; } param->typhcnt = connData->typhcnt; } /* copy any typespecs from conn object */ if (connData->typspeccnt > 0) { param->typspecs = pqt_dupspecs( connData->typspecs, connData->typspeccnt); if (!param->typspecs) { PQparamClear(param); PQseterror(PQT_OUTOFMEMORY); return NULL; } param->typspeccnt = connData->typspeccnt; } pqt_getfmtinfo(conn, ¶m->fmtinfo); return param; }
static char * parseId(char *id, char **start, int *len, int *flags, int typpos) { char *p = id; *flags = 0; *start = NULL; *len = 0; if (*p == '"') p++; /* check first character */ if (!isalpha(*p) && *p != '_') { *flags |= TYPFLAG_INVALID; PQseterror( "Invalid first character for identifier '%c' (pos:%d)", *p, typpos); return p; } if (*id == '"') { id++; if (!(p = strchr(id, '"'))) { *flags |= TYPFLAG_INVALID; PQseterror("Unterminated double quote '%s' (pos:%d)", id-1, typpos); return p; } *len = (int) (p - id); *start = id; p++; } else { for (p=id+1; isalnum(*p) || *p=='_'; p++) ; *len = (int) (p - id); *start = id; *flags |= TYPFLAG_CASEFOLD; } /* range check */ if (*len == 0 || *len > PQT_MAXIDLEN) { *flags |= TYPFLAG_INVALID; PQseterror("Identifier out of range %d (pos:%d), range is 1 to %d", *len, typpos, PQT_MAXIDLEN); return p; } /* direct pointer request */ if (*p == '*') { p++; *flags |= TYPFLAG_POINTER; } /* Is this an array? Ex. %int4[] or %"a b"[] */ if (p[0] == '[' && p[1] == ']') { if (*flags & TYPFLAG_POINTER) { PQseterror( "'*' specifer flag cannot be used with arrays[] '%s' (pos:%d)", id, typpos); return NULL; } *flags |= TYPFLAG_ARRAY; p += 2; } return p; }