static bytea * encrypt_internal(int is_pubenc, int is_text, text *data, text *key, text *args) { MBuf *src, *dst; uint8 tmp[VARHDRSZ]; uint8 *restmp; bytea *res; int res_len; PGP_Context *ctx; int err; struct debug_expect ex; text *tmp_data = NULL; /* * Add data and key info RNG. */ add_entropy(data, key, NULL); init_work(&ctx, is_text, args, &ex); if (is_text && pgp_get_unicode_mode(ctx)) { tmp_data = convert_to_utf8(data); if (tmp_data == data) tmp_data = NULL; else data = tmp_data; } src = create_mbuf_from_vardata(data); dst = mbuf_create(VARSIZE(data) + 128); /* * reserve room for header */ mbuf_append(dst, tmp, VARHDRSZ); /* * set key */ if (is_pubenc) { MBuf *kbuf = create_mbuf_from_vardata(key); err = pgp_set_pubkey(ctx, kbuf, NULL, 0, 0); mbuf_free(kbuf); } else err = pgp_set_symkey(ctx, (uint8 *) VARDATA(key), VARSIZE(key) - VARHDRSZ); /* * encrypt */ if (err >= 0) err = pgp_encrypt(ctx, src, dst); /* * check for error */ if (err) { if (ex.debug) px_set_debug_handler(NULL); if (tmp_data) clear_and_pfree(tmp_data); pgp_free(ctx); mbuf_free(src); mbuf_free(dst); ereport(ERROR, (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), errmsg("%s", px_strerror(err)))); } /* res_len includes VARHDRSZ */ res_len = mbuf_steal_data(dst, &restmp); res = (bytea *) restmp; SET_VARSIZE(res, res_len); if (tmp_data) clear_and_pfree(tmp_data); pgp_free(ctx); mbuf_free(src); mbuf_free(dst); px_set_debug_handler(NULL); return res; }
/* * Emit a PG error or notice, together with any available info about * the current Python error, previously set by PLy_exception_set(). * This should be used to propagate Python errors into PG. If fmt is * NULL, the Python error becomes the primary error message, otherwise * it becomes the detail. If there is a Python traceback, it is put * in the context. */ void PLy_elog(int elevel, const char *fmt,...) { char *xmsg; char *tbmsg; int tb_depth; StringInfoData emsg; PyObject *exc, *val, *tb; const char *primary = NULL; int sqlerrcode = 0; char *detail = NULL; char *hint = NULL; char *query = NULL; int position = 0; PyErr_Fetch(&exc, &val, &tb); if (exc != NULL) { PyErr_NormalizeException(&exc, &val, &tb); if (PyErr_GivenExceptionMatches(val, PLy_exc_spi_error)) PLy_get_spi_error_data(val, &sqlerrcode, &detail, &hint, &query, &position); else if (PyErr_GivenExceptionMatches(val, PLy_exc_fatal)) elevel = FATAL; } /* this releases our refcount on tb! */ PLy_traceback(exc, val, tb, &xmsg, &tbmsg, &tb_depth); if (fmt) { initStringInfo(&emsg); for (;;) { va_list ap; int needed; va_start(ap, fmt); needed = appendStringInfoVA(&emsg, dgettext(TEXTDOMAIN, fmt), ap); va_end(ap); if (needed == 0) break; enlargeStringInfo(&emsg, needed); } primary = emsg.data; /* Since we have a format string, we cannot have a SPI detail. */ Assert(detail == NULL); /* If there's an exception message, it goes in the detail. */ if (xmsg) detail = xmsg; } else { if (xmsg) primary = xmsg; } PG_TRY(); { ereport(elevel, (errcode(sqlerrcode ? sqlerrcode : ERRCODE_EXTERNAL_ROUTINE_EXCEPTION), errmsg_internal("%s", primary ? primary : "no exception data"), (detail) ? errdetail_internal("%s", detail) : 0, (tb_depth > 0 && tbmsg) ? errcontext("%s", tbmsg) : 0, (hint) ? errhint("%s", hint) : 0, (query) ? internalerrquery(query) : 0, (position) ? internalerrposition(position) : 0)); } PG_CATCH(); { if (fmt) pfree(emsg.data); if (xmsg) pfree(xmsg); if (tbmsg) pfree(tbmsg); Py_XDECREF(exc); Py_XDECREF(val); PG_RE_THROW(); } PG_END_TRY(); if (fmt) pfree(emsg.data); if (xmsg) pfree(xmsg); if (tbmsg) pfree(tbmsg); Py_XDECREF(exc); Py_XDECREF(val); }
/* * ean2isn --- Try to convert an ean13 number to a UPC/ISxN number. * This doesn't verify for a valid check digit. * * If errorOK is false, ereport a useful error message if the ean13 is bad. * If errorOK is true, just return "false" for bad input. */ static bool ean2isn(ean13 ean, bool errorOK, ean13 *result, enum isn_type accept) { enum isn_type type = INVALID; char buf[MAXEAN13LEN + 1]; char *aux; unsigned digval; unsigned search; ean13 ret = ean; ean >>= 1; /* verify it's in the EAN13 range */ if (ean > UINT64CONST(9999999999999)) goto eantoobig; /* convert the number */ search = 0; aux = buf + 13; *aux = '\0'; /* terminate string; aux points to last digit */ do { digval = (unsigned) (ean % 10); /* get the decimal value */ ean /= 10; /* get next digit */ *--aux = (char) (digval + '0'); /* convert to ascii and store */ } while (ean && search++ < 12); while (search++ < 12) *--aux = '0'; /* fill the remaining EAN13 with '0' */ /* find out the data type: */ if (strncmp("978", buf, 3) == 0) { /* ISBN */ type = ISBN; } else if (strncmp("977", buf, 3) == 0) { /* ISSN */ type = ISSN; } else if (strncmp("9790", buf, 4) == 0) { /* ISMN */ type = ISMN; } else if (strncmp("979", buf, 3) == 0) { /* ISBN-13 */ type = ISBN; } else if (*buf == '0') { /* UPC */ type = UPC; } else { type = EAN13; } if (accept != ANY && accept != EAN13 && accept != type) goto eanwrongtype; *result = ret; return true; eanwrongtype: if (!errorOK) { if (type != EAN13) { ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("cannot cast EAN13(%s) to %s for number: \"%s\"", isn_names[type], isn_names[accept], buf))); } else { ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("cannot cast %s to %s for number: \"%s\"", isn_names[type], isn_names[accept], buf))); } } return false; eantoobig: if (!errorOK) { char eanbuf[64]; /* * Format the number separately to keep the machine-dependent format * code out of the translatable message text */ snprintf(eanbuf, sizeof(eanbuf), EAN13_FORMAT, ean); ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("value \"%s\" is out of range for %s type", eanbuf, isn_names[type]))); } return false; }
Datum hstore_populate_record(PG_FUNCTION_ARGS) { Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, 0); HStore *hs; HEntry *entries; char *ptr; HeapTupleHeader rec; Oid tupType; int32 tupTypmod; TupleDesc tupdesc; HeapTupleData tuple; HeapTuple rettuple; RecordIOData *my_extra; int ncolumns; int i; Datum *values; bool *nulls; if (!type_is_rowtype(argtype)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("first argument must be a rowtype"))); if (PG_ARGISNULL(0)) { if (PG_ARGISNULL(1)) PG_RETURN_NULL(); rec = NULL; /* * have no tuple to look at, so the only source of type info is the * argtype. The lookup_rowtype_tupdesc call below will error out if we * don't have a known composite type oid here. */ tupType = argtype; tupTypmod = -1; } else { rec = PG_GETARG_HEAPTUPLEHEADER(0); if (PG_ARGISNULL(1)) PG_RETURN_POINTER(rec); /* Extract type info from the tuple itself */ tupType = HeapTupleHeaderGetTypeId(rec); tupTypmod = HeapTupleHeaderGetTypMod(rec); } hs = PG_GETARG_HS(1); entries = ARRPTR(hs); ptr = STRPTR(hs); /* * if the input hstore is empty, we can only skip the rest if we were * passed in a non-null record, since otherwise there may be issues with * domain nulls. */ if (HS_COUNT(hs) == 0 && rec) PG_RETURN_POINTER(rec); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); ncolumns = tupdesc->natts; if (rec) { /* Build a temporary HeapTuple control structure */ tuple.t_len = HeapTupleHeaderGetDatumLength(rec); ItemPointerSetInvalid(&(tuple.t_self)); tuple.t_tableOid = InvalidOid; tuple.t_data = rec; } /* * We arrange to look up the needed I/O info just once per series of * calls, assuming the record type doesn't change underneath us. */ my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra; if (my_extra == NULL || my_extra->ncolumns != ncolumns) { fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData)); my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra; my_extra->record_type = InvalidOid; my_extra->record_typmod = 0; } if (my_extra->record_type != tupType || my_extra->record_typmod != tupTypmod) { MemSet(my_extra, 0, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData)); my_extra->record_type = tupType; my_extra->record_typmod = tupTypmod; my_extra->ncolumns = ncolumns; } values = (Datum *) palloc(ncolumns * sizeof(Datum)); nulls = (bool *) palloc(ncolumns * sizeof(bool)); if (rec) { /* Break down the tuple into fields */ heap_deform_tuple(&tuple, tupdesc, values, nulls); } else { for (i = 0; i < ncolumns; ++i) { values[i] = (Datum) 0; nulls[i] = true; } } for (i = 0; i < ncolumns; ++i) { ColumnIOData *column_info = &my_extra->columns[i]; Oid column_type = tupdesc->attrs[i]->atttypid; char *value; int idx; int vallen; /* Ignore dropped columns in datatype */ if (tupdesc->attrs[i]->attisdropped) { nulls[i] = true; continue; } idx = hstoreFindKey(hs, 0, NameStr(tupdesc->attrs[i]->attname), strlen(NameStr(tupdesc->attrs[i]->attname))); /* * we can't just skip here if the key wasn't found since we might have * a domain to deal with. If we were passed in a non-null record * datum, we assume that the existing values are valid (if they're * not, then it's not our fault), but if we were passed in a null, * then every field which we don't populate needs to be run through * the input function just in case it's a domain type. */ if (idx < 0 && rec) continue; /* * Prepare to convert the column value from text */ if (column_info->column_type != column_type) { getTypeInputInfo(column_type, &column_info->typiofunc, &column_info->typioparam); fmgr_info_cxt(column_info->typiofunc, &column_info->proc, fcinfo->flinfo->fn_mcxt); column_info->column_type = column_type; } if (idx < 0 || HS_VALISNULL(entries, idx)) { /* * need InputFunctionCall to happen even for nulls, so that domain * checks are done */ values[i] = InputFunctionCall(&column_info->proc, NULL, column_info->typioparam, tupdesc->attrs[i]->atttypmod); nulls[i] = true; } else { vallen = HS_VALLEN(entries, idx); value = palloc(1 + vallen); memcpy(value, HS_VAL(entries, ptr, idx), vallen); value[vallen] = 0; values[i] = InputFunctionCall(&column_info->proc, value, column_info->typioparam, tupdesc->attrs[i]->atttypmod); nulls[i] = false; } } rettuple = heap_form_tuple(tupdesc, values, nulls); ReleaseTupleDesc(tupdesc); PG_RETURN_DATUM(HeapTupleGetDatum(rettuple)); }
/* * Does the supplied GpPolicy support unique indexing on the specified * attributes? * * If the table is distributed randomly, no unique indexing is supported. * Otherwise, the set of columns being indexed should be a superset of the * policy. * * If the proposed index does not match the distribution policy but the relation * is empty and does not have a primary key or unique index, update the * distribution policy to match the index definition (MPP-101), as long as it * doesn't contain expressions. */ void checkPolicyForUniqueIndex(Relation rel, AttrNumber *indattr, int nidxatts, bool isprimary, bool has_exprs, bool has_pkey, bool has_ukey) { Bitmapset *polbm = NULL; Bitmapset *indbm = NULL; int i; GpPolicy *pol = rel->rd_cdbpolicy; /* * Firstly, unique/primary key indexes aren't supported if we're * distributing randomly. */ if (GpPolicyIsRandomly(pol)) { ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("%s and DISTRIBUTED RANDOMLY are incompatible", isprimary ? "PRIMARY KEY" : "UNIQUE"))); } /* * We use bitmaps to make intersection tests easier. As noted, order is * not relevant so looping is just painful. */ for (i = 0; i < pol->nattrs; i++) polbm = bms_add_member(polbm, pol->attrs[i]); for (i = 0; i < nidxatts; i++) { if (indattr[i] < 0) ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("cannot create %s on system column", isprimary ? "primary key" : "unique index"))); indbm = bms_add_member(indbm, indattr[i]); } Assert(bms_membership(polbm) != BMS_EMPTY_SET); Assert(bms_membership(indbm) != BMS_EMPTY_SET); /* * If the existing policy is not a subset, we must either error out or * update the distribution policy. It might be tempting to say that even * when the policy is a subset, we should update it to match the index * definition. The problem then is that if the user actually wants to * distribution on (a, b) but then creates an index on (a, b, c) we'll * change the policy underneath them. * * What is really needed is a new field in gp_distribution_policy telling us * if the policy has been explicitly set. */ if (!bms_is_subset(polbm, indbm)) { if (cdbRelSize(rel) != 0 || has_pkey || has_ukey || has_exprs) { ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("%s must contain all columns in the " "distribution key of relation \"%s\"", isprimary ? "PRIMARY KEY" : "UNIQUE index", RelationGetRelationName(rel)))); } else { /* update policy since table is not populated yet. See MPP-101 */ GpPolicy *policy = palloc(sizeof(GpPolicy) + (sizeof(AttrNumber) * nidxatts)); policy->ptype = POLICYTYPE_PARTITIONED; policy->nattrs = 0; for (i = 0; i < nidxatts; i++) policy->attrs[policy->nattrs++] = indattr[i]; GpPolicyReplace(rel->rd_id, policy); if (isprimary) elog(NOTICE, "updating distribution policy to match new primary key"); else elog(NOTICE, "updating distribution policy to match new unique index"); } } }
/* * Internal handler function */ static Datum handler_internal(Oid function_oid, FunctionCallInfo fcinfo, bool execute) { HeapTuple proctuple; Form_pg_proc pg_proc_entry; char *sourcecode; Datum prosrcdatum; bool isnull; const char **xslt_params; int i; Oid *argtypes; char **argnames; char *argmodes; int numargs; xmlDocPtr ssdoc; xmlDocPtr xmldoc; xmlDocPtr resdoc; xsltStylesheetPtr stylesheet; int reslen; xmlChar *resstr; Datum resdatum; if (CALLED_AS_TRIGGER(fcinfo)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("trigger functions not supported"))); proctuple = SearchSysCache(PROCOID, ObjectIdGetDatum(function_oid), 0, 0, 0); if (!HeapTupleIsValid(proctuple)) elog(ERROR, "cache lookup failed for function %u", function_oid); prosrcdatum = SysCacheGetAttr(PROCOID, proctuple, Anum_pg_proc_prosrc, &isnull); if (isnull) elog(ERROR, "null prosrc"); sourcecode = pstrdup(DatumGetCString(DirectFunctionCall1(textout, prosrcdatum))); /* allow one blank line at the start */ if (sourcecode[0] == '\n') sourcecode++; numargs = get_func_arg_info(proctuple, &argtypes, &argnames, &argmodes); if (numargs < 1) ereport(ERROR, (errmsg("XSLT function must have at least one argument"))); if (argtypes[0] != XMLOID) ereport(ERROR, (errmsg("first argument of XSLT function must have type XML"))); #if 0 xsltSetGenericErrorFunc(NULL, xmlGenericError); #endif ssdoc = xmlParseDoc((xmlChar *) sourcecode); /* XXX use backend's xml_parse here() */ stylesheet = xsltParseStylesheetDoc(ssdoc); /* XXX check error handling */ if (!stylesheet) ereport(ERROR, (errmsg("could not parse stylesheet"))); pg_proc_entry = (Form_pg_proc) GETSTRUCT(proctuple); { char *method; method = (char *) stylesheet->method; /* * TODO: This is strictly speaking not correct because the * default output method may be "html", but that can only * detected at run time, so punt for now. */ if (!method) method = "xml"; if (strcmp(method, "xml") == 0 && pg_proc_entry->prorettype != XMLOID) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("XSLT stylesheet has output method \"xml\" but return type of function is not xml"))); else if ((strcmp(method, "html") == 0 || strcmp(method, "text") == 0) && pg_proc_entry->prorettype != TEXTOID && pg_proc_entry->prorettype != VARCHAROID) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("XSLT stylesheet has output method \"%s\" but return type of function is not text or varchar", method))); } /* validation stops here */ if (!execute) { ReleaseSysCache(proctuple); PG_RETURN_VOID(); } /* execution begins here */ xslt_params = palloc(((numargs - 1) * 2 + 1) * sizeof(*xslt_params)); for (i = 0; i < numargs-1; i++) { xslt_params[i*2] = argnames[i+1]; xslt_params[i*2+1] = type_to_cstring(PG_GETARG_DATUM(i+1), argtypes[i+1]); } xslt_params[i*2] = NULL; { xmltype *arg0 = PG_GETARG_XML_P(0); // XXX this ought to use xml_parse() xmldoc = xmlParseMemory((char *) VARDATA(arg0), VARSIZE(arg0) - VARHDRSZ); } resdoc = xsltApplyStylesheet(stylesheet, xmldoc, xslt_params); if (!resdoc) elog(ERROR, "xsltApplyStylesheet() failed"); xmlFreeDoc(xmldoc); if (xsltSaveResultToString(&resstr, &reslen, resdoc, stylesheet) != 0) elog(ERROR, "result serialization failed"); xsltFreeStylesheet(stylesheet); xmlFreeDoc(resdoc); xsltCleanupGlobals(); xmlCleanupParser(); resdatum = cstring_to_type(resstr ? (char *) resstr : "", pg_proc_entry->prorettype); ReleaseSysCache(proctuple); PG_RETURN_DATUM(resdatum); }
Datum hstore_from_arrays(PG_FUNCTION_ARGS) { int4 buflen; HStore *out; Pairs *pairs; Datum *key_datums; bool *key_nulls; int key_count; Datum *value_datums; bool *value_nulls; int value_count; ArrayType *key_array; ArrayType *value_array; int i; if (PG_ARGISNULL(0)) PG_RETURN_NULL(); key_array = PG_GETARG_ARRAYTYPE_P(0); Assert(ARR_ELEMTYPE(key_array) == TEXTOID); /* * must check >1 rather than != 1 because empty arrays have 0 dimensions, * not 1 */ if (ARR_NDIM(key_array) > 1) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("wrong number of array subscripts"))); deconstruct_array(key_array, TEXTOID, -1, false, 'i', &key_datums, &key_nulls, &key_count); /* value_array might be NULL */ if (PG_ARGISNULL(1)) { value_array = NULL; value_count = key_count; value_datums = NULL; value_nulls = NULL; } else { value_array = PG_GETARG_ARRAYTYPE_P(1); Assert(ARR_ELEMTYPE(value_array) == TEXTOID); if (ARR_NDIM(value_array) > 1) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("wrong number of array subscripts"))); if ((ARR_NDIM(key_array) > 0 || ARR_NDIM(value_array) > 0) && (ARR_NDIM(key_array) != ARR_NDIM(value_array) || ARR_DIMS(key_array)[0] != ARR_DIMS(value_array)[0] || ARR_LBOUND(key_array)[0] != ARR_LBOUND(value_array)[0])) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("arrays must have same bounds"))); deconstruct_array(value_array, TEXTOID, -1, false, 'i', &value_datums, &value_nulls, &value_count); Assert(key_count == value_count); } pairs = palloc(key_count * sizeof(Pairs)); for (i = 0; i < key_count; ++i) { if (key_nulls[i]) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("null value not allowed for hstore key"))); if (!value_nulls || value_nulls[i]) { pairs[i].key = VARDATA_ANY(key_datums[i]); pairs[i].val = NULL; pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key_datums[i])); pairs[i].vallen = 4; pairs[i].isnull = true; pairs[i].needfree = false; } else { pairs[i].key = VARDATA_ANY(key_datums[i]); pairs[i].val = VARDATA_ANY(value_datums[i]); pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key_datums[i])); pairs[i].vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(value_datums[i])); pairs[i].isnull = false; pairs[i].needfree = false; } } key_count = hstoreUniquePairs(pairs, key_count, &buflen); out = hstorePairs(pairs, key_count, buflen); PG_RETURN_POINTER(out); }
/* * LocalBufferAlloc - * Find or create a local buffer for the given page of the given relation. * * API is similar to bufmgr.c's BufferAlloc, except that we do not need * to do any locking since this is all local. Also, IO_IN_PROGRESS * does not get set. Lastly, we support only default access strategy * (hence, usage_count is always advanced). */ BufferDesc * LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, bool *foundPtr) { BufferTag newTag; /* identity of requested block */ LocalBufferLookupEnt *hresult; BufferDesc *bufHdr; int b; int trycounter; bool found; uint32 buf_state; INIT_BUFFERTAG(newTag, smgr->smgr_rnode.node, forkNum, blockNum); /* Initialize local buffers if first request in this session */ if (LocalBufHash == NULL) InitLocalBuffers(); /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); if (hresult) { b = hresult->id; bufHdr = GetLocalBufferDescriptor(b); Assert(BUFFERTAGS_EQUAL(bufHdr->tag, newTag)); #ifdef LBDEBUG fprintf(stderr, "LB ALLOC (%u,%d,%d) %d\n", smgr->smgr_rnode.node.relNode, forkNum, blockNum, -b - 1); #endif buf_state = pg_atomic_read_u32(&bufHdr->state); /* this part is equivalent to PinBuffer for a shared buffer */ if (LocalRefCount[b] == 0) { if (BUF_STATE_GET_USAGECOUNT(buf_state) < BM_MAX_USAGE_COUNT) { buf_state += BUF_USAGECOUNT_ONE; pg_atomic_write_u32(&bufHdr->state, buf_state); } } LocalRefCount[b]++; ResourceOwnerRememberBuffer(CurrentResourceOwner, BufferDescriptorGetBuffer(bufHdr)); if (buf_state & BM_VALID) *foundPtr = TRUE; else { /* Previous read attempt must have failed; try again */ *foundPtr = FALSE; } return bufHdr; } #ifdef LBDEBUG fprintf(stderr, "LB ALLOC (%u,%d,%d) %d\n", smgr->smgr_rnode.node.relNode, forkNum, blockNum, -nextFreeLocalBuf - 1); #endif /* * Need to get a new buffer. We use a clock sweep algorithm (essentially * the same as what freelist.c does now...) */ trycounter = NLocBuffer; for (;;) { b = nextFreeLocalBuf; if (++nextFreeLocalBuf >= NLocBuffer) nextFreeLocalBuf = 0; bufHdr = GetLocalBufferDescriptor(b); if (LocalRefCount[b] == 0) { buf_state = pg_atomic_read_u32(&bufHdr->state); if (BUF_STATE_GET_USAGECOUNT(buf_state) > 0) { buf_state -= BUF_USAGECOUNT_ONE; pg_atomic_write_u32(&bufHdr->state, buf_state); trycounter = NLocBuffer; } else { /* Found a usable buffer */ LocalRefCount[b]++; ResourceOwnerRememberBuffer(CurrentResourceOwner, BufferDescriptorGetBuffer(bufHdr)); break; } } else if (--trycounter == 0) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES), errmsg("no empty local buffer available"))); } /* * this buffer is not referenced but it might still be dirty. if that's * the case, write it out before reusing it! */ if (buf_state & BM_DIRTY) { SMgrRelation oreln; Page localpage = (char *) LocalBufHdrGetBlock(bufHdr); /* Find smgr relation for buffer */ oreln = smgropen(bufHdr->tag.rnode, MyBackendId); PageSetChecksumInplace(localpage, bufHdr->tag.blockNum); /* And write... */ smgrwrite(oreln, bufHdr->tag.forkNum, bufHdr->tag.blockNum, localpage, false); /* Mark not-dirty now in case we error out below */ buf_state &= ~BM_DIRTY; pg_atomic_write_u32(&bufHdr->state, buf_state); pgBufferUsage.local_blks_written++; } /* * lazy memory allocation: allocate space on first use of a buffer. */ if (LocalBufHdrGetBlock(bufHdr) == NULL) { /* Set pointer for use by BufferGetBlock() macro */ LocalBufHdrGetBlock(bufHdr) = GetLocalBufferStorage(); } /* * Update the hash table: remove old entry, if any, and make new one. */ if (buf_state & BM_TAG_VALID) { hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &bufHdr->tag, HASH_REMOVE, NULL); if (!hresult) /* shouldn't happen */ elog(ERROR, "local buffer hash table corrupted"); /* mark buffer invalid just in case hash insert fails */ CLEAR_BUFFERTAG(bufHdr->tag); buf_state &= ~(BM_VALID | BM_TAG_VALID); pg_atomic_write_u32(&bufHdr->state, buf_state); } hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_ENTER, &found); if (found) /* shouldn't happen */ elog(ERROR, "local buffer hash table corrupted"); hresult->id = b; /* * it's all ours now. */ bufHdr->tag = newTag; buf_state &= ~(BM_VALID | BM_DIRTY | BM_JUST_DIRTIED | BM_IO_ERROR); buf_state |= BM_TAG_VALID; buf_state &= ~BUF_USAGECOUNT_MASK; buf_state += BUF_USAGECOUNT_ONE; pg_atomic_write_u32(&bufHdr->state, buf_state); *foundPtr = FALSE; return bufHdr; }
/* * make polish notaion of query */ static int4 makepol(QPRS_STATE * state) { int4 val = 0, type; int4 lenval = 0; char *strval = NULL; int4 stack[STACKDEPTH]; int4 lenstack = 0; uint16 flag = 0; while ((type = gettoken_query(state, &val, &lenval, &strval, &flag)) != END) { switch (type) { case VAL: pushval_asis(state, VAL, strval, lenval, flag); while (lenstack && (stack[lenstack - 1] == (int4) '&' || stack[lenstack - 1] == (int4) '!')) { lenstack--; pushquery(state, OPR, stack[lenstack], 0, 0, 0); } break; case OPR: if (lenstack && val == (int4) '|') pushquery(state, OPR, val, 0, 0, 0); else { if (lenstack == STACKDEPTH) /* internal error */ elog(ERROR, "stack too short"); stack[lenstack] = val; lenstack++; } break; case OPEN: if (makepol(state) == ERR) return ERR; if (lenstack && (stack[lenstack - 1] == (int4) '&' || stack[lenstack - 1] == (int4) '!')) { lenstack--; pushquery(state, OPR, stack[lenstack], 0, 0, 0); } break; case CLOSE: while (lenstack) { lenstack--; pushquery(state, OPR, stack[lenstack], 0, 0, 0); }; return END; break; case ERR: default: ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("syntax error"))); return ERR; } } while (lenstack) { lenstack--; pushquery(state, OPR, stack[lenstack], 0, 0, 0); }; return END; }
/* * Read a message from the specified connection carrying file descriptors */ int pool_recvfds(PoolPort *port, int *fds, int count) { int r; uint n32; char buf[SEND_MSG_BUFFER_SIZE]; struct iovec iov[1]; struct msghdr msg; int controllen = CMSG_LEN(count * sizeof(int)); struct cmsghdr *cmptr = malloc(CMSG_SPACE(count * sizeof(int))); if (cmptr == NULL) return EOF; iov[0].iov_base = buf; iov[0].iov_len = SEND_MSG_BUFFER_SIZE; msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_control = (caddr_t) cmptr; msg.msg_controllen = controllen; r = recvmsg(Socket(*port), &msg, 0); if (r < 0) { /* * Report broken connection */ ereport(ERROR, (errcode_for_socket_access(), errmsg("could not receive data from client: %m"))); goto failure; } else if (r == 0) { goto failure; } else if (r != SEND_MSG_BUFFER_SIZE) { ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("incomplete message from client [size: %u errno %u]",r,errno))); goto failure; } /* Verify response */ if (buf[0] != 'f') { ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("unexpected message code"))); goto failure; } memcpy(&n32, buf + 1, 4); n32 = ntohl(n32); if (n32 != 8) { ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid message size"))); goto failure; } /* * If connection count is 0 it means pool does not have connections * to fulfill request. Otherwise number of returned connections * should be equal to requested count. If it not the case consider this * a protocol violation. (Probably connection went out of sync) */ memcpy(&n32, buf + 5, 4); n32 = ntohl(n32); if (n32 == 0) { ereport(LOG, (errcode(ERRCODE_INSUFFICIENT_RESOURCES), errmsg("failed to acquire connections"))); goto failure; } if (n32 != count) { ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("unexpected connection count"))); goto failure; } memcpy(fds, CMSG_DATA(CMSG_FIRSTHDR(&msg)), count * sizeof(int)); free(cmptr); return 0; failure: free(cmptr); return EOF; }
/* * Read a message from the specified connection carrying pid numbers * of transactions interacting with pooler */ int pool_recvpids(PoolPort *port, int **pids) { int r, i; uint n32; char buf[SEND_PID_BUFFER_SIZE]; /* * Buffer size is upper bounded by the maximum number of connections, * as in the pooler each connection has one Pooler Agent. */ r = recv(Socket(*port), &buf, SEND_PID_BUFFER_SIZE, 0); if (r < 0) { /* * Report broken connection */ ereport(ERROR, (errcode_for_socket_access(), errmsg("could not receive data from client: %m"))); goto failure; } else if (r == 0) { goto failure; } else if (r != SEND_PID_BUFFER_SIZE) { ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("incomplete message from client"))); goto failure; } /* Verify response */ if (buf[0] != 'p') { ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("unexpected message code"))); goto failure; } memcpy(&n32, buf + 1, 4); n32 = ntohl(n32); if (n32 == 0) { elog(WARNING, "No transaction to abort"); return n32; } *pids = (int *) palloc(sizeof(int) * n32); for (i = 0; i < n32; i++) { int n; memcpy(&n, buf + 5 + i * sizeof(int), sizeof(int)); *pids[i] = ntohl(n); } return n32; failure: return 0; }
/* * Read pooler protocol message from the buffer. */ int pool_getmessage(PoolPort *port, StringInfo s, int maxlen) { int32 len; resetStringInfo(s); /* Read message length word */ if (pool_getbytes(port, (char *) &len, 4) == EOF) { ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("unexpected EOF within message length word"))); return EOF; } len = ntohl(len); if (len < 4 || (maxlen > 0 && len > maxlen)) { ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid message length"))); return EOF; } len -= 4; /* discount length itself */ if (len > 0) { /* * Allocate space for message. If we run out of room (ridiculously * large message), we will elog(ERROR) */ PG_TRY(); { enlargeStringInfo(s, len); } PG_CATCH(); { if (pool_discardbytes(port, len) == EOF) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("incomplete message from client"))); PG_RE_THROW(); } PG_END_TRY(); /* And grab the message */ if (pool_getbytes(port, s->data, len) == EOF) { ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("incomplete message from client"))); return EOF; } s->len = len; /* Place a trailing null per StringInfo convention */ s->data[len] = '\0'; } return 0; }
/* * Helper function for pgp_armor. Converts arrays of keys and values into * plain C arrays, and checks that they don't contain invalid characters. */ static int parse_key_value_arrays(ArrayType *key_array, ArrayType *val_array, char ***p_keys, char ***p_values) { int nkdims = ARR_NDIM(key_array); int nvdims = ARR_NDIM(val_array); char **keys, **values; Datum *key_datums, *val_datums; bool *key_nulls, *val_nulls; int key_count, val_count; int i; if (nkdims > 1 || nkdims != nvdims) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("wrong number of array subscripts"))); if (nkdims == 0) return 0; deconstruct_array(key_array, TEXTOID, -1, false, 'i', &key_datums, &key_nulls, &key_count); deconstruct_array(val_array, TEXTOID, -1, false, 'i', &val_datums, &val_nulls, &val_count); if (key_count != val_count) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("mismatched array dimensions"))); keys = (char **) palloc(sizeof(char *) * key_count); values = (char **) palloc(sizeof(char *) * val_count); for (i = 0; i < key_count; i++) { char *v; /* Check that the key doesn't contain anything funny */ if (key_nulls[i]) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("null value not allowed for header key"))); v = TextDatumGetCString(key_datums[i]); if (!string_is_ascii(v)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("header key must not contain non-ASCII characters"))); if (strstr(v, ": ")) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("header key must not contain \": \""))); if (strchr(v, '\n')) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("header key must not contain newlines"))); keys[i] = v; /* And the same for the value */ if (val_nulls[i]) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("null value not allowed for header value"))); v = TextDatumGetCString(val_datums[i]); if (!string_is_ascii(v)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("header value must not contain non-ASCII characters"))); if (strchr(v, '\n')) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("header value must not contain newlines"))); values[i] = v; } *p_keys = keys; *p_values = values; return key_count; }
static bytea * decrypt_internal(int is_pubenc, int need_text, text *data, text *key, text *keypsw, text *args) { int err; MBuf *src = NULL, *dst = NULL; uint8 tmp[VARHDRSZ]; uint8 *restmp; bytea *res; int res_len; PGP_Context *ctx = NULL; struct debug_expect ex; int got_unicode = 0; init_work(&ctx, need_text, args, &ex); src = mbuf_create_from_data((uint8 *) VARDATA(data), VARSIZE(data) - VARHDRSZ); dst = mbuf_create(VARSIZE(data) + 2048); /* * reserve room for header */ mbuf_append(dst, tmp, VARHDRSZ); /* * set key */ if (is_pubenc) { uint8 *psw = NULL; int psw_len = 0; MBuf *kbuf; if (keypsw) { psw = (uint8 *) VARDATA(keypsw); psw_len = VARSIZE(keypsw) - VARHDRSZ; } kbuf = create_mbuf_from_vardata(key); err = pgp_set_pubkey(ctx, kbuf, psw, psw_len, 1); mbuf_free(kbuf); } else err = pgp_set_symkey(ctx, (uint8 *) VARDATA(key), VARSIZE(key) - VARHDRSZ); /* * decrypt */ if (err >= 0) err = pgp_decrypt(ctx, src, dst); /* * failed? */ if (err < 0) goto out; if (ex.expect) check_expect(ctx, &ex); /* remember the setting */ got_unicode = pgp_get_unicode_mode(ctx); out: if (src) mbuf_free(src); if (ctx) pgp_free(ctx); if (err) { px_set_debug_handler(NULL); if (dst) mbuf_free(dst); ereport(ERROR, (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), errmsg("%s", px_strerror(err)))); } res_len = mbuf_steal_data(dst, &restmp); mbuf_free(dst); /* res_len includes VARHDRSZ */ res = (bytea *) restmp; SET_VARSIZE(res, res_len); if (need_text && got_unicode) { text *utf = convert_from_utf8(res); if (utf != res) { clear_and_pfree(res); res = utf; } } px_set_debug_handler(NULL); /* * add successful decryptions also into RNG */ add_entropy(res, key, keypsw); return res; }
/* * pgfdw_xact_callback --- cleanup at main-transaction end. */ static void pgfdw_xact_callback(XactEvent event, void *arg) { HASH_SEQ_STATUS scan; ConnCacheEntry *entry; /* Quick exit if no connections were touched in this transaction. */ if (!xact_got_connection) return; /* * Scan all connection cache entries to find open remote transactions, and * close them. */ hash_seq_init(&scan, ConnectionHash); while ((entry = (ConnCacheEntry *) hash_seq_search(&scan))) { Jresult *res; /* Ignore cache entry if no open connection right now */ if (entry->conn == NULL) continue; /* If it has an open remote transaction, try to close it */ if (entry->xact_depth > 0) { elog(DEBUG3, "closing remote transaction on connection %p", entry->conn); switch (event) { case XACT_EVENT_PRE_COMMIT: /* Commit all remote transactions during pre-commit */ do_sql_command(entry->conn, "COMMIT TRANSACTION"); /* * If there were any errors in subtransactions, and we * made prepared statements, do a DEALLOCATE ALL to make * sure we get rid of all prepared statements. This is * annoying and not terribly bulletproof, but it's * probably not worth trying harder. * * DEALLOCATE ALL only exists in 8.3 and later, so this * constrains how old a server jdbc2_fdw can * communicate with. We intentionally ignore errors in * the DEALLOCATE, so that we can hobble along to some * extent with older servers (leaking prepared statements * as we go; but we don't really support update operations * pre-8.3 anyway). */ if (entry->have_prep_stmt && entry->have_error) { res = JQexec(entry->conn, "DEALLOCATE ALL"); JQclear(res); } entry->have_prep_stmt = false; entry->have_error = false; break; case XACT_EVENT_PRE_PREPARE: /* * We disallow remote transactions that modified anything, * since it's not very reasonable to hold them open until * the prepared transaction is committed. For the moment, * throw error unconditionally; later we might allow * read-only cases. Note that the error will cause us to * come right back here with event == XACT_EVENT_ABORT, so * we'll clean up the connection state at that point. */ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot prepare a transaction that modified remote tables"))); break; case XACT_EVENT_COMMIT: case XACT_EVENT_PREPARE: /* Pre-commit should have closed the open transaction */ elog(ERROR, "missed cleaning up connection during pre-commit"); break; case XACT_EVENT_ABORT: /* Assume we might have lost track of prepared statements */ entry->have_error = true; /* If we're aborting, abort all remote transactions too */ res = JQexec(entry->conn, "ABORT TRANSACTION"); /* Note: can't throw ERROR, it would be infinite loop */ if (JQresultStatus(res) != PGRES_COMMAND_OK) pgfdw_report_error(WARNING, res, entry->conn, true, "ABORT TRANSACTION"); else { JQclear(res); /* As above, make sure to clear any prepared stmts */ if (entry->have_prep_stmt && entry->have_error) { res = JQexec(entry->conn, "DEALLOCATE ALL"); JQclear(res); } entry->have_prep_stmt = false; entry->have_error = false; } break; } } /* Reset state to show we're out of a transaction */ entry->xact_depth = 0; /* * If the connection isn't in a good idle state, discard it to * recover. Next GetConnection will open a new connection. */ if (JQstatus(entry->conn) != CONNECTION_OK || JQtransactionStatus(entry->conn) != PQTRANS_IDLE) { elog(DEBUG3, "discarding connection %p", entry->conn); JQfinish(entry->conn); entry->conn = NULL; } } /* * Regardless of the event type, we can now mark ourselves as out of the * transaction. (Note: if we are here during PRE_COMMIT or PRE_PREPARE, * this saves a useless scan of the hashtable during COMMIT or PREPARE.) */ xact_got_connection = false; /* Also reset cursor numbering for next transaction */ cursor_number = 0; }
/* * input */ static ltxtquery * queryin(char *buf) { QPRS_STATE state; int4 i; ltxtquery *query; int4 commonlen; ITEM *ptr; NODE *tmp; int4 pos = 0; #ifdef BS_DEBUG char pbuf[16384], *cur; #endif /* init state */ state.buf = buf; state.state = WAITOPERAND; state.count = 0; state.num = 0; state.str = NULL; /* init list of operand */ state.sumlen = 0; state.lenop = 64; state.curop = state.op = (char *) palloc(state.lenop); *(state.curop) = '\0'; /* parse query & make polish notation (postfix, but in reverse order) */ makepol(&state); if (!state.num) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("syntax error"), errdetail("Empty query."))); /* make finish struct */ commonlen = COMPUTESIZE(state.num, state.sumlen); query = (ltxtquery *) palloc(commonlen); query->len = commonlen; query->size = state.num; ptr = GETQUERY(query); /* set item in polish notation */ for (i = 0; i < state.num; i++) { ptr[i].type = state.str->type; ptr[i].val = state.str->val; ptr[i].distance = state.str->distance; ptr[i].length = state.str->length; ptr[i].flag = state.str->flag; tmp = state.str->next; pfree(state.str); state.str = tmp; } /* set user friendly-operand view */ memcpy((void *) GETOPERAND(query), (void *) state.op, state.sumlen); pfree(state.op); /* set left operand's position for every operator */ pos = 0; findoprnd(ptr, &pos); return query; }
/* This function accepts an array, and returns one item for each entry in the array */ Datum int_enum(PG_FUNCTION_ARGS) { PGARRAY *p = (PGARRAY *) PG_GETARG_POINTER(0); CTX *pc; ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo; if (!rsi || !IsA(rsi, ReturnSetInfo)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("int_enum called in context that cannot accept a set"))); if (!p) { elog(WARNING, "no data sent"); PG_RETURN_NULL(); } if (!fcinfo->flinfo->fn_extra) { /* Allocate working state */ MemoryContext oldcontext; oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt); pc = (CTX *) palloc(sizeof(CTX)); /* Don't copy attribute if you don't need to */ if (VARATT_IS_EXTENDED(p)) { /* Toasted!!! */ pc->p = (PGARRAY *) PG_DETOAST_DATUM_COPY(p); pc->flags = TOASTED; } else { /* Untoasted */ pc->p = p; pc->flags = 0; } /* Now that we have a detoasted array, verify dimensions */ if (pc->p->a.ndim != 1) elog(ERROR, "int_enum only accepts 1-D arrays"); pc->num = 0; fcinfo->flinfo->fn_extra = (void *) pc; MemoryContextSwitchTo(oldcontext); } else /* use existing working state */ pc = (CTX *) fcinfo->flinfo->fn_extra; /* Are we done yet? */ if (pc->num >= pc->p->items) { /* We are done */ if (pc->flags & TOASTED) pfree(pc->p); pfree(pc); fcinfo->flinfo->fn_extra = NULL; rsi->isDone = ExprEndResult; } else { /* nope, return the next value */ int val = pc->p->array[pc->num++]; rsi->isDone = ExprMultipleResult; PG_RETURN_INT32(val); } PG_RETURN_NULL(); }
/* * get token from query string */ static int4 gettoken_query(QPRS_STATE * state, int4 *val, int4 *lenval, char **strval, uint16 *flag) { while (1) { switch (state->state) { case WAITOPERAND: if (*(state->buf) == '!') { (state->buf)++; *val = (int4) '!'; return OPR; } else if (*(state->buf) == '(') { state->count++; (state->buf)++; return OPEN; } else if (ISALNUM(*(state->buf))) { state->state = INOPERAND; *strval = state->buf; *lenval = 1; *flag = 0; } else if (!isspace((unsigned char) *(state->buf))) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("operand syntax error"))); break; case INOPERAND: if (ISALNUM(*(state->buf))) { if (*flag) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("modificators syntax error"))); (*lenval)++; } else if (*(state->buf) == '%') *flag |= LVAR_SUBLEXEME; else if (*(state->buf) == '@') *flag |= LVAR_INCASE; else if (*(state->buf) == '*') *flag |= LVAR_ANYEND; else { state->state = WAITOPERATOR; return VAL; } break; case WAITOPERATOR: if (*(state->buf) == '&' || *(state->buf) == '|') { state->state = WAITOPERAND; *val = (int4) *(state->buf); (state->buf)++; return OPR; } else if (*(state->buf) == ')') { (state->buf)++; state->count--; return (state->count < 0) ? ERR : CLOSE; } else if (*(state->buf) == '\0') return (state->count) ? ERR : END; else if (*(state->buf) != ' ') return ERR; break; default: return ERR; break; } (state->buf)++; } return END; }
/* * Validator for a GIN opclass. */ bool ginvalidate(Oid opclassoid) { bool result = true; HeapTuple classtup; Form_pg_opclass classform; Oid opfamilyoid; Oid opcintype; Oid opckeytype; char *opclassname; HeapTuple familytup; Form_pg_opfamily familyform; char *opfamilyname; CatCList *proclist, *oprlist; List *grouplist; OpFamilyOpFuncGroup *opclassgroup; int i; ListCell *lc; /* Fetch opclass information */ classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid)); if (!HeapTupleIsValid(classtup)) elog(ERROR, "cache lookup failed for operator class %u", opclassoid); classform = (Form_pg_opclass) GETSTRUCT(classtup); opfamilyoid = classform->opcfamily; opcintype = classform->opcintype; opckeytype = classform->opckeytype; if (!OidIsValid(opckeytype)) opckeytype = opcintype; opclassname = NameStr(classform->opcname); /* Fetch opfamily information */ familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid)); if (!HeapTupleIsValid(familytup)) elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid); familyform = (Form_pg_opfamily) GETSTRUCT(familytup); opfamilyname = NameStr(familyform->opfname); /* Fetch all operators and support functions of the opfamily */ oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid)); proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid)); /* Check individual support functions */ for (i = 0; i < proclist->n_members; i++) { HeapTuple proctup = &proclist->members[i]->tuple; Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup); bool ok; /* * All GIN support functions should be registered with matching * left/right types */ if (procform->amproclefttype != procform->amprocrighttype) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("gin opfamily %s contains support procedure %s with cross-type registration", opfamilyname, format_procedure(procform->amproc)))); result = false; } /* * We can't check signatures except within the specific opclass, since * we need to know the associated opckeytype in many cases. */ if (procform->amproclefttype != opcintype) continue; /* Check procedure numbers and function signatures */ switch (procform->amprocnum) { case GIN_COMPARE_PROC: ok = check_amproc_signature(procform->amproc, INT4OID, false, 2, 2, opckeytype, opckeytype); break; case GIN_EXTRACTVALUE_PROC: /* Some opclasses omit nullFlags */ ok = check_amproc_signature(procform->amproc, INTERNALOID, false, 2, 3, opcintype, INTERNALOID, INTERNALOID); break; case GIN_EXTRACTQUERY_PROC: /* Some opclasses omit nullFlags and searchMode */ ok = check_amproc_signature(procform->amproc, INTERNALOID, false, 5, 7, opcintype, INTERNALOID, INT2OID, INTERNALOID, INTERNALOID, INTERNALOID, INTERNALOID); break; case GIN_CONSISTENT_PROC: /* Some opclasses omit queryKeys and nullFlags */ ok = check_amproc_signature(procform->amproc, BOOLOID, false, 6, 8, INTERNALOID, INT2OID, opcintype, INT4OID, INTERNALOID, INTERNALOID, INTERNALOID, INTERNALOID); break; case GIN_COMPARE_PARTIAL_PROC: ok = check_amproc_signature(procform->amproc, INT4OID, false, 4, 4, opckeytype, opckeytype, INT2OID, INTERNALOID); break; case GIN_TRICONSISTENT_PROC: ok = check_amproc_signature(procform->amproc, CHAROID, false, 7, 7, INTERNALOID, INT2OID, opcintype, INT4OID, INTERNALOID, INTERNALOID, INTERNALOID); break; default: ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("gin opfamily %s contains function %s with invalid support number %d", opfamilyname, format_procedure(procform->amproc), procform->amprocnum))); result = false; continue; /* don't want additional message */ } if (!ok) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("gin opfamily %s contains function %s with wrong signature for support number %d", opfamilyname, format_procedure(procform->amproc), procform->amprocnum))); result = false; } } /* Check individual operators */ for (i = 0; i < oprlist->n_members; i++) { HeapTuple oprtup = &oprlist->members[i]->tuple; Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup); /* TODO: Check that only allowed strategy numbers exist */ if (oprform->amopstrategy < 1 || oprform->amopstrategy > 63) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("gin opfamily %s contains operator %s with invalid strategy number %d", opfamilyname, format_operator(oprform->amopopr), oprform->amopstrategy))); result = false; } /* gin doesn't support ORDER BY operators */ if (oprform->amoppurpose != AMOP_SEARCH || OidIsValid(oprform->amopsortfamily)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("gin opfamily %s contains invalid ORDER BY specification for operator %s", opfamilyname, format_operator(oprform->amopopr)))); result = false; } /* Check operator signature --- same for all gin strategies */ if (!check_amop_signature(oprform->amopopr, BOOLOID, oprform->amoplefttype, oprform->amoprighttype)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("gin opfamily %s contains operator %s with wrong signature", opfamilyname, format_operator(oprform->amopopr)))); result = false; } } /* Now check for inconsistent groups of operators/functions */ grouplist = identify_opfamily_groups(oprlist, proclist); opclassgroup = NULL; foreach(lc, grouplist) { OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc); /* Remember the group exactly matching the test opclass */ if (thisgroup->lefttype == opcintype && thisgroup->righttype == opcintype) opclassgroup = thisgroup; /* * There is not a lot we can do to check the operator sets, since each * GIN opclass is more or less a law unto itself, and some contain * only operators that are binary-compatible with the opclass datatype * (meaning that empty operator sets can be OK). That case also means * that we shouldn't insist on nonempty function sets except for the * opclass's own group. */ }
/* * make_scalar_array_op() * Build expression tree for "scalar op ANY/ALL (array)" construct. */ Expr * make_scalar_array_op(ParseState *pstate, List *opname, bool useOr, Node *ltree, Node *rtree, int location) { Oid ltypeId, rtypeId, atypeId, res_atypeId; Operator tup; Form_pg_operator opform; Oid actual_arg_types[2]; Oid declared_arg_types[2]; List *args; Oid rettype; ScalarArrayOpExpr *result; ltypeId = exprType(ltree); atypeId = exprType(rtree); /* * The right-hand input of the operator will be the element type of the * array. However, if we currently have just an untyped literal on the * right, stay with that and hope we can resolve the operator. */ if (atypeId == UNKNOWNOID) rtypeId = UNKNOWNOID; else { rtypeId = get_element_type(atypeId); if (!OidIsValid(rtypeId)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("op ANY/ALL (array) requires array on right side"), parser_errposition(pstate, location))); } /* Now resolve the operator */ tup = oper(pstate, opname, ltypeId, rtypeId, false, location); opform = (Form_pg_operator) GETSTRUCT(tup); args = list_make2(ltree, rtree); actual_arg_types[0] = ltypeId; actual_arg_types[1] = rtypeId; declared_arg_types[0] = opform->oprleft; declared_arg_types[1] = opform->oprright; /* * enforce consistency with ANYARRAY and ANYELEMENT argument and return * types, possibly adjusting return type or declared_arg_types (which will * be used as the cast destination by make_fn_arguments) */ rettype = enforce_generic_type_consistency(actual_arg_types, declared_arg_types, 2, opform->oprresult); /* * Check that operator result is boolean */ if (rettype != BOOLOID) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("op ANY/ALL (array) requires operator to yield boolean"), parser_errposition(pstate, location))); if (get_func_retset(opform->oprcode)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("op ANY/ALL (array) requires operator not to return a set"), parser_errposition(pstate, location))); /* * Now switch back to the array type on the right, arranging for any * needed cast to be applied. */ res_atypeId = get_array_type(declared_arg_types[1]); if (!OidIsValid(res_atypeId)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("could not find array type for data type %s", format_type_be(declared_arg_types[1])), parser_errposition(pstate, location))); actual_arg_types[1] = atypeId; declared_arg_types[1] = res_atypeId; /* perform the necessary typecasting of arguments */ make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types); /* and build the expression node */ result = makeNode(ScalarArrayOpExpr); result->opno = oprid(tup); result->opfuncid = InvalidOid; result->useOr = useOr; result->args = args; ReleaseOperator(tup); return (Expr *) result; }
Datum hstore_from_array(PG_FUNCTION_ARGS) { ArrayType *in_array = PG_GETARG_ARRAYTYPE_P(0); int ndims = ARR_NDIM(in_array); int count; int4 buflen; HStore *out; Pairs *pairs; Datum *in_datums; bool *in_nulls; int in_count; int i; Assert(ARR_ELEMTYPE(in_array) == TEXTOID); switch (ndims) { case 0: out = hstorePairs(NULL, 0, 0); PG_RETURN_POINTER(out); case 1: if ((ARR_DIMS(in_array)[0]) % 2) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("array must have even number of elements"))); break; case 2: if ((ARR_DIMS(in_array)[1]) != 2) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("array must have two columns"))); break; default: ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("wrong number of array subscripts"))); } deconstruct_array(in_array, TEXTOID, -1, false, 'i', &in_datums, &in_nulls, &in_count); count = in_count / 2; pairs = palloc(count * sizeof(Pairs)); for (i = 0; i < count; ++i) { if (in_nulls[i * 2]) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("null value not allowed for hstore key"))); if (in_nulls[i * 2 + 1]) { pairs[i].key = VARDATA_ANY(in_datums[i * 2]); pairs[i].val = NULL; pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(in_datums[i * 2])); pairs[i].vallen = 4; pairs[i].isnull = true; pairs[i].needfree = false; } else { pairs[i].key = VARDATA_ANY(in_datums[i * 2]); pairs[i].val = VARDATA_ANY(in_datums[i * 2 + 1]); pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(in_datums[i * 2])); pairs[i].vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(in_datums[i * 2 + 1])); pairs[i].isnull = false; pairs[i].needfree = false; } } count = hstoreUniquePairs(pairs, count, &buflen); out = hstorePairs(pairs, count, buflen); PG_RETURN_POINTER(out); }
/* * pg_get_replication_slots - SQL SRF showing active replication slots. */ Datum pg_get_replication_slots(PG_FUNCTION_ARGS) { #define PG_GET_REPLICATION_SLOTS_COLS 9 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; TupleDesc tupdesc; Tuplestorestate *tupstore; MemoryContext per_query_ctx; MemoryContext oldcontext; int slotno; /* check to see if caller supports us returning a tuplestore */ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); if (!(rsinfo->allowedModes & SFRM_Materialize)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialize mode required, but it is not " \ "allowed in this context"))); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); /* * We don't require any special permission to see this function's data * because nothing should be sensitive. The most critical being the slot * name, which shouldn't contain anything particularly sensitive. */ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); tupstore = tuplestore_begin_heap(true, false, work_mem); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; MemoryContextSwitchTo(oldcontext); for (slotno = 0; slotno < max_replication_slots; slotno++) { ReplicationSlot *slot = &ReplicationSlotCtl->replication_slots[slotno]; Datum values[PG_GET_REPLICATION_SLOTS_COLS]; bool nulls[PG_GET_REPLICATION_SLOTS_COLS]; TransactionId xmin; TransactionId catalog_xmin; XLogRecPtr restart_lsn; pid_t active_pid; Oid database; NameData slot_name; NameData plugin; int i; SpinLockAcquire(&slot->mutex); if (!slot->in_use) { SpinLockRelease(&slot->mutex); continue; } else { xmin = slot->data.xmin; catalog_xmin = slot->data.catalog_xmin; database = slot->data.database; restart_lsn = slot->data.restart_lsn; namecpy(&slot_name, &slot->data.name); namecpy(&plugin, &slot->data.plugin); active_pid = slot->active_pid; } SpinLockRelease(&slot->mutex); memset(nulls, 0, sizeof(nulls)); i = 0; values[i++] = NameGetDatum(&slot_name); if (database == InvalidOid) nulls[i++] = true; else values[i++] = NameGetDatum(&plugin); if (database == InvalidOid) values[i++] = CStringGetTextDatum("physical"); else values[i++] = CStringGetTextDatum("logical"); if (database == InvalidOid) nulls[i++] = true; else values[i++] = database; values[i++] = BoolGetDatum(active_pid != 0); if (active_pid != 0) values[i++] = Int32GetDatum(active_pid); else nulls[i++] = true; if (xmin != InvalidTransactionId) values[i++] = TransactionIdGetDatum(xmin); else nulls[i++] = true; if (catalog_xmin != InvalidTransactionId) values[i++] = TransactionIdGetDatum(catalog_xmin); else nulls[i++] = true; if (restart_lsn != InvalidTransactionId) values[i++] = LSNGetDatum(restart_lsn); else nulls[i++] = true; tuplestore_putvalues(tupstore, tupdesc, values, nulls); } tuplestore_donestoring(tupstore); return (Datum) 0; }
Datum pginstall_platform(PG_FUNCTION_ARGS) { ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; TupleDesc tupdesc; Tuplestorestate *tupstore; MemoryContext per_query_ctx; MemoryContext oldcontext; PlatformData platform; Datum values[3]; bool nulls[3]; /* Fetch the list of available extensions */ current_platform(&platform); /* check to see if caller supports us returning a tuplestore */ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); if (!(rsinfo->allowedModes & SFRM_Materialize)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialize mode required, but it is not " \ "allowed in this context"))); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); /* Build tuplestore to hold the result rows */ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); tupstore = tuplestore_begin_heap(true, false, work_mem); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; MemoryContextSwitchTo(oldcontext); memset(values, 0, sizeof(values)); memset(nulls, 0, sizeof(nulls)); /* os name */ values[0] = CStringGetTextDatum(platform.os_name); /* os version */ values[1] = CStringGetTextDatum(platform.os_version); /* arch */ values[2] = CStringGetTextDatum(platform.arch); tuplestore_putvalues(tupstore, tupdesc, values, nulls); /* clean up and return the tuplestore */ tuplestore_donestoring(tupstore); return (Datum) 0; }
static float4 calc_rank_cd(float4 *arrdata, TSVector txt, TSQuery query, int method) { DocRepresentation *doc; int len, i, doclen = 0; Extention ext; double Wdoc = 0.0; double invws[lengthof(weights)]; double SumDist = 0.0, PrevExtPos = 0.0, CurExtPos = 0.0; int NExtent = 0; QueryRepresentation qr; for (i = 0; i < lengthof(weights); i++) { invws[i] = ((double) ((arrdata[i] >= 0) ? arrdata[i] : weights[i])); if (invws[i] > 1.0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("weight out of range"))); invws[i] = 1.0 / invws[i]; } qr.query = query; qr.operandexist = (bool *) palloc0(sizeof(bool) * query->size); doc = get_docrep(txt, &qr, &doclen); if (!doc) { pfree(qr.operandexist); return 0.0; } MemSet(&ext, 0, sizeof(Extention)); while (Cover(doc, doclen, &qr, &ext)) { double Cpos = 0.0; double InvSum = 0.0; int nNoise; DocRepresentation *ptr = ext.begin; while (ptr <= ext.end) { InvSum += invws[ptr->wclass]; ptr++; } Cpos = ((double) (ext.end - ext.begin + 1)) / InvSum; /* * if doc are big enough then ext.q may be equal to ext.p due to limit * of posional information. In this case we approximate number of * noise word as half cover's length */ nNoise = (ext.q - ext.p) - (ext.end - ext.begin); if (nNoise < 0) nNoise = (ext.end - ext.begin) / 2; Wdoc += Cpos / ((double) (1 + nNoise)); CurExtPos = ((double) (ext.q + ext.p)) / 2.0; if (NExtent > 0 && CurExtPos > PrevExtPos /* prevent devision by * zero in a case of multiple lexize */ ) SumDist += 1.0 / (CurExtPos - PrevExtPos); PrevExtPos = CurExtPos; NExtent++; } if ((method & RANK_NORM_LOGLENGTH) && txt->size > 0) Wdoc /= log((double) (cnt_length(txt) + 1)); if (method & RANK_NORM_LENGTH) { len = cnt_length(txt); if (len > 0) Wdoc /= (double) len; } if ((method & RANK_NORM_EXTDIST) && NExtent > 0 && SumDist > 0) Wdoc /= ((double) NExtent) / SumDist; if ((method & RANK_NORM_UNIQ) && txt->size > 0) Wdoc /= (double) (txt->size); if ((method & RANK_NORM_LOGUNIQ) && txt->size > 0) Wdoc /= log((double) (txt->size + 1)) / log(2.0); if (method & RANK_NORM_RDIVRPLUS1) Wdoc /= (Wdoc + 1); pfree(doc); pfree(qr.operandexist); return (float4) Wdoc; }
/* * make value of tsvector, given parsed text */ TSVector make_tsvector(ParsedText *prs) { int i, j, lenstr = 0, totallen; TSVector in; WordEntry *ptr; char *str; int stroff; prs->curwords = uniqueWORD(prs->words, prs->curwords); for (i = 0; i < prs->curwords; i++) { lenstr += prs->words[i].len; if (prs->words[i].alen) { lenstr = SHORTALIGN(lenstr); lenstr += sizeof(uint16) + prs->words[i].pos.apos[0] * sizeof(WordEntryPos); } } if (lenstr > MAXSTRPOS) ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("string is too long for tsvector (%d bytes, max %d bytes)", lenstr, MAXSTRPOS))); totallen = CALCDATASIZE(prs->curwords, lenstr); in = (TSVector) palloc0(totallen); SET_VARSIZE(in, totallen); in->size = prs->curwords; ptr = ARRPTR(in); str = STRPTR(in); stroff = 0; for (i = 0; i < prs->curwords; i++) { ptr->len = prs->words[i].len; ptr->pos = stroff; memcpy(str + stroff, prs->words[i].word, prs->words[i].len); stroff += prs->words[i].len; pfree(prs->words[i].word); if (prs->words[i].alen) { int k = prs->words[i].pos.apos[0]; WordEntryPos *wptr; if (k > 0xFFFF) elog(ERROR, "positions array too long"); ptr->haspos = 1; stroff = SHORTALIGN(stroff); *(uint16 *) (str + stroff) = (uint16) k; wptr = POSDATAPTR(in, ptr); for (j = 0; j < k; j++) { WEP_SETWEIGHT(wptr[j], 0); WEP_SETPOS(wptr[j], prs->words[i].pos.apos[j + 1]); } stroff += sizeof(uint16) + k * sizeof(WordEntryPos); pfree(prs->words[i].pos.apos); } else ptr->haspos = 0; ptr++; } pfree(prs->words); return in; }
/* * pg_prewarm(regclass, mode text, fork text, * first_block int8, last_block int8) * * The first argument is the relation to be prewarmed; the second controls * how prewarming is done; legal options are 'prefetch', 'read', and 'buffer'. * The third is the name of the relation fork to be prewarmed. The fourth * and fifth arguments specify the first and last block to be prewarmed. * If the fourth argument is NULL, it will be taken as 0; if the fifth argument * is NULL, it will be taken as the number of blocks in the relation. The * return value is the number of blocks successfully prewarmed. */ Datum pg_prewarm(PG_FUNCTION_ARGS) { Oid relOid; text *forkName; text *type; int64 first_block; int64 last_block; int64 nblocks; int64 blocks_done = 0; int64 block; Relation rel; ForkNumber forkNumber; char *forkString; char *ttype; PrewarmType ptype; AclResult aclresult; /* Basic sanity checking. */ if (PG_ARGISNULL(0)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("relation cannot be null"))); relOid = PG_GETARG_OID(0); if (PG_ARGISNULL(1)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errmsg("prewarm type cannot be null")))); type = PG_GETARG_TEXT_P(1); ttype = text_to_cstring(type); if (strcmp(ttype, "prefetch") == 0) ptype = PREWARM_PREFETCH; else if (strcmp(ttype, "read") == 0) ptype = PREWARM_READ; else if (strcmp(ttype, "buffer") == 0) ptype = PREWARM_BUFFER; else { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid prewarm type"), errhint("Valid prewarm types are \"prefetch\", \"read\", and \"buffer\"."))); PG_RETURN_INT64(0); /* Placate compiler. */ } if (PG_ARGISNULL(2)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errmsg("relation fork cannot be null")))); forkName = PG_GETARG_TEXT_P(2); forkString = text_to_cstring(forkName); forkNumber = forkname_to_number(forkString); /* Open relation and check privileges. */ rel = relation_open(relOid, AccessShareLock); aclresult = pg_class_aclcheck(relOid, GetUserId(), ACL_SELECT); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_CLASS, get_rel_name(relOid)); /* Check that the fork exists. */ RelationOpenSmgr(rel); if (!smgrexists(rel->rd_smgr, forkNumber)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("fork \"%s\" does not exist for this relation", forkString))); /* Validate block numbers, or handle nulls. */ nblocks = RelationGetNumberOfBlocksInFork(rel, forkNumber); if (PG_ARGISNULL(3)) first_block = 0; else { first_block = PG_GETARG_INT64(3); if (first_block < 0 || first_block >= nblocks) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("starting block number must be between 0 and " INT64_FORMAT, nblocks - 1))); } if (PG_ARGISNULL(4)) last_block = nblocks - 1; else { last_block = PG_GETARG_INT64(4); if (last_block < 0 || last_block >= nblocks) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("ending block number must be between 0 and " INT64_FORMAT, nblocks - 1))); } /* Now we're ready to do the real work. */ if (ptype == PREWARM_PREFETCH) { #ifdef USE_PREFETCH /* * In prefetch mode, we just hint the OS to read the blocks, but we * don't know whether it really does it, and we don't wait for it to * finish. * * It would probably be better to pass our prefetch requests in chunks * of a megabyte or maybe even a whole segment at a time, but there's * no practical way to do that at present without a gross modularity * violation, so we just do this. */ for (block = first_block; block <= last_block; ++block) { CHECK_FOR_INTERRUPTS(); PrefetchBuffer(rel, forkNumber, block); ++blocks_done; } #else ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("prefetch is not supported by this build"))); #endif } else if (ptype == PREWARM_READ) { /* * In read mode, we actually read the blocks, but not into shared * buffers. This is more portable than prefetch mode (it works * everywhere) and is synchronous. */ for (block = first_block; block <= last_block; ++block) { CHECK_FOR_INTERRUPTS(); smgrread(rel->rd_smgr, forkNumber, block, blockbuffer); ++blocks_done; } } else if (ptype == PREWARM_BUFFER) { /* * In buffer mode, we actually pull the data into shared_buffers. */ for (block = first_block; block <= last_block; ++block) { Buffer buf; CHECK_FOR_INTERRUPTS(); buf = ReadBufferExtended(rel, forkNumber, block, RBM_NORMAL, NULL); ReleaseBuffer(buf); ++blocks_done; } } /* Close relation, release lock. */ relation_close(rel, AccessShareLock); PG_RETURN_INT64(blocks_done); }
/* * suppress_redundant_updates_trigger * * This trigger function will inhibit an update from being done * if the OLD and NEW records are identical. */ Datum suppress_redundant_updates_trigger(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *) fcinfo->context; HeapTuple newtuple, oldtuple, rettuple; HeapTupleHeader newheader, oldheader; /* make sure it's called as a trigger */ if (!CALLED_AS_TRIGGER(fcinfo)) ereport(ERROR, (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), errmsg("suppress_redundant_updates_trigger: must be called as trigger"))); /* and that it's called on update */ if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) ereport(ERROR, (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), errmsg("suppress_redundant_updates_trigger: must be called on update"))); /* and that it's called before update */ if (!TRIGGER_FIRED_BEFORE(trigdata->tg_event)) ereport(ERROR, (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), errmsg("suppress_redundant_updates_trigger: must be called before update"))); /* and that it's called for each row */ if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) ereport(ERROR, (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), errmsg("suppress_redundant_updates_trigger: must be called for each row"))); /* get tuple data, set default result */ rettuple = newtuple = trigdata->tg_newtuple; oldtuple = trigdata->tg_trigtuple; newheader = newtuple->t_data; oldheader = oldtuple->t_data; /* * We are called before the OID, if any, has been transcribed from the old * tuple to the new (in heap_update). To avoid a bogus compare failure, * copy the OID now. But check that someone didn't already put another * OID value into newtuple. (That's not actually possible at present, but * maybe someday.) */ if (trigdata->tg_relation->rd_rel->relhasoids && !OidIsValid(HeapTupleHeaderGetOid(newheader))) HeapTupleHeaderSetOid(newheader, HeapTupleHeaderGetOid(oldheader)); /* if the tuple payload is the same ... */ if (newtuple->t_len == oldtuple->t_len && newheader->t_hoff == oldheader->t_hoff && (HeapTupleHeaderGetNatts(newheader) == HeapTupleHeaderGetNatts(oldheader)) && ((newheader->t_infomask & ~HEAP_XACT_MASK) == (oldheader->t_infomask & ~HEAP_XACT_MASK)) && memcmp(((char *) newheader) + offsetof(HeapTupleHeaderData, t_bits), ((char *) oldheader) + offsetof(HeapTupleHeaderData, t_bits), newtuple->t_len - offsetof(HeapTupleHeaderData, t_bits)) == 0) { /* ... then suppress the update */ rettuple = NULL; } return PointerGetDatum(rettuple); }
/* * Connect to remote server using specified server and user mapping properties. */ static Jconn * connect_jdbc_server(ForeignServer *server, UserMapping *user) { Jconn *volatile conn = NULL; /* * Use PG_TRY block to ensure closing connection on error. */ PG_TRY(); { const char **keywords; const char **values; int n; /* * Construct connection params from generic options of ForeignServer * and UserMapping. (Some of them might not be libpq options, in * which case we'll just waste a few array slots.) Add 3 extra slots * for fallback_application_name, client_encoding, end marker. */ n = list_length(server->options) + list_length(user->options) + 3; keywords = (const char **) palloc(n * sizeof(char *)); values = (const char **) palloc(n * sizeof(char *)); n = 0; n += ExtractConnectionOptions(server->options, keywords + n, values + n); n += ExtractConnectionOptions(user->options, keywords + n, values + n); /* Use "jdbc2_fdw" as fallback_application_name. */ keywords[n] = "fallback_application_name"; values[n] = "jdbc2_fdw"; n++; /* Set client_encoding so that libpq can convert encoding properly. */ keywords[n] = "client_encoding"; values[n] = GetDatabaseEncodingName(); n++; keywords[n] = values[n] = NULL; /* verify connection parameters and make connection */ check_conn_params(keywords, values); conn = JQconnectdbParams(server, user, keywords, values); if (!conn || JQstatus(conn) != CONNECTION_OK) { char *connmessage; int msglen; /* libpq typically appends a newline, strip that */ connmessage = pstrdup(JQerrorMessage(conn)); msglen = strlen(connmessage); if (msglen > 0 && connmessage[msglen - 1] == '\n') connmessage[msglen - 1] = '\0'; ereport(ERROR, (errcode(ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION), errmsg("could not connect to server \"%s\"", server->servername), errdetail_internal("%s", connmessage))); } /* * Check that non-superuser has used password to establish connection; * otherwise, he's piggybacking on the jdbc server's user * identity. See also dblink_security_check() in contrib/dblink. */ if (!superuser() && !JQconnectionUsedPassword(conn)) ereport(ERROR, (errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED), errmsg("password is required"), errdetail("Non-superuser cannot connect if the server does not request a password."), errhint("Target server's authentication method must be changed."))); pfree(keywords); pfree(values); } PG_CATCH(); { /* Release Jconn data structure if we managed to create one */ if (conn) JQfinish(conn); PG_RE_THROW(); } PG_END_TRY(); return conn; }
/* * ean2string --- Try to convert an ean13 number to an hyphenated string. * Assumes there's enough space in result to hold * the string (maximum MAXEAN13LEN+1 bytes) * This doesn't verify for a valid check digit. * * If shortType is true, the returned string is in the old ISxN short format. * If errorOK is false, ereport a useful error message if the string is bad. * If errorOK is true, just return "false" for bad input. */ static bool ean2string(ean13 ean, bool errorOK, char *result, bool shortType) { const char *(*TABLE)[2]; const unsigned (*TABLE_index)[2]; enum isn_type type = INVALID; char *aux; unsigned digval; unsigned search; char valid = '\0'; /* was the number initially written with a * valid check digit? */ TABLE_index = ISBN_index; if ((ean & 1) != 0) valid = '!'; ean >>= 1; /* verify it's in the EAN13 range */ if (ean > UINT64CONST(9999999999999)) goto eantoobig; /* convert the number */ search = 0; aux = result + MAXEAN13LEN; *aux = '\0'; /* terminate string; aux points to last digit */ *--aux = valid; /* append '!' for numbers with invalid but * corrected check digit */ do { digval = (unsigned) (ean % 10); /* get the decimal value */ ean /= 10; /* get next digit */ *--aux = (char) (digval + '0'); /* convert to ascii and store */ if (search == 0) *--aux = '-'; /* the check digit is always there */ } while (ean && search++ < 13); while (search++ < 13) *--aux = '0'; /* fill the remaining EAN13 with '0' */ /* The string should be in this form: ???DDDDDDDDDDDD-D" */ search = hyphenate(result, result + 3, EAN13_range, EAN13_index); /* verify it's a logically valid EAN13 */ if (search == 0) { search = hyphenate(result, result + 3, NULL, NULL); goto okay; } /* find out what type of hyphenation is needed: */ if (strncmp("978-", result, search) == 0) { /* ISBN -13 978-range */ /* The string should be in this form: 978-??000000000-0" */ type = ISBN; TABLE = ISBN_range; TABLE_index = ISBN_index; } else if (strncmp("977-", result, search) == 0) { /* ISSN */ /* The string should be in this form: 977-??000000000-0" */ type = ISSN; TABLE = ISSN_range; TABLE_index = ISSN_index; } else if (strncmp("979-0", result, search + 1) == 0) { /* ISMN */ /* The string should be in this form: 979-0?000000000-0" */ type = ISMN; TABLE = ISMN_range; TABLE_index = ISMN_index; } else if (strncmp("979-", result, search) == 0) { /* ISBN-13 979-range */ /* The string should be in this form: 979-??000000000-0" */ type = ISBN; TABLE = ISBN_range_new; TABLE_index = ISBN_index_new; } else if (*result == '0') { /* UPC */ /* The string should be in this form: 000-00000000000-0" */ type = UPC; TABLE = UPC_range; TABLE_index = UPC_index; } else { type = EAN13; TABLE = NULL; TABLE_index = NULL; } /* verify it's a logically valid EAN13/UPC/ISxN */ digval = search; search = hyphenate(result + digval, result + digval + 2, TABLE, TABLE_index); /* verify it's a valid EAN13 */ if (search == 0) { search = hyphenate(result + digval, result + digval + 2, NULL, NULL); goto okay; } okay: /* convert to the old short type: */ if (shortType) switch (type) { case ISBN: ean2ISBN(result); break; case ISMN: ean2ISMN(result); break; case ISSN: ean2ISSN(result); break; case UPC: ean2UPC(result); break; default: break; } return true; eantoobig: if (!errorOK) { char eanbuf[64]; /* * Format the number separately to keep the machine-dependent format * code out of the translatable message text */ snprintf(eanbuf, sizeof(eanbuf), EAN13_FORMAT, ean); ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("value \"%s\" is out of range for %s type", eanbuf, isn_names[type]))); } return false; }
Datum pgp_armor_headers(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; pgp_armor_headers_state *state; char *utf8key; char *utf8val; HeapTuple tuple; TupleDesc tupdesc; AttInMetadata *attinmeta; if (SRF_IS_FIRSTCALL()) { text *data = PG_GETARG_TEXT_PP(0); int res; MemoryContext oldcontext; funcctx = SRF_FIRSTCALL_INIT(); /* we need the state allocated in the multi call context */ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); attinmeta = TupleDescGetAttInMetadata(tupdesc); funcctx->attinmeta = attinmeta; state = (pgp_armor_headers_state *) palloc(sizeof(pgp_armor_headers_state)); res = pgp_extract_armor_headers((uint8 *) VARDATA_ANY(data), VARSIZE_ANY_EXHDR(data), &state->nheaders, &state->keys, &state->values); if (res < 0) ereport(ERROR, (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), errmsg("%s", px_strerror(res)))); MemoryContextSwitchTo(oldcontext); funcctx->user_fctx = state; } funcctx = SRF_PERCALL_SETUP(); state = (pgp_armor_headers_state *) funcctx->user_fctx; if (funcctx->call_cntr >= state->nheaders) SRF_RETURN_DONE(funcctx); else { char *values[2]; /* we assume that the keys (and values) are in UTF-8. */ utf8key = state->keys[funcctx->call_cntr]; utf8val = state->values[funcctx->call_cntr]; values[0] = pg_any_to_server(utf8key, strlen(utf8key), PG_UTF8); values[1] = pg_any_to_server(utf8val, strlen(utf8val), PG_UTF8); /* build a tuple */ tuple = BuildTupleFromCStrings(funcctx->attinmeta, values); SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); } }