Datum xslt_process(PG_FUNCTION_ARGS) { #ifdef USE_LIBXSLT text *doct = PG_GETARG_TEXT_P(0); text *ssheet = PG_GETARG_TEXT_P(1); text *paramstr; const char **params; xsltStylesheetPtr stylesheet = NULL; xmlDocPtr doctree; xmlDocPtr restree; xmlDocPtr ssdoc = NULL; xmlChar *resstr; int resstat; int reslen; if (fcinfo->nargs == 3) { paramstr = PG_GETARG_TEXT_P(2); params = parse_params(paramstr); } else { /* No parameters */ params = (const char **) palloc(sizeof(char *)); params[0] = NULL; } /* Setup parser */ pgxml_parser_init(); /* Check to see if document is a file or a literal */ if (VARDATA(doct)[0] == '<') doctree = xmlParseMemory((char *) VARDATA(doct), VARSIZE(doct) - VARHDRSZ); else doctree = xmlParseFile(text_to_cstring(doct)); if (doctree == NULL) xml_ereport(ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION, "error parsing XML document"); /* Same for stylesheet */ if (VARDATA(ssheet)[0] == '<') { ssdoc = xmlParseMemory((char *) VARDATA(ssheet), VARSIZE(ssheet) - VARHDRSZ); if (ssdoc == NULL) { xmlFreeDoc(doctree); xml_ereport(ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION, "error parsing stylesheet as XML document"); } stylesheet = xsltParseStylesheetDoc(ssdoc); } else stylesheet = xsltParseStylesheetFile((xmlChar *) text_to_cstring(ssheet)); if (stylesheet == NULL) { xmlFreeDoc(doctree); xsltCleanupGlobals(); xml_ereport(ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION, "failed to parse stylesheet"); } restree = xsltApplyStylesheet(stylesheet, doctree, params); resstat = xsltSaveResultToString(&resstr, &reslen, restree, stylesheet); xsltFreeStylesheet(stylesheet); xmlFreeDoc(restree); xmlFreeDoc(doctree); xsltCleanupGlobals(); if (resstat < 0) PG_RETURN_NULL(); PG_RETURN_TEXT_P(cstring_to_text_with_len((char *) resstr, reslen)); #else /* !USE_LIBXSLT */ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("xslt_process() is not available without libxslt"))); PG_RETURN_NULL(); #endif /* USE_LIBXSLT */ }
/* * EnumValuesCreate * Create an entry in pg_enum for each of the supplied enum values. * * vals is a list of Value strings. */ void EnumValuesCreate(Oid enumTypeOid, List *vals) { Relation pg_enum; NameData enumlabel; Oid *oids; int elemno, num_elems; Datum values[Natts_pg_enum]; bool nulls[Natts_pg_enum]; ListCell *lc; HeapTuple tup; num_elems = list_length(vals); /* * We do not bother to check the list of values for duplicates --- if you * have any, you'll get a less-than-friendly unique-index violation. It is * probably not worth trying harder. */ pg_enum = heap_open(EnumRelationId, RowExclusiveLock); /* * Allocate OIDs for the enum's members. * * While this method does not absolutely guarantee that we generate no * duplicate OIDs (since we haven't entered each oid into the table before * allocating the next), trouble could only occur if the OID counter wraps * all the way around before we finish. Which seems unlikely. */ oids = (Oid *) palloc(num_elems * sizeof(Oid)); for (elemno = 0; elemno < num_elems; elemno++) { /* * We assign even-numbered OIDs to all the new___ enum labels. This * tells the comparison functions the OIDs are in the correct sort * order and can be compared directly. */ Oid new_oid; do { new_oid = GetNewOid(pg_enum); } while (new_oid & 1); oids[elemno] = new_oid; } /* sort them, just in case OID counter wrapped from high to low */ qsort(oids, num_elems, sizeof(Oid), oid_cmp); /* and make the entries */ memset(nulls, false, sizeof(nulls)); elemno = 0; foreach(lc, vals) { char *lab = strVal(lfirst(lc)); /* * labels are stored in a name field, for easier syscache lookup, so * check the length to make sure it's within range. */ if (strlen(lab) > (NAMEDATALEN - 1)) ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("invalid enum label \"%s\"", lab), errdetail("Labels must be %d characters or less.", NAMEDATALEN - 1))); values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid); values[Anum_pg_enum_enumsortorder - 1] = Float4GetDatum(elemno + 1); namestrcpy(&enumlabel, lab); values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel); tup = heap_form_tuple(RelationGetDescr(pg_enum), values, nulls); HeapTupleSetOid(tup, oids[elemno]); simple_heap_insert(pg_enum, tup); CatalogUpdateIndexes(pg_enum, tup); heap_freetuple(tup); elemno++; }
Datum dbms_pipe_create_pipe (PG_FUNCTION_ARGS) { text *pipe_name = NULL; int limit = 0; bool is_private; bool limit_is_valid = false; bool created; float8 endtime; int cycle = 0; int timeout = 10; if (PG_ARGISNULL(0)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("pipe name is NULL"), errdetail("Pipename may not be NULL."))); else pipe_name = PG_GETARG_TEXT_P(0); if (!PG_ARGISNULL(1)) { limit = PG_GETARG_INT32(1); limit_is_valid = true; } is_private = PG_ARGISNULL(2) ? false : PG_GETARG_BOOL(2); WATCH_PRE(timeout, endtime, cycle); if (ora_lock_shmem(SHMEMMSGSZ, MAX_PIPES,MAX_EVENTS,MAX_LOCKS,false)) { pipe *p; if (NULL != (p = find_pipe(pipe_name, &created, false))) { if (!created) { LWLockRelease(shmem_lockid); ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("pipe creation error"), errdetail("Pipe is registered."))); } if (is_private) { char *user; p->uid = GetUserId(); #if PG_VERSION_NUM >= 90500 user = (char*)DirectFunctionCall1(namein, CStringGetDatum(GetUserNameFromId(p->uid, false))); #else user = (char*)DirectFunctionCall1(namein, CStringGetDatum(GetUserNameFromId(p->uid))); #endif p->creator = ora_sstrcpy(user); pfree(user); } p->limit = limit_is_valid ? limit : -1; p->registered = true; LWLockRelease(shmem_lockid); PG_RETURN_VOID(); } } WATCH_POST(timeout, endtime, cycle); LOCK_ERROR(); PG_RETURN_VOID(); }
Datum lquery_in(PG_FUNCTION_ARGS) { char *buf = (char *) PG_GETARG_POINTER(0); char *ptr; int num = 0, totallen = 0, numOR = 0; int state = LQPRS_WAITLEVEL; lquery *result; nodeitem *lptr = NULL; lquery_level *cur, *curqlevel, *tmpql; lquery_variant *lrptr = NULL; bool hasnot = false; bool wasbad = false; int charlen; int pos = 0; ptr = buf; while (*ptr) { charlen = pg_mblen(ptr); if (charlen == 1) { if (t_iseq(ptr, '.')) num++; else if (t_iseq(ptr, '|')) numOR++; } ptr += charlen; } num++; if (num > MaxAllocSize / ITEMSIZE) ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("number of levels (%d) exceeds the maximum allowed (%d)", num, (int) (MaxAllocSize / ITEMSIZE)))); curqlevel = tmpql = (lquery_level *) palloc0(ITEMSIZE * num); ptr = buf; while (*ptr) { charlen = pg_mblen(ptr); if (state == LQPRS_WAITLEVEL) { if (ISALNUM(ptr)) { GETVAR(curqlevel) = lptr = (nodeitem *) palloc0(sizeof(nodeitem) * (numOR + 1)); lptr->start = ptr; state = LQPRS_WAITDELIM; curqlevel->numvar = 1; } else if (charlen == 1 && t_iseq(ptr, '!')) { GETVAR(curqlevel) = lptr = (nodeitem *) palloc0(sizeof(nodeitem) * (numOR + 1)); lptr->start = ptr + 1; state = LQPRS_WAITDELIM; curqlevel->numvar = 1; curqlevel->flag |= LQL_NOT; hasnot = true; } else if (charlen == 1 && t_iseq(ptr, '*')) state = LQPRS_WAITOPEN; else UNCHAR; } else if (state == LQPRS_WAITVAR) { if (ISALNUM(ptr)) { lptr++; lptr->start = ptr; state = LQPRS_WAITDELIM; curqlevel->numvar++; } else UNCHAR; } else if (state == LQPRS_WAITDELIM) { if (charlen == 1 && t_iseq(ptr, '@')) { if (lptr->start == ptr) UNCHAR; lptr->flag |= LVAR_INCASE; curqlevel->flag |= LVAR_INCASE; } else if (charlen == 1 && t_iseq(ptr, '*')) { if (lptr->start == ptr) UNCHAR; lptr->flag |= LVAR_ANYEND; curqlevel->flag |= LVAR_ANYEND; } else if (charlen == 1 && t_iseq(ptr, '%')) { if (lptr->start == ptr) UNCHAR; lptr->flag |= LVAR_SUBLEXEME; curqlevel->flag |= LVAR_SUBLEXEME; } else if (charlen == 1 && t_iseq(ptr, '|')) { lptr->len = ptr - lptr->start - ((lptr->flag & LVAR_SUBLEXEME) ? 1 : 0) - ((lptr->flag & LVAR_INCASE) ? 1 : 0) - ((lptr->flag & LVAR_ANYEND) ? 1 : 0); if (lptr->wlen > 255) ereport(ERROR, (errcode(ERRCODE_NAME_TOO_LONG), errmsg("name of level is too long"), errdetail("Name length is %d, must " "be < 256, in position %d.", lptr->wlen, pos))); state = LQPRS_WAITVAR; } else if (charlen == 1 && t_iseq(ptr, '.')) { lptr->len = ptr - lptr->start - ((lptr->flag & LVAR_SUBLEXEME) ? 1 : 0) - ((lptr->flag & LVAR_INCASE) ? 1 : 0) - ((lptr->flag & LVAR_ANYEND) ? 1 : 0); if (lptr->wlen > 255) ereport(ERROR, (errcode(ERRCODE_NAME_TOO_LONG), errmsg("name of level is too long"), errdetail("Name length is %d, must " "be < 256, in position %d.", lptr->wlen, pos))); state = LQPRS_WAITLEVEL; curqlevel = NEXTLEV(curqlevel); } else if (ISALNUM(ptr)) { if (lptr->flag) UNCHAR; } else UNCHAR; } else if (state == LQPRS_WAITOPEN) { if (charlen == 1 && t_iseq(ptr, '{')) state = LQPRS_WAITFNUM; else if (charlen == 1 && t_iseq(ptr, '.')) { curqlevel->low = 0; curqlevel->high = 0xffff; curqlevel = NEXTLEV(curqlevel); state = LQPRS_WAITLEVEL; } else UNCHAR; } else if (state == LQPRS_WAITFNUM) { if (charlen == 1 && t_iseq(ptr, ',')) state = LQPRS_WAITSNUM; else if (t_isdigit(ptr)) { curqlevel->low = atoi(ptr); state = LQPRS_WAITND; } else UNCHAR; } else if (state == LQPRS_WAITSNUM) { if (t_isdigit(ptr)) { curqlevel->high = atoi(ptr); state = LQPRS_WAITCLOSE; } else if (charlen == 1 && t_iseq(ptr, '}')) { curqlevel->high = 0xffff; state = LQPRS_WAITEND; } else UNCHAR; } else if (state == LQPRS_WAITCLOSE) { if (charlen == 1 && t_iseq(ptr, '}')) state = LQPRS_WAITEND; else if (!t_isdigit(ptr)) UNCHAR; } else if (state == LQPRS_WAITND) { if (charlen == 1 && t_iseq(ptr, '}')) { curqlevel->high = curqlevel->low; state = LQPRS_WAITEND; } else if (charlen == 1 && t_iseq(ptr, ',')) state = LQPRS_WAITSNUM; else if (!t_isdigit(ptr)) UNCHAR; } else if (state == LQPRS_WAITEND) { if (charlen == 1 && t_iseq(ptr, '.')) { state = LQPRS_WAITLEVEL; curqlevel = NEXTLEV(curqlevel); } else UNCHAR; } else /* internal error */ elog(ERROR, "internal error in parser"); ptr += charlen; if (state == LQPRS_WAITDELIM) lptr->wlen++; pos++; } if (state == LQPRS_WAITDELIM) { if (lptr->start == ptr) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("syntax error"), errdetail("Unexpected end of line."))); lptr->len = ptr - lptr->start - ((lptr->flag & LVAR_SUBLEXEME) ? 1 : 0) - ((lptr->flag & LVAR_INCASE) ? 1 : 0) - ((lptr->flag & LVAR_ANYEND) ? 1 : 0); if (lptr->len == 0) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("syntax error"), errdetail("Unexpected end of line."))); if (lptr->wlen > 255) ereport(ERROR, (errcode(ERRCODE_NAME_TOO_LONG), errmsg("name of level is too long"), errdetail("Name length is %d, must " "be < 256, in position %d.", lptr->wlen, pos))); } else if (state == LQPRS_WAITOPEN) curqlevel->high = 0xffff; else if (state != LQPRS_WAITEND) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("syntax error"), errdetail("Unexpected end of line."))); curqlevel = tmpql; totallen = LQUERY_HDRSIZE; while ((char *) curqlevel - (char *) tmpql < num * ITEMSIZE) { totallen += LQL_HDRSIZE; if (curqlevel->numvar) { lptr = GETVAR(curqlevel); while (lptr - GETVAR(curqlevel) < curqlevel->numvar) { totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len); lptr++; } } else if (curqlevel->low > curqlevel->high) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("syntax error"), errdetail("Low limit(%d) is greater than upper(%d).", curqlevel->low, curqlevel->high))); curqlevel = NEXTLEV(curqlevel); } result = (lquery *) palloc0(totallen); SET_VARSIZE(result, totallen); result->numlevel = num; result->firstgood = 0; result->flag = 0; if (hasnot) result->flag |= LQUERY_HASNOT; cur = LQUERY_FIRST(result); curqlevel = tmpql; while ((char *) curqlevel - (char *) tmpql < num * ITEMSIZE) { memcpy(cur, curqlevel, LQL_HDRSIZE); cur->totallen = LQL_HDRSIZE; if (curqlevel->numvar) { lrptr = LQL_FIRST(cur); lptr = GETVAR(curqlevel); while (lptr - GETVAR(curqlevel) < curqlevel->numvar) { cur->totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len); lrptr->len = lptr->len; lrptr->flag = lptr->flag; lrptr->val = ltree_crc32_sz(lptr->start, lptr->len); memcpy(lrptr->name, lptr->start, lptr->len); lptr++; lrptr = LVAR_NEXT(lrptr); } pfree(GETVAR(curqlevel)); if (cur->numvar > 1 || cur->flag != 0) wasbad = true; else if (wasbad == false) (result->firstgood)++; } else wasbad = true; curqlevel = NEXTLEV(curqlevel); cur = LQL_NEXT(cur); } pfree(tmpql); PG_RETURN_POINTER(result); }
/* * copy one file */ void copy_file(char *fromfile, char *tofile) { char *buffer; int srcfd; int dstfd; int nbytes; off_t offset; off_t flush_offset; /* Size of copy buffer (read and write requests) */ #define COPY_BUF_SIZE (8 * BLCKSZ) /* * Size of data flush requests. It seems beneficial on most platforms to * do this every 1MB or so. But macOS, at least with early releases of * APFS, is really unfriendly to small mmap/msync requests, so there do it * only every 32MB. */ #if defined(__darwin__) #define FLUSH_DISTANCE (32 * 1024 * 1024) #else #define FLUSH_DISTANCE (1024 * 1024) #endif /* Use palloc to ensure we get a maxaligned buffer */ buffer = palloc(COPY_BUF_SIZE); /* * Open the files */ srcfd = OpenTransientFile(fromfile, O_RDONLY | PG_BINARY); if (srcfd < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not open file \"%s\": %m", fromfile))); dstfd = OpenTransientFile(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY); if (dstfd < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not create file \"%s\": %m", tofile))); /* * Do the data copying. */ flush_offset = 0; for (offset = 0;; offset += nbytes) { /* If we got a cancel signal during the copy of the file, quit */ CHECK_FOR_INTERRUPTS(); /* * We fsync the files later, but during the copy, flush them every so * often to avoid spamming the cache and hopefully get the kernel to * start writing them out before the fsync comes. */ if (offset - flush_offset >= FLUSH_DISTANCE) { pg_flush_data(dstfd, flush_offset, offset - flush_offset); flush_offset = offset; } pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_READ); nbytes = read(srcfd, buffer, COPY_BUF_SIZE); pgstat_report_wait_end(); if (nbytes < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not read file \"%s\": %m", fromfile))); if (nbytes == 0) break; errno = 0; pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_WRITE); if ((int) write(dstfd, buffer, nbytes) != nbytes) { /* if write didn't set errno, assume problem is no disk space */ if (errno == 0) errno = ENOSPC; ereport(ERROR, (errcode_for_file_access(), errmsg("could not write to file \"%s\": %m", tofile))); } pgstat_report_wait_end(); } if (offset > flush_offset) pg_flush_data(dstfd, flush_offset, offset - flush_offset); if (CloseTransientFile(dstfd)) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", tofile))); if (CloseTransientFile(srcfd)) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", fromfile))); pfree(buffer); }
PGDLLEXPORT Datum kshortest_path(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; TupleDesc tuple_desc; General_path_element_t *path = NULL; size_t result_count = 0; /* stuff done only on the first call of the function */ if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); /* CREATE OR REPLACE FUNCTION _pgr_ksp( sql text, start_vid bigint, end_vid bigint, k integer, directed boolean, heap_paths boolean */ PGR_DBG("Calling process"); compute( text_to_cstring(PG_GETARG_TEXT_P(0)), /* SQL */ PG_GETARG_INT64(1), /* start_vid */ PG_GETARG_INT64(2), /* end_vid */ PG_GETARG_INT32(3), /* k */ PG_GETARG_BOOL(4), /* directed */ PG_GETARG_BOOL(5), /* heap_paths */ &path, &result_count); PGR_DBG("Total number of tuples to be returned %ld \n", result_count); /* */ /**********************************************************************/ #if PGSQL_VERSION > 95 funcctx->max_calls = result_count; #else funcctx->max_calls = (uint32_t)result_count; #endif funcctx->user_fctx = path; if (get_call_result_type(fcinfo, NULL, &tuple_desc) != TYPEFUNC_COMPOSITE) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record\n"))); funcctx->tuple_desc = tuple_desc; MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); tuple_desc = funcctx->tuple_desc; path = (General_path_element_t*) funcctx->user_fctx; if (funcctx->call_cntr < funcctx->max_calls) { /* do when there is more left to send */ HeapTuple tuple; Datum result; Datum *values; bool* nulls; values = palloc(7 * sizeof(Datum)); nulls = palloc(7 * sizeof(bool)); size_t i; for (i = 0; i < 7; ++i) { nulls[i] = false; } values[0] = Int32GetDatum(funcctx->call_cntr + 1); values[1] = Int32GetDatum(path[funcctx->call_cntr].start_id + 1); values[2] = Int32GetDatum(path[funcctx->call_cntr].seq); values[3] = Int64GetDatum(path[funcctx->call_cntr].node); values[4] = Int64GetDatum(path[funcctx->call_cntr].edge); values[5] = Float8GetDatum(path[funcctx->call_cntr].cost); values[6] = Float8GetDatum(path[funcctx->call_cntr].agg_cost); tuple = heap_form_tuple(tuple_desc, values, nulls); result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } else { /* do when there is no more left */ SRF_RETURN_DONE(funcctx); } }
Datum pg_logdir_ls(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; struct dirent *de; directory_fctx *fctx; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("only superuser can list the log directory")))); if (strcmp(Log_filename, "postgresql-%Y-%m-%d_%H%M%S.log") != 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errmsg("the log_filename parameter must equal 'postgresql-%%Y-%%m-%%d_%%H%%M%%S.log'")))); if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; TupleDesc tupdesc; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); fctx = palloc(sizeof(directory_fctx)); tupdesc = CreateTemplateTupleDesc(2, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "starttime", TIMESTAMPOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "filename", TEXTOID, -1, 0); funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); fctx->location = pstrdup(Log_directory); fctx->dirdesc = AllocateDir(fctx->location); if (!fctx->dirdesc) ereport(ERROR, (errcode_for_file_access(), errmsg("could not read directory \"%s\": %m", fctx->location))); funcctx->user_fctx = fctx; MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); fctx = (directory_fctx *) funcctx->user_fctx; while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL) { char *values[2]; HeapTuple tuple; char timestampbuf[32]; char *field[MAXDATEFIELDS]; char lowstr[MAXDATELEN + 1]; int dtype; int nf, ftype[MAXDATEFIELDS]; fsec_t fsec; int tz = 0; struct pg_tm date; /* * Default format: postgresql-YYYY-MM-DD_HHMMSS.log */ if (strlen(de->d_name) != 32 || strncmp(de->d_name, "postgresql-", 11) != 0 || de->d_name[21] != '_' || strcmp(de->d_name + 28, ".log") != 0) continue; /* extract timestamp portion of filename */ strcpy(timestampbuf, de->d_name + 11); timestampbuf[17] = '\0'; /* parse and decode expected timestamp to verify it's OK format */ if (ParseDateTime(timestampbuf, lowstr, MAXDATELEN, field, ftype, MAXDATEFIELDS, &nf)) continue; if (DecodeDateTime(field, ftype, nf, &dtype, &date, &fsec, &tz)) continue; /* Seems the timestamp is OK; prepare and return tuple */ values[0] = timestampbuf; values[1] = psprintf("%s/%s", fctx->location, de->d_name); tuple = BuildTupleFromCStrings(funcctx->attinmeta, values); SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); } FreeDir(fctx->dirdesc); SRF_RETURN_DONE(funcctx); }
/* * get_relation_info - * Retrieves catalog information for a given relation. * * Given the Oid of the relation, return the following info into fields * of the RelOptInfo struct: * * min_attr lowest valid AttrNumber * max_attr highest valid AttrNumber * indexlist list of IndexOptInfos for relation's indexes * fdwroutine if it's a foreign table, the FDW function pointers * pages number of pages * tuples number of tuples * * Also, initialize the attr_needed[] and attr_widths[] arrays. In most * cases these are left as zeroes, but sometimes we need to compute attr * widths here, and we may as well cache the results for costsize.c. * * If inhparent is true, all we need to do is set up the attr arrays: * the RelOptInfo actually represents the appendrel formed by an inheritance * tree, and so the parent rel's physical size and index information isn't * important for it. */ void get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, RelOptInfo *rel) { Index varno = rel->relid; Relation relation; bool hasindex; List *indexinfos = NIL; /* * We need not lock the relation since it was already locked, either by * the rewriter or when expand_inherited_rtentry() added it to the query's * rangetable. */ relation = heap_open(relationObjectId, NoLock); /* Temporary and unlogged relations are inaccessible during recovery. */ if (!RelationNeedsWAL(relation) && RecoveryInProgress()) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot access temporary or unlogged relations during recovery"))); rel->min_attr = FirstLowInvalidHeapAttributeNumber + 1; rel->max_attr = RelationGetNumberOfAttributes(relation); rel->reltablespace = RelationGetForm(relation)->reltablespace; Assert(rel->max_attr >= rel->min_attr); rel->attr_needed = (Relids *) palloc0((rel->max_attr - rel->min_attr + 1) * sizeof(Relids)); rel->attr_widths = (int32 *) palloc0((rel->max_attr - rel->min_attr + 1) * sizeof(int32)); /* * Estimate relation size --- unless it's an inheritance parent, in which * case the size will be computed later in set_append_rel_pathlist, and we * must leave it zero for now to avoid bollixing the total_table_pages * calculation. */ if (!inhparent) estimate_rel_size(relation, rel->attr_widths - rel->min_attr, &rel->pages, &rel->tuples, &rel->allvisfrac); /* * Make list of indexes. Ignore indexes on system catalogs if told to. * Don't bother with indexes for an inheritance parent, either. */ if (inhparent || (IgnoreSystemIndexes && IsSystemRelation(relation))) hasindex = false; else hasindex = relation->rd_rel->relhasindex; if (hasindex) { List *indexoidlist; ListCell *l; LOCKMODE lmode; indexoidlist = RelationGetIndexList(relation); /* * For each index, we get the same type of lock that the executor will * need, and do not release it. This saves a couple of trips to the * shared lock manager while not creating any real loss of * concurrency, because no schema changes could be happening on the * index while we hold lock on the parent rel, and neither lock type * blocks any other kind of index operation. */ if (rel->relid == root->parse->resultRelation) lmode = RowExclusiveLock; else lmode = AccessShareLock; foreach(l, indexoidlist) { Oid indexoid = lfirst_oid(l); Relation indexRelation; Form_pg_index index; IndexOptInfo *info; int ncolumns; int i; /* * Extract info from the relation descriptor for the index. */ indexRelation = index_open(indexoid, lmode); index = indexRelation->rd_index; /* * Ignore invalid indexes, since they can't safely be used for * queries. Note that this is OK because the data structure we * are constructing is only used by the planner --- the executor * still needs to insert into "invalid" indexes, if they're marked * IndexIsReady. */ if (!IndexIsValid(index)) { index_close(indexRelation, NoLock); continue; } /* * If the index is valid, but cannot yet be used, ignore it; but * mark the plan we are generating as transient. See * src/backend/access/heap/README.HOT for discussion. */ if (index->indcheckxmin && !TransactionIdPrecedes(HeapTupleHeaderGetXmin(indexRelation->rd_indextuple->t_data), TransactionXmin)) { root->glob->transientPlan = true; index_close(indexRelation, NoLock); continue; } info = makeNode(IndexOptInfo); info->indexoid = index->indexrelid; info->reltablespace = RelationGetForm(indexRelation)->reltablespace; info->rel = rel; info->ncolumns = ncolumns = index->indnatts; info->indexkeys = (int *) palloc(sizeof(int) * ncolumns); info->indexcollations = (Oid *) palloc(sizeof(Oid) * ncolumns); info->opfamily = (Oid *) palloc(sizeof(Oid) * ncolumns); info->opcintype = (Oid *) palloc(sizeof(Oid) * ncolumns); for (i = 0; i < ncolumns; i++) { info->indexkeys[i] = index->indkey.values[i]; info->indexcollations[i] = indexRelation->rd_indcollation[i]; info->opfamily[i] = indexRelation->rd_opfamily[i]; info->opcintype[i] = indexRelation->rd_opcintype[i]; } info->relam = indexRelation->rd_rel->relam; info->amcostestimate = indexRelation->rd_am->amcostestimate; info->canreturn = index_can_return(indexRelation); info->amcanorderbyop = indexRelation->rd_am->amcanorderbyop; info->amoptionalkey = indexRelation->rd_am->amoptionalkey; info->amsearcharray = indexRelation->rd_am->amsearcharray; info->amsearchnulls = indexRelation->rd_am->amsearchnulls; info->amhasgettuple = OidIsValid(indexRelation->rd_am->amgettuple); info->amhasgetbitmap = OidIsValid(indexRelation->rd_am->amgetbitmap); /* * Fetch the ordering information for the index, if any. */ if (info->relam == BTREE_AM_OID) { /* * If it's a btree index, we can use its opfamily OIDs * directly as the sort ordering opfamily OIDs. */ Assert(indexRelation->rd_am->amcanorder); info->sortopfamily = info->opfamily; info->reverse_sort = (bool *) palloc(sizeof(bool) * ncolumns); info->nulls_first = (bool *) palloc(sizeof(bool) * ncolumns); for (i = 0; i < ncolumns; i++) { int16 opt = indexRelation->rd_indoption[i]; info->reverse_sort[i] = (opt & INDOPTION_DESC) != 0; info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0; } } else if (indexRelation->rd_am->amcanorder) { /* * Otherwise, identify the corresponding btree opfamilies by * trying to map this index's "<" operators into btree. Since * "<" uniquely defines the behavior of a sort order, this is * a sufficient test. * * XXX This method is rather slow and also requires the * undesirable assumption that the other index AM numbers its * strategies the same as btree. It'd be better to have a way * to explicitly declare the corresponding btree opfamily for * each opfamily of the other index type. But given the lack * of current or foreseeable amcanorder index types, it's not * worth expending more effort on now. */ info->sortopfamily = (Oid *) palloc(sizeof(Oid) * ncolumns); info->reverse_sort = (bool *) palloc(sizeof(bool) * ncolumns); info->nulls_first = (bool *) palloc(sizeof(bool) * ncolumns); for (i = 0; i < ncolumns; i++) { int16 opt = indexRelation->rd_indoption[i]; Oid ltopr; Oid btopfamily; Oid btopcintype; int16 btstrategy; info->reverse_sort[i] = (opt & INDOPTION_DESC) != 0; info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0; ltopr = get_opfamily_member(info->opfamily[i], info->opcintype[i], info->opcintype[i], BTLessStrategyNumber); if (OidIsValid(ltopr) && get_ordering_op_properties(ltopr, &btopfamily, &btopcintype, &btstrategy) && btopcintype == info->opcintype[i] && btstrategy == BTLessStrategyNumber) { /* Successful mapping */ info->sortopfamily[i] = btopfamily; } else { /* Fail ... quietly treat index as unordered */ info->sortopfamily = NULL; info->reverse_sort = NULL; info->nulls_first = NULL; break; } } } else { info->sortopfamily = NULL; info->reverse_sort = NULL; info->nulls_first = NULL; } /* * Fetch the index expressions and predicate, if any. We must * modify the copies we obtain from the relcache to have the * correct varno for the parent relation, so that they match up * correctly against qual clauses. */ info->indexprs = RelationGetIndexExpressions(indexRelation); info->indpred = RelationGetIndexPredicate(indexRelation); if (info->indexprs && varno != 1) ChangeVarNodes((Node *) info->indexprs, 1, varno, 0); if (info->indpred && varno != 1) ChangeVarNodes((Node *) info->indpred, 1, varno, 0); /* Build targetlist using the completed indexprs data */ info->indextlist = build_index_tlist(root, info, relation); info->predOK = false; /* set later in indxpath.c */ info->unique = index->indisunique; info->immediate = index->indimmediate; info->hypothetical = false; /* * Estimate the index size. If it's not a partial index, we lock * the number-of-tuples estimate to equal the parent table; if it * is partial then we have to use the same methods as we would for * a table, except we can be sure that the index is not larger * than the table. */ if (info->indpred == NIL) { info->pages = RelationGetNumberOfBlocks(indexRelation); info->tuples = rel->tuples; } else { double allvisfrac; /* dummy */ estimate_rel_size(indexRelation, NULL, &info->pages, &info->tuples, &allvisfrac); if (info->tuples > rel->tuples) info->tuples = rel->tuples; } if (info->relam == BTREE_AM_OID) { /* For btrees, get tree height while we have the index open */ info->tree_height = _bt_getrootheight(indexRelation); } else { /* For other index types, just set it to "unknown" for now */ info->tree_height = -1; } index_close(indexRelation, NoLock); indexinfos = lcons(info, indexinfos); } list_free(indexoidlist); }
/* * intorel_startup --- executor startup */ static void intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo) { DR_intorel *myState = (DR_intorel *) self; IntoClause *into = myState->into; bool is_matview; char relkind; CreateStmt *create; ObjectAddress intoRelationAddr; Relation intoRelationDesc; RangeTblEntry *rte; Datum toast_options; ListCell *lc; int attnum; static char *validnsps[] = HEAP_RELOPT_NAMESPACES; Assert(into != NULL); /* else somebody forgot to set it */ /* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */ is_matview = (into->viewQuery != NULL); relkind = is_matview ? RELKIND_MATVIEW : RELKIND_RELATION; /* * Create the target relation by faking up a CREATE TABLE parsetree and * passing it to DefineRelation. */ create = makeNode(CreateStmt); create->relation = into->rel; create->tableElts = NIL; /* will fill below */ create->inhRelations = NIL; create->ofTypename = NULL; create->constraints = NIL; create->options = into->options; create->oncommit = into->onCommit; create->tablespacename = into->tableSpaceName; create->if_not_exists = false; /* * Build column definitions using "pre-cooked" type and collation info. If * a column name list was specified in CREATE TABLE AS, override the * column names derived from the query. (Too few column names are OK, too * many are not.) */ lc = list_head(into->colNames); for (attnum = 0; attnum < typeinfo->natts; attnum++) { Form_pg_attribute attribute = typeinfo->attrs[attnum]; ColumnDef *col = makeNode(ColumnDef); TypeName *coltype = makeNode(TypeName); if (lc) { col->colname = strVal(lfirst(lc)); lc = lnext(lc); } else col->colname = NameStr(attribute->attname); col->typeName = coltype; col->inhcount = 0; col->is_local = true; col->is_not_null = false; col->is_from_type = false; col->storage = 0; col->raw_default = NULL; col->cooked_default = NULL; col->collClause = NULL; col->collOid = attribute->attcollation; col->constraints = NIL; col->fdwoptions = NIL; col->location = -1; coltype->names = NIL; coltype->typeOid = attribute->atttypid; coltype->setof = false; coltype->pct_type = false; coltype->typmods = NIL; coltype->typemod = attribute->atttypmod; coltype->arrayBounds = NIL; coltype->location = -1; /* * It's possible that the column is of a collatable type but the * collation could not be resolved, so double-check. (We must check * this here because DefineRelation would adopt the type's default * collation rather than complaining.) */ if (!OidIsValid(col->collOid) && type_is_collatable(coltype->typeOid)) ereport(ERROR, (errcode(ERRCODE_INDETERMINATE_COLLATION), errmsg("no collation was derived for column \"%s\" with collatable type %s", col->colname, format_type_be(coltype->typeOid)), errhint("Use the COLLATE clause to set the collation explicitly."))); create->tableElts = lappend(create->tableElts, col); } if (lc != NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("too many column names were specified"))); /* * Actually create the target table */ intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL); /* * If necessary, create a TOAST table for the target table. Note that * NewRelationCreateToastTable ends with CommandCounterIncrement(), so * that the TOAST table will be visible for insertion. */ CommandCounterIncrement(); /* parse and validate reloptions for the toast table */ toast_options = transformRelOptions((Datum) 0, create->options, "toast", validnsps, true, false); (void) heap_reloptions(RELKIND_TOASTVALUE, toast_options, true); NewRelationCreateToastTable(intoRelationAddr.objectId, toast_options); /* Create the "view" part of a materialized view. */ if (is_matview) { /* StoreViewQuery scribbles on tree, so make a copy */ Query *query = (Query *) copyObject(into->viewQuery); StoreViewQuery(intoRelationAddr.objectId, query, false); CommandCounterIncrement(); } /* * Finally we can open the target table */ intoRelationDesc = heap_open(intoRelationAddr.objectId, AccessExclusiveLock); /* * Check INSERT permission on the constructed table. * * XXX: It would arguably make sense to skip this check if into->skipData * is true. */ rte = makeNode(RangeTblEntry); rte->rtekind = RTE_RELATION; rte->relid = intoRelationAddr.objectId; rte->relkind = relkind; rte->requiredPerms = ACL_INSERT; for (attnum = 1; attnum <= intoRelationDesc->rd_att->natts; attnum++) rte->insertedCols = bms_add_member(rte->insertedCols, attnum - FirstLowInvalidHeapAttributeNumber); ExecCheckRTPerms(list_make1(rte), true); /* * Make sure the constructed table does not have RLS enabled. * * check_enable_rls() will ereport(ERROR) itself if the user has requested * something invalid, and otherwise will return RLS_ENABLED if RLS should * be enabled here. We don't actually support that currently, so throw * our own ereport(ERROR) if that happens. */ if (check_enable_rls(intoRelationAddr.objectId, InvalidOid, false) == RLS_ENABLED) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errmsg("policies not yet implemented for this command")))); /* * Tentatively mark the target as populated, if it's a matview and we're * going to fill it; otherwise, no change needed. */ if (is_matview && !into->skipData) SetMatViewPopulatedState(intoRelationDesc, true); /* * Fill private fields of myState for use by later routines */ myState->rel = intoRelationDesc; myState->output_cid = GetCurrentCommandId(true); /* and remember the new relation's address for ExecCreateTableAs */ CreateAsReladdr = intoRelationAddr; /* * We can skip WAL-logging the insertions, unless PITR or streaming * replication is in use. We can skip the FSM in any case. */ myState->hi_options = HEAP_INSERT_SKIP_FSM | (XLogIsNeeded() ? 0 : HEAP_INSERT_SKIP_WAL); myState->bistate = GetBulkInsertState(); /* Not using WAL requires smgr_targblock be initially invalid */ Assert(RelationGetTargetBlock(intoRelationDesc) == InvalidBlockNumber); }
/* * parsetinterval -- parse a tinterval string * * output parameters: * i_start, i_end: tinterval margins * * Time interval: * `[' {` '} `'' <AbsTime> `'' {` '} `'' <AbsTime> `'' {` '} `]' * * OR `Undefined Range' (see also INVALID_INTERVAL_STR) * * where <AbsTime> satisfies the syntax of absolute time. * * e.g. [ ' Jan 18 1902' 'Jan 1 00:00:00 1970'] */ static void parsetinterval(char *i_string, AbsoluteTime *i_start, AbsoluteTime *i_end) { char *p, *p1; char c; p = i_string; /* skip leading blanks up to '[' */ while ((c = *p) != '\0') { if (IsSpace(c)) p++; else if (c != '[') goto bogus; /* syntax error */ else break; } if (c == '\0') goto bogus; /* syntax error */ p++; /* skip leading blanks up to '"' */ while ((c = *p) != '\0') { if (IsSpace(c)) p++; else if (c != '"') goto bogus; /* syntax error */ else break; } if (c == '\0') goto bogus; /* syntax error */ p++; if (strncmp(INVALID_INTERVAL_STR, p, strlen(INVALID_INTERVAL_STR)) == 0) goto bogus; /* undefined range, handled like a syntax err. */ /* search for the end of the first date and change it to a \0 */ p1 = p; while ((c = *p1) != '\0') { if (c == '"') break; p1++; } if (c == '\0') goto bogus; /* syntax error */ *p1 = '\0'; /* get the first date */ *i_start = DatumGetAbsoluteTime(DirectFunctionCall1(abstimein, CStringGetDatum(p))); /* undo change to \0 */ *p1 = c; p = ++p1; /* skip blanks up to '"', beginning of second date */ while ((c = *p) != '\0') { if (IsSpace(c)) p++; else if (c != '"') goto bogus; /* syntax error */ else break; } if (c == '\0') goto bogus; /* syntax error */ p++; /* search for the end of the second date and change it to a \0 */ p1 = p; while ((c = *p1) != '\0') { if (c == '"') break; p1++; } if (c == '\0') goto bogus; /* syntax error */ *p1 = '\0'; /* get the second date */ *i_end = DatumGetAbsoluteTime(DirectFunctionCall1(abstimein, CStringGetDatum(p))); /* undo change to \0 */ *p1 = c; p = ++p1; /* skip blanks up to ']' */ while ((c = *p) != '\0') { if (IsSpace(c)) p++; else if (c != ']') goto bogus; /* syntax error */ else break; } if (c == '\0') goto bogus; /* syntax error */ p++; c = *p; if (c != '\0') goto bogus; /* syntax error */ /* it seems to be a valid tinterval */ return; bogus: ereport(ERROR, (errcode(ERRCODE_INVALID_DATETIME_FORMAT), errmsg("invalid input syntax for type tinterval: \"%s\"", i_string))); *i_start = *i_end = INVALID_ABSTIME; /* keep compiler quiet */ }
void abstime2tm(AbsoluteTime _time, int *tzp, struct pg_tm * tm, char **tzn) { pg_time_t time = (pg_time_t) _time; struct pg_tm *tx; /* * If HasCTZSet is true then we have a brute force time zone specified. Go * ahead and rotate to the local time zone since we will later bypass any * calls which adjust the tm fields. */ if (HasCTZSet && (tzp != NULL)) time -= CTimeZone; if (!HasCTZSet && tzp != NULL) tx = pg_localtime(&time, session_timezone); else tx = pg_gmtime(&time); tm->tm_year = tx->tm_year + 1900; tm->tm_mon = tx->tm_mon + 1; tm->tm_mday = tx->tm_mday; tm->tm_hour = tx->tm_hour; tm->tm_min = tx->tm_min; tm->tm_sec = tx->tm_sec; tm->tm_isdst = tx->tm_isdst; tm->tm_gmtoff = tx->tm_gmtoff; tm->tm_zone = tx->tm_zone; if (tzp != NULL) { /* * We have a brute force time zone per SQL99? Then use it without * change since we have already rotated to the time zone. */ if (HasCTZSet) { *tzp = CTimeZone; tm->tm_gmtoff = CTimeZone; tm->tm_isdst = 0; tm->tm_zone = NULL; if (tzn != NULL) *tzn = NULL; } else { *tzp = -tm->tm_gmtoff; /* tm_gmtoff is Sun/DEC-ism */ /* * XXX FreeBSD man pages indicate that this should work - tgl * 97/04/23 */ if (tzn != NULL) { /* * Copy no more than MAXTZLEN bytes of timezone to tzn, in * case it contains an error message, which doesn't fit in the * buffer */ StrNCpy(*tzn, tm->tm_zone, MAXTZLEN + 1); if (strlen(tm->tm_zone) > MAXTZLEN) ereport(WARNING, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid time zone name: \"%s\"", tm->tm_zone))); } } } else tm->tm_isdst = -1; }
/* * ApplicableOpExpressionList walks over all filter clauses that relate to this * foreign table, and chooses applicable clauses that we know we can translate * into Mongo queries. Currently, these clauses include comparison expressions * that have a column and a constant as arguments. For example, "o_orderdate >= * date '1994-01-01' + interval '1' year" is an applicable expression. */ List * ApplicableOpExpressionList(RelOptInfo *baserel) { List *opExpressionList = NIL; List *restrictInfoList = baserel->baserestrictinfo; ListCell *restrictInfoCell = NULL; foreach(restrictInfoCell, restrictInfoList) { RestrictInfo *restrictInfo = (RestrictInfo *) lfirst(restrictInfoCell); Expr *expression = restrictInfo->clause; NodeTag expressionType = 0; OpExpr *opExpression = NULL; char *operatorName = NULL; char *mongoOperatorName = NULL; List *argumentList = NIL; Var *column = NULL; Const *constant = NULL; bool equalsOperator = false; bool constantIsArray = false; /* we only support operator expressions */ expressionType = nodeTag(expression); if (expressionType != T_OpExpr) { continue; } opExpression = (OpExpr *) expression; operatorName = get_opname(opExpression->opno); /* we only support =, <, >, <=, >=, and <> operators */ if (strncmp(operatorName, EQUALITY_OPERATOR_NAME, NAMEDATALEN) == 0) { equalsOperator = true; } mongoOperatorName = MongoOperatorName(operatorName); if (!equalsOperator && mongoOperatorName == NULL) { ereport(INFO, (errmsg_internal("Ignoring unsupported operator %s", operatorName))); continue; } /* * We only support simple binary operators that compare a column against * a constant. If the expression is a tree, we don't recurse into it. */ argumentList = opExpression->args; column = (Var *) FindArgumentOfType(argumentList, T_Var); constant = (Const *) FindArgumentOfType(argumentList, T_Const); /* * We don't push down operators where the constant is an array, since * conditional operators for arrays in MongoDB aren't properly defined. * For example, {similar_products : [ "B0009S4IJW", "6301964144" ]} * finds results that are equal to the array, but {similar_products: * {$gte: [ "B0009S4IJW", "6301964144" ]}} returns an empty set. */ if (constant != NULL) { Oid constantArrayTypeId = get_element_type(constant->consttype); if (constantArrayTypeId != InvalidOid) { constantIsArray = true; ereport(INFO, (errmsg_internal("Ignoring %s expression with array", operatorName))); } } else { ereport(INFO, (errmsg_internal("Ignoring %s expression without a constant", operatorName))); } if (column != NULL && constant != NULL && !constantIsArray) { opExpressionList = lappend(opExpressionList, opExpression); } }
Datum plpgsql_validator(PG_FUNCTION_ARGS) { Oid funcoid = PG_GETARG_OID(0); HeapTuple tuple; Form_pg_proc proc; char functyptype; int numargs; Oid *argtypes; char **argnames; char *argmodes; bool istrigger = false; int i; /* Get the new function's pg_proc entry */ tuple = SearchSysCache(PROCOID, ObjectIdGetDatum(funcoid), 0, 0, 0); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for function %u", funcoid); proc = (Form_pg_proc) GETSTRUCT(tuple); functyptype = get_typtype(proc->prorettype); /* Disallow pseudotype result */ /* except for TRIGGER, RECORD, VOID, ANYARRAY, or ANYELEMENT */ if (functyptype == 'p') { /* we assume OPAQUE with no arguments means a trigger */ if (proc->prorettype == TRIGGEROID || (proc->prorettype == OPAQUEOID && proc->pronargs == 0)) istrigger = true; else if (proc->prorettype != RECORDOID && proc->prorettype != VOIDOID && proc->prorettype != ANYARRAYOID && proc->prorettype != ANYELEMENTOID) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("plpgsql functions cannot return type %s", format_type_be(proc->prorettype)))); } /* Disallow pseudotypes in arguments (either IN or OUT) */ /* except for ANYARRAY or ANYELEMENT */ numargs = get_func_arg_info(tuple, &argtypes, &argnames, &argmodes); for (i = 0; i < numargs; i++) { if (get_typtype(argtypes[i]) == 'p') { if (argtypes[i] != ANYARRAYOID && argtypes[i] != ANYELEMENTOID) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("plpgsql functions cannot take type %s", format_type_be(argtypes[i])))); } } /* Postpone body checks if !check_function_bodies */ if (check_function_bodies) { FunctionCallInfoData fake_fcinfo; FmgrInfo flinfo; TriggerData trigdata; int rc; /* * Connect to SPI manager (is this needed for compilation?) */ if ((rc = SPI_connect()) != SPI_OK_CONNECT) elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc)); /* * Set up a fake fcinfo with just enough info to satisfy * plpgsql_compile(). */ MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo)); MemSet(&flinfo, 0, sizeof(flinfo)); fake_fcinfo.flinfo = &flinfo; flinfo.fn_oid = funcoid; flinfo.fn_mcxt = CurrentMemoryContext; if (istrigger) { MemSet(&trigdata, 0, sizeof(trigdata)); trigdata.type = T_TriggerData; fake_fcinfo.context = (Node *) &trigdata; } /* Test-compile the function */ plpgsql_compile(&fake_fcinfo, true); /* * Disconnect from SPI manager */ if ((rc = SPI_finish()) != SPI_OK_FINISH) elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc)); } ReleaseSysCache(tuple); PG_RETURN_VOID(); }
/* * 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; ReleaseSysCache(tup); return (Expr *) result; }
/* * Wait for activity on one or more sockets. * While waiting, allow signals to run * * NOTE! Currently does not implement exceptfds check, * since it is not used in postgresql! */ int pgwin32_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timeval * timeout) { WSAEVENT events[FD_SETSIZE * 2]; /* worst case is readfds totally * different from writefds, so * 2*FD_SETSIZE sockets */ SOCKET sockets[FD_SETSIZE * 2]; int numevents = 0; int i; int r; DWORD timeoutval = WSA_INFINITE; FD_SET outreadfds; FD_SET outwritefds; int nummatches = 0; Assert(exceptfds == NULL); if (pgwin32_poll_signals()) return -1; FD_ZERO(&outreadfds); FD_ZERO(&outwritefds); /* * Write FDs are different in the way that it is only flagged by * WSASelectEvent() if we have tried to write to them first. So try an * empty write */ if (writefds) { for (i = 0; i < writefds->fd_count; i++) { char c; WSABUF buf; DWORD sent; buf.buf = &c; buf.len = 0; r = WSASend(writefds->fd_array[i], &buf, 1, &sent, 0, NULL, NULL); if (r == 0) /* Completed - means things are fine! */ FD_SET(writefds->fd_array[i], &outwritefds); else { /* Not completed */ if (WSAGetLastError() != WSAEWOULDBLOCK) /* * Not completed, and not just "would block", so an error * occured */ FD_SET(writefds->fd_array[i], &outwritefds); } } if (outwritefds.fd_count > 0) { memcpy(writefds, &outwritefds, sizeof(fd_set)); if (readfds) FD_ZERO(readfds); return outwritefds.fd_count; } } /* Now set up for an actual select */ if (timeout != NULL) { /* timeoutval is in milliseconds */ timeoutval = timeout->tv_sec * 1000 + timeout->tv_usec / 1000; } if (readfds != NULL) { for (i = 0; i < readfds->fd_count; i++) { events[numevents] = WSACreateEvent(); sockets[numevents] = readfds->fd_array[i]; numevents++; } } if (writefds != NULL) { for (i = 0; i < writefds->fd_count; i++) { if (!readfds || !FD_ISSET(writefds->fd_array[i], readfds)) { /* If the socket is not in the read list */ events[numevents] = WSACreateEvent(); sockets[numevents] = writefds->fd_array[i]; numevents++; } } } for (i = 0; i < numevents; i++) { int flags = 0; if (readfds && FD_ISSET(sockets[i], readfds)) flags |= FD_READ | FD_ACCEPT | FD_CLOSE; if (writefds && FD_ISSET(sockets[i], writefds)) flags |= FD_WRITE | FD_CLOSE; if (WSAEventSelect(sockets[i], events[i], flags) == SOCKET_ERROR) { TranslateSocketError(); for (i = 0; i < numevents; i++) WSACloseEvent(events[i]); return -1; } } events[numevents] = pgwin32_signal_event; r = WaitForMultipleObjectsEx(numevents + 1, events, FALSE, timeoutval, TRUE); if (r != WAIT_TIMEOUT && r != WAIT_IO_COMPLETION && r != (WAIT_OBJECT_0 + numevents)) { /* * We scan all events, even those not signalled, in case more than one * event has been tagged but Wait.. can only return one. */ WSANETWORKEVENTS resEvents; for (i = 0; i < numevents; i++) { ZeroMemory(&resEvents, sizeof(resEvents)); if (WSAEnumNetworkEvents(sockets[i], events[i], &resEvents) == SOCKET_ERROR) ereport(FATAL, (errmsg_internal("failed to enumerate network events: %i", (int) GetLastError()))); /* Read activity? */ if (readfds && FD_ISSET(sockets[i], readfds)) { if ((resEvents.lNetworkEvents & FD_READ) || (resEvents.lNetworkEvents & FD_ACCEPT) || (resEvents.lNetworkEvents & FD_CLOSE)) { FD_SET(sockets[i], &outreadfds); nummatches++; } } /* Write activity? */ if (writefds && FD_ISSET(sockets[i], writefds)) { if ((resEvents.lNetworkEvents & FD_WRITE) || (resEvents.lNetworkEvents & FD_CLOSE)) { FD_SET(sockets[i], &outwritefds); nummatches++; } } } } /* Clean up all handles */ for (i = 0; i < numevents; i++) { WSAEventSelect(sockets[i], events[i], 0); WSACloseEvent(events[i]); } if (r == WSA_WAIT_TIMEOUT) { if (readfds) FD_ZERO(readfds); if (writefds) FD_ZERO(writefds); return 0; } if (r == WAIT_OBJECT_0 + numevents) { pgwin32_dispatch_queued_signals(); errno = EINTR; if (readfds) FD_ZERO(readfds); if (writefds) FD_ZERO(writefds); return -1; } /* Overwrite socket sets with our resulting values */ if (readfds) memcpy(readfds, &outreadfds, sizeof(fd_set)); if (writefds) memcpy(writefds, &outwritefds, sizeof(fd_set)); return nummatches; }
/* * ExecCreateTableAs -- execute a CREATE TABLE AS command */ ObjectAddress ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString, ParamListInfo params, char *completionTag) { Query *query = (Query *) stmt->query; IntoClause *into = stmt->into; bool is_matview = (into->viewQuery != NULL); DestReceiver *dest; Oid save_userid = InvalidOid; int save_sec_context = 0; int save_nestlevel = 0; ObjectAddress address; List *rewritten; PlannedStmt *plan; QueryDesc *queryDesc; ScanDirection dir; if (stmt->if_not_exists) { Oid nspid; nspid = RangeVarGetCreationNamespace(stmt->into->rel); if (get_relname_relid(stmt->into->rel->relname, nspid)) { ereport(NOTICE, (errcode(ERRCODE_DUPLICATE_TABLE), errmsg("relation \"%s\" already exists, skipping", stmt->into->rel->relname))); return InvalidObjectAddress; } } /* * Create the tuple receiver object and insert info it will need */ dest = CreateIntoRelDestReceiver(into); /* * The contained Query could be a SELECT, or an EXECUTE utility command. * If the latter, we just pass it off to ExecuteQuery. */ Assert(IsA(query, Query)); if (query->commandType == CMD_UTILITY && IsA(query->utilityStmt, ExecuteStmt)) { ExecuteStmt *estmt = (ExecuteStmt *) query->utilityStmt; Assert(!is_matview); /* excluded by syntax */ ExecuteQuery(estmt, into, queryString, params, dest, completionTag); address = CreateAsReladdr; CreateAsReladdr = InvalidObjectAddress; return address; } Assert(query->commandType == CMD_SELECT); /* * For materialized views, lock down security-restricted operations and * arrange to make GUC variable changes local to this command. This is * not necessary for security, but this keeps the behavior similar to * REFRESH MATERIALIZED VIEW. Otherwise, one could create a materialized * view not possible to refresh. */ if (is_matview) { GetUserIdAndSecContext(&save_userid, &save_sec_context); SetUserIdAndSecContext(save_userid, save_sec_context | SECURITY_RESTRICTED_OPERATION); save_nestlevel = NewGUCNestLevel(); } /* * Parse analysis was done already, but we still have to run the rule * rewriter. We do not do AcquireRewriteLocks: we assume the query either * came straight from the parser, or suitable locks were acquired by * plancache.c. * * Because the rewriter and planner tend to scribble on the input, we make * a preliminary copy of the source querytree. This prevents problems in * the case that CTAS is in a portal or plpgsql function and is executed * repeatedly. (See also the same hack in EXPLAIN and PREPARE.) */ rewritten = QueryRewrite((Query *) copyObject(query)); /* SELECT should never rewrite to more or less than one SELECT query */ if (list_length(rewritten) != 1) elog(ERROR, "unexpected rewrite result for CREATE TABLE AS SELECT"); query = (Query *) linitial(rewritten); Assert(query->commandType == CMD_SELECT); /* plan the query */ plan = pg_plan_query(query, 0, params); /* * Use a snapshot with an updated command ID to ensure this query sees * results of any previously executed queries. (This could only matter if * the planner executed an allegedly-stable function that changed the * database contents, but let's do it anyway to be parallel to the EXPLAIN * code path.) */ PushCopiedSnapshot(GetActiveSnapshot()); UpdateActiveSnapshotCommandId(); /* Create a QueryDesc, redirecting output to our tuple receiver */ queryDesc = CreateQueryDesc(plan, queryString, GetActiveSnapshot(), InvalidSnapshot, dest, params, 0); /* call ExecutorStart to prepare the plan for execution */ ExecutorStart(queryDesc, GetIntoRelEFlags(into)); /* * Normally, we run the plan to completion; but if skipData is specified, * just do tuple receiver startup and shutdown. */ if (into->skipData) dir = NoMovementScanDirection; else dir = ForwardScanDirection; /* run the plan */ ExecutorRun(queryDesc, dir, 0L); /* save the rowcount if we're given a completionTag to fill */ if (completionTag) snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "SELECT %u", queryDesc->estate->es_processed); /* and clean up */ ExecutorFinish(queryDesc); ExecutorEnd(queryDesc); FreeQueryDesc(queryDesc); PopActiveSnapshot(); if (is_matview) { /* Roll back any GUC changes */ AtEOXact_GUC(false, save_nestlevel); /* Restore userid and security context */ SetUserIdAndSecContext(save_userid, save_sec_context); } address = CreateAsReladdr; CreateAsReladdr = InvalidObjectAddress; return address; }
/* * DefineOperator * this function extracts all the information from the * parameter list generated by the parser and then has * OperatorCreate() do all the actual work. * * 'parameters' is a list of DefElem */ void DefineOperator(List *names, List *parameters, Oid newOid) { char *oprName; Oid oprNamespace; AclResult aclresult; bool canHash = false; /* operator hashes */ bool canMerge = false; /* operator merges */ List *functionName = NIL; /* function for operator */ TypeName *typeName1 = NULL; /* first type name */ TypeName *typeName2 = NULL; /* second type name */ Oid typeId1 = InvalidOid; /* types converted to OID */ Oid typeId2 = InvalidOid; List *commutatorName = NIL; /* optional commutator operator name */ List *negatorName = NIL; /* optional negator operator name */ List *restrictionName = NIL; /* optional restrict. sel. procedure */ List *joinName = NIL; /* optional join sel. procedure */ List *leftSortName = NIL; /* optional left sort operator */ List *rightSortName = NIL; /* optional right sort operator */ List *ltCompareName = NIL; /* optional < compare operator */ List *gtCompareName = NIL; /* optional > compare operator */ ListCell *pl; Oid opOid; /* Convert list of names to a name and namespace */ oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName); /* Check we have creation rights in target namespace */ aclresult = pg_namespace_aclcheck(oprNamespace, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(oprNamespace)); /* * loop over the definition list and extract the information we need. */ foreach(pl, parameters) { DefElem *defel = (DefElem *) lfirst(pl); if (pg_strcasecmp(defel->defname, "leftarg") == 0) { typeName1 = defGetTypeName(defel); if (typeName1->setof) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("setof type not allowed for operator argument"))); } else if (pg_strcasecmp(defel->defname, "rightarg") == 0) { typeName2 = defGetTypeName(defel); if (typeName2->setof) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("setof type not allowed for operator argument"))); } else if (pg_strcasecmp(defel->defname, "procedure") == 0) functionName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "commutator") == 0) commutatorName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "negator") == 0) negatorName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "restrict") == 0) restrictionName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "join") == 0) joinName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "hashes") == 0) canHash = defGetBoolean(defel); else if (pg_strcasecmp(defel->defname, "merges") == 0) canMerge = defGetBoolean(defel); else if (pg_strcasecmp(defel->defname, "sort1") == 0) leftSortName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "sort2") == 0) rightSortName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "ltcmp") == 0) ltCompareName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "gtcmp") == 0) gtCompareName = defGetQualifiedName(defel); else ereport(WARNING, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("operator attribute \"%s\" not recognized", defel->defname))); }
/* * CompactCheckpointerRequestQueue * Remove duplicates from the request queue to avoid backend fsyncs. * Returns "true" if any entries were removed. * * Although a full fsync request queue is not common, it can lead to severe * performance problems when it does happen. So far, this situation has * only been observed to occur when the system is under heavy write load, * and especially during the "sync" phase of a checkpoint. Without this * logic, each backend begins doing an fsync for every block written, which * gets very expensive and can slow down the whole system. * * Trying to do this every time the queue is full could lose if there * aren't any removable entries. But that should be vanishingly rare in * practice: there's one queue entry per shared buffer. */ static bool CompactCheckpointerRequestQueue(void) { struct CheckpointerSlotMapping { CheckpointerRequest request; int slot; }; int n, preserve_count; int num_skipped = 0; HASHCTL ctl; HTAB *htab; bool *skip_slot; /* must hold CheckpointerCommLock in exclusive mode */ Assert(LWLockHeldByMe(CheckpointerCommLock)); /* Initialize skip_slot array */ skip_slot = palloc0(sizeof(bool) * CheckpointerShmem->num_requests); /* Initialize temporary hash table */ MemSet(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(CheckpointerRequest); ctl.entrysize = sizeof(struct CheckpointerSlotMapping); ctl.hcxt = CurrentMemoryContext; htab = hash_create("CompactCheckpointerRequestQueue", CheckpointerShmem->num_requests, &ctl, HASH_ELEM | HASH_BLOBS | HASH_CONTEXT); /* * The basic idea here is that a request can be skipped if it's followed * by a later, identical request. It might seem more sensible to work * backwards from the end of the queue and check whether a request is * *preceded* by an earlier, identical request, in the hopes of doing less * copying. But that might change the semantics, if there's an * intervening FORGET_RELATION_FSYNC or FORGET_DATABASE_FSYNC request, so * we do it this way. It would be possible to be even smarter if we made * the code below understand the specific semantics of such requests (it * could blow away preceding entries that would end up being canceled * anyhow), but it's not clear that the extra complexity would buy us * anything. */ for (n = 0; n < CheckpointerShmem->num_requests; n++) { CheckpointerRequest *request; struct CheckpointerSlotMapping *slotmap; bool found; /* * We use the request struct directly as a hashtable key. This * assumes that any padding bytes in the structs are consistently the * same, which should be okay because we zeroed them in * CheckpointerShmemInit. Note also that RelFileNode had better * contain no pad bytes. */ request = &CheckpointerShmem->requests[n]; slotmap = hash_search(htab, request, HASH_ENTER, &found); if (found) { /* Duplicate, so mark the previous occurrence as skippable */ skip_slot[slotmap->slot] = true; num_skipped++; } /* Remember slot containing latest occurrence of this request value */ slotmap->slot = n; } /* Done with the hash table. */ hash_destroy(htab); /* If no duplicates, we're out of luck. */ if (!num_skipped) { pfree(skip_slot); return false; } /* We found some duplicates; remove them. */ preserve_count = 0; for (n = 0; n < CheckpointerShmem->num_requests; n++) { if (skip_slot[n]) continue; CheckpointerShmem->requests[preserve_count++] = CheckpointerShmem->requests[n]; } ereport(DEBUG1, (errmsg("compacted fsync request queue from %d entries to %d entries", CheckpointerShmem->num_requests, preserve_count))); CheckpointerShmem->num_requests = preserve_count; /* Cleanup. */ pfree(skip_slot); return true; }
Datum pg_file_rename(PG_FUNCTION_ARGS) { char *fn1, *fn2, *fn3; int rc; requireSuperuser(); if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) PG_RETURN_NULL(); fn1 = convert_and_check_filename(PG_GETARG_TEXT_P(0), false); fn2 = convert_and_check_filename(PG_GETARG_TEXT_P(1), false); if (PG_ARGISNULL(2)) fn3 = 0; else fn3 = convert_and_check_filename(PG_GETARG_TEXT_P(2), false); if (access(fn1, W_OK) < 0) { ereport(WARNING, (errcode_for_file_access(), errmsg("file \"%s\" is not accessible: %m", fn1))); PG_RETURN_BOOL(false); } if (fn3 && access(fn2, W_OK) < 0) { ereport(WARNING, (errcode_for_file_access(), errmsg("file \"%s\" is not accessible: %m", fn2))); PG_RETURN_BOOL(false); } rc = access(fn3 ? fn3 : fn2, 2); if (rc >= 0 || errno != ENOENT) { ereport(ERROR, (ERRCODE_DUPLICATE_FILE, errmsg("cannot rename to target file \"%s\"", fn3 ? fn3 : fn2))); } if (fn3) { if (rename(fn2, fn3) != 0) { ereport(ERROR, (errcode_for_file_access(), errmsg("could not rename \"%s\" to \"%s\": %m", fn2, fn3))); } if (rename(fn1, fn2) != 0) { ereport(WARNING, (errcode_for_file_access(), errmsg("could not rename \"%s\" to \"%s\": %m", fn1, fn2))); if (rename(fn3, fn2) != 0) { ereport(ERROR, (errcode_for_file_access(), errmsg("could not rename \"%s\" back to \"%s\": %m", fn3, fn2))); } else { ereport(ERROR, (ERRCODE_UNDEFINED_FILE, errmsg("renaming \"%s\" to \"%s\" was reverted", fn2, fn3))); } } } else if (rename(fn1, fn2) != 0) { ereport(ERROR, (errcode_for_file_access(), errmsg("could not rename \"%s\" to \"%s\": %m", fn1, fn2))); } PG_RETURN_BOOL(true); }
/* * Main entry point for checkpointer process * * This is invoked from AuxiliaryProcessMain, which has already created the * basic execution environment, but not enabled signals yet. */ void CheckpointerMain(void) { sigjmp_buf local_sigjmp_buf; MemoryContext checkpointer_context; CheckpointerShmem->checkpointer_pid = MyProcPid; /* * Properly accept or ignore signals the postmaster might send us * * Note: we deliberately ignore SIGTERM, because during a standard Unix * system shutdown cycle, init will SIGTERM all processes at once. We * want to wait for the backends to exit, whereupon the postmaster will * tell us it's okay to shut down (via SIGUSR2). */ pqsignal(SIGHUP, ChkptSigHupHandler); /* set flag to read config file */ pqsignal(SIGINT, ReqCheckpointHandler); /* request checkpoint */ pqsignal(SIGTERM, SIG_IGN); /* ignore SIGTERM */ pqsignal(SIGQUIT, chkpt_quickdie); /* hard crash time */ pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); pqsignal(SIGUSR1, chkpt_sigusr1_handler); pqsignal(SIGUSR2, ReqShutdownHandler); /* request shutdown */ /* * Reset some signals that are accepted by postmaster but not here */ pqsignal(SIGCHLD, SIG_DFL); pqsignal(SIGTTIN, SIG_DFL); pqsignal(SIGTTOU, SIG_DFL); pqsignal(SIGCONT, SIG_DFL); pqsignal(SIGWINCH, SIG_DFL); /* We allow SIGQUIT (quickdie) at all times */ sigdelset(&BlockSig, SIGQUIT); /* * Initialize so that first time-driven event happens at the correct time. */ last_checkpoint_time = last_xlog_switch_time = (pg_time_t) time(NULL); /* * Create a resource owner to keep track of our resources (currently only * buffer pins). */ CurrentResourceOwner = ResourceOwnerCreate(NULL, "Checkpointer"); /* * Create a memory context that we will do all our work in. We do this so * that we can reset the context during error recovery and thereby avoid * possible memory leaks. Formerly this code just ran in * TopMemoryContext, but resetting that would be a really bad idea. */ checkpointer_context = AllocSetContextCreate(TopMemoryContext, "Checkpointer", ALLOCSET_DEFAULT_SIZES); MemoryContextSwitchTo(checkpointer_context); /* * If an exception is encountered, processing resumes here. * * See notes in postgres.c about the design of this coding. */ if (sigsetjmp(local_sigjmp_buf, 1) != 0) { /* Since not using PG_TRY, must reset error stack by hand */ error_context_stack = NULL; /* Prevent interrupts while cleaning up */ HOLD_INTERRUPTS(); /* Report the error to the server log */ EmitErrorReport(); /* * These operations are really just a minimal subset of * AbortTransaction(). We don't have very many resources to worry * about in checkpointer, but we do have LWLocks, buffers, and temp * files. */ LWLockReleaseAll(); ConditionVariableCancelSleep(); pgstat_report_wait_end(); AbortBufferIO(); UnlockBuffers(); /* buffer pins are released here: */ ResourceOwnerRelease(CurrentResourceOwner, RESOURCE_RELEASE_BEFORE_LOCKS, false, true); /* we needn't bother with the other ResourceOwnerRelease phases */ AtEOXact_Buffers(false); AtEOXact_SMgr(); AtEOXact_Files(); AtEOXact_HashTables(false); /* Warn any waiting backends that the checkpoint failed. */ if (ckpt_active) { SpinLockAcquire(&CheckpointerShmem->ckpt_lck); CheckpointerShmem->ckpt_failed++; CheckpointerShmem->ckpt_done = CheckpointerShmem->ckpt_started; SpinLockRelease(&CheckpointerShmem->ckpt_lck); ckpt_active = false; } /* * Now return to normal top-level context and clear ErrorContext for * next time. */ MemoryContextSwitchTo(checkpointer_context); FlushErrorState(); /* Flush any leaked data in the top-level context */ MemoryContextResetAndDeleteChildren(checkpointer_context); /* Now we can allow interrupts again */ RESUME_INTERRUPTS(); /* * Sleep at least 1 second after any error. A write error is likely * to be repeated, and we don't want to be filling the error logs as * fast as we can. */ pg_usleep(1000000L); /* * Close all open files after any error. This is helpful on Windows, * where holding deleted files open causes various strange errors. * It's not clear we need it elsewhere, but shouldn't hurt. */ smgrcloseall(); } /* We can now handle ereport(ERROR) */ PG_exception_stack = &local_sigjmp_buf; /* * Unblock signals (they were blocked when the postmaster forked us) */ PG_SETMASK(&UnBlockSig); /* * Ensure all shared memory values are set correctly for the config. Doing * this here ensures no race conditions from other concurrent updaters. */ UpdateSharedMemoryConfig(); /* * Advertise our latch that backends can use to wake us up while we're * sleeping. */ ProcGlobal->checkpointerLatch = &MyProc->procLatch; /* * Loop forever */ for (;;) { bool do_checkpoint = false; int flags = 0; pg_time_t now; int elapsed_secs; int cur_timeout; int rc; /* Clear any already-pending wakeups */ ResetLatch(MyLatch); /* * Process any requests or signals received recently. */ AbsorbFsyncRequests(); if (got_SIGHUP) { got_SIGHUP = false; ProcessConfigFile(PGC_SIGHUP); /* * Checkpointer is the last process to shut down, so we ask it to * hold the keys for a range of other tasks required most of which * have nothing to do with checkpointing at all. * * For various reasons, some config values can change dynamically * so the primary copy of them is held in shared memory to make * sure all backends see the same value. We make Checkpointer * responsible for updating the shared memory copy if the * parameter setting changes because of SIGHUP. */ UpdateSharedMemoryConfig(); } if (checkpoint_requested) { checkpoint_requested = false; do_checkpoint = true; BgWriterStats.m_requested_checkpoints++; } if (shutdown_requested) { /* * From here on, elog(ERROR) should end with exit(1), not send * control back to the sigsetjmp block above */ ExitOnAnyError = true; /* Close down the database */ ShutdownXLOG(0, 0); /* Normal exit from the checkpointer is here */ proc_exit(0); /* done */ } /* * Force a checkpoint if too much time has elapsed since the last one. * Note that we count a timed checkpoint in stats only when this * occurs without an external request, but we set the CAUSE_TIME flag * bit even if there is also an external request. */ now = (pg_time_t) time(NULL); elapsed_secs = now - last_checkpoint_time; if (elapsed_secs >= CheckPointTimeout) { if (!do_checkpoint) BgWriterStats.m_timed_checkpoints++; do_checkpoint = true; flags |= CHECKPOINT_CAUSE_TIME; } /* * Do a checkpoint if requested. */ if (do_checkpoint) { bool ckpt_performed = false; bool do_restartpoint; /* * Check if we should perform a checkpoint or a restartpoint. As a * side-effect, RecoveryInProgress() initializes TimeLineID if * it's not set yet. */ do_restartpoint = RecoveryInProgress(); /* * Atomically fetch the request flags to figure out what kind of a * checkpoint we should perform, and increase the started-counter * to acknowledge that we've started a new checkpoint. */ SpinLockAcquire(&CheckpointerShmem->ckpt_lck); flags |= CheckpointerShmem->ckpt_flags; CheckpointerShmem->ckpt_flags = 0; CheckpointerShmem->ckpt_started++; SpinLockRelease(&CheckpointerShmem->ckpt_lck); /* * The end-of-recovery checkpoint is a real checkpoint that's * performed while we're still in recovery. */ if (flags & CHECKPOINT_END_OF_RECOVERY) do_restartpoint = false; /* * We will warn if (a) too soon since last checkpoint (whatever * caused it) and (b) somebody set the CHECKPOINT_CAUSE_XLOG flag * since the last checkpoint start. Note in particular that this * implementation will not generate warnings caused by * CheckPointTimeout < CheckPointWarning. */ if (!do_restartpoint && (flags & CHECKPOINT_CAUSE_XLOG) && elapsed_secs < CheckPointWarning) ereport(LOG, (errmsg_plural("checkpoints are occurring too frequently (%d second apart)", "checkpoints are occurring too frequently (%d seconds apart)", elapsed_secs, elapsed_secs), errhint("Consider increasing the configuration parameter \"max_wal_size\"."))); /* * Initialize checkpointer-private variables used during * checkpoint. */ ckpt_active = true; if (do_restartpoint) ckpt_start_recptr = GetXLogReplayRecPtr(NULL); else ckpt_start_recptr = GetInsertRecPtr(); ckpt_start_time = now; ckpt_cached_elapsed = 0; /* * Do the checkpoint. */ if (!do_restartpoint) { CreateCheckPoint(flags); ckpt_performed = true; } else ckpt_performed = CreateRestartPoint(flags); /* * After any checkpoint, close all smgr files. This is so we * won't hang onto smgr references to deleted files indefinitely. */ smgrcloseall(); /* * Indicate checkpoint completion to any waiting backends. */ SpinLockAcquire(&CheckpointerShmem->ckpt_lck); CheckpointerShmem->ckpt_done = CheckpointerShmem->ckpt_started; SpinLockRelease(&CheckpointerShmem->ckpt_lck); if (ckpt_performed) { /* * Note we record the checkpoint start time not end time as * last_checkpoint_time. This is so that time-driven * checkpoints happen at a predictable spacing. */ last_checkpoint_time = now; } else { /* * We were not able to perform the restartpoint (checkpoints * throw an ERROR in case of error). Most likely because we * have not received any new checkpoint WAL records since the * last restartpoint. Try again in 15 s. */ last_checkpoint_time = now - CheckPointTimeout + 15; } ckpt_active = false; } /* Check for archive_timeout and switch xlog files if necessary. */ CheckArchiveTimeout(); /* * Send off activity statistics to the stats collector. (The reason * why we re-use bgwriter-related code for this is that the bgwriter * and checkpointer used to be just one process. It's probably not * worth the trouble to split the stats support into two independent * stats message types.) */ pgstat_send_bgwriter(); /* * Sleep until we are signaled or it's time for another checkpoint or * xlog file switch. */ now = (pg_time_t) time(NULL); elapsed_secs = now - last_checkpoint_time; if (elapsed_secs >= CheckPointTimeout) continue; /* no sleep for us ... */ cur_timeout = CheckPointTimeout - elapsed_secs; if (XLogArchiveTimeout > 0 && !RecoveryInProgress()) { elapsed_secs = now - last_xlog_switch_time; if (elapsed_secs >= XLogArchiveTimeout) continue; /* no sleep for us ... */ cur_timeout = Min(cur_timeout, XLogArchiveTimeout - elapsed_secs); } rc = WaitLatch(MyLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, cur_timeout * 1000L /* convert to ms */ , WAIT_EVENT_CHECKPOINTER_MAIN); /* * Emergency bailout if postmaster has died. This is to avoid the * necessity for manual cleanup of all postmaster children. */ if (rc & WL_POSTMASTER_DEATH) exit(1); } }
static int compute_trsp( char* edges_sql, int dovertex, int start_id, double start_pos, int end_id, double end_pos, bool directed, bool has_reverse_cost, char *restrict_sql, path_element_t **path, size_t *path_count) { pgr_SPI_connect(); PGR_DBG("Load edges"); pgr_edge_t *edges = NULL; size_t total_tuples = 0; pgr_get_data_5_columns(edges_sql, &edges, &total_tuples); PGR_DBG("Total %ld edges", total_tuples); PGR_DBG("Load restrictions"); restrict_t *restricts = NULL; size_t total_restrict_tuples = 0; if (restrict_sql == NULL) { PGR_DBG("Sql for restrictions is null."); } else { pgr_get_restriction_data(restrict_sql, &restricts, &total_restrict_tuples); } #ifdef DEBUG int t1; for (t1=0; t1<total_restrict_tuples; t1++) { PGR_DBG("restricts: %.2f, %ld, %ld, %ld, %ld, %ld, %ld", restricts[t1].to_cost, restricts[t1].target_id, restricts[t1].via[0], restricts[t1].via[1], restricts[t1].via[2], restricts[t1].via[3], restricts[t1].via[4]); } #endif PGR_DBG("Total %ld restriction", total_restrict_tuples); int v_max_id=0; int v_min_id=INT_MAX; /* track if start and end are both in edge tuples */ int s_count = 0; int t_count = 0; char *err_msg; int ret = -1; register int z; PGR_DBG("start turn_restrict_shortest_path\n"); if (start_id == end_id) { PGR_DBG("Starting vertex and Ending Vertex are equal"); *path = NULL; return 0; } //defining min and max vertex id PGR_DBG("Total %i edge tuples", total_tuples); for(z=0; z<total_tuples; z++) { if(edges[z].source<v_min_id) v_min_id=(int)edges[z].source; if(edges[z].source>v_max_id) v_max_id=(int)edges[z].source; if(edges[z].target<v_min_id) v_min_id=(int)edges[z].target; if(edges[z].target>v_max_id) v_max_id=(int)edges[z].target; //PGR_DBG("%i <-> %i", v_min_id, v_max_id); } //:::::::::::::::::::::::::::::::::::: //:: reducing vertex id (renumbering) //:::::::::::::::::::::::::::::::::::: for(z=0; z<total_tuples; z++) { //check if edges[] contains source and target if (dovertex) { if(edges[z].source == start_id || edges[z].target == start_id) ++s_count; if(edges[z].source == end_id || edges[z].target == end_id) ++t_count; } else { if(edges[z].id == start_id) ++s_count; if(edges[z].id == end_id) ++t_count; } edges[z].source-=v_min_id; edges[z].target-=v_min_id; edges[z].cost = edges[z].cost; //PGR_DBG("edgeID: %i SRc:%i - %i, cost: %f", edges[z].id,edges[z].source, edges[z].target,edges[z].cost); } PGR_DBG("Min vertex id: %i , Max vid: %i",v_min_id,v_max_id); PGR_DBG("Total %ld edge tuples", total_tuples); if(s_count == 0) { elog(ERROR, "Start id was not found."); return -1; } if(t_count == 0) { elog(ERROR, "Target id was not found."); return -1; } if (dovertex) { start_id -= v_min_id; end_id -= v_min_id; } if (dovertex) { PGR_DBG("Calling trsp_node_wrapper\n"); ret = trsp_node_wrapper(edges, total_tuples, restricts, total_restrict_tuples, start_id, end_id, directed, has_reverse_cost, path, path_count, &err_msg); } else { PGR_DBG("Calling trsp_edge_wrapper\n"); ret = trsp_edge_wrapper(edges, total_tuples, restricts, total_restrict_tuples, start_id, start_pos, end_id, end_pos, directed, has_reverse_cost, path, path_count, &err_msg); } PGR_DBG("Message received from inside:"); PGR_DBG("%s",err_msg); //PGR_DBG("SIZE %i\n",*path_count); //:::::::::::::::::::::::::::::::: //:: restoring original vertex id //:::::::::::::::::::::::::::::::: for(z=0;z<*path_count;z++) { //PGR_DBG("vetex %i\n",(*path)[z].vertex_id); if (z || (*path)[z].vertex_id != -1) (*path)[z].vertex_id+=v_min_id; } PGR_DBG("ret = %i\n", ret); PGR_DBG("*path_count = %i\n", *path_count); if (ret < 0) { //elog(ERROR, "Error computing path: %s", err_msg); ereport(ERROR, (errcode(ERRCODE_E_R_E_CONTAINING_SQL_NOT_PERMITTED), errmsg("Error computing path: %s", err_msg))); } pgr_SPI_finish(); return 0; }
/* * RequestCheckpoint * Called in backend processes to request a checkpoint * * flags is a bitwise OR of the following: * CHECKPOINT_IS_SHUTDOWN: checkpoint is for database shutdown. * CHECKPOINT_END_OF_RECOVERY: checkpoint is for end of WAL recovery. * CHECKPOINT_IMMEDIATE: finish the checkpoint ASAP, * ignoring checkpoint_completion_target parameter. * CHECKPOINT_FORCE: force a checkpoint even if no XLOG activity has occurred * since the last one (implied by CHECKPOINT_IS_SHUTDOWN or * CHECKPOINT_END_OF_RECOVERY). * CHECKPOINT_WAIT: wait for completion before returning (otherwise, * just signal checkpointer to do it, and return). * CHECKPOINT_CAUSE_XLOG: checkpoint is requested due to xlog filling. * (This affects logging, and in particular enables CheckPointWarning.) */ void RequestCheckpoint(int flags) { int ntries; int old_failed, old_started; /* * If in a standalone backend, just do it ourselves. */ if (!IsPostmasterEnvironment) { /* * There's no point in doing slow checkpoints in a standalone backend, * because there's no other backends the checkpoint could disrupt. */ CreateCheckPoint(flags | CHECKPOINT_IMMEDIATE); /* * After any checkpoint, close all smgr files. This is so we won't * hang onto smgr references to deleted files indefinitely. */ smgrcloseall(); return; } /* * Atomically set the request flags, and take a snapshot of the counters. * When we see ckpt_started > old_started, we know the flags we set here * have been seen by checkpointer. * * Note that we OR the flags with any existing flags, to avoid overriding * a "stronger" request by another backend. The flag senses must be * chosen to make this work! */ SpinLockAcquire(&CheckpointerShmem->ckpt_lck); old_failed = CheckpointerShmem->ckpt_failed; old_started = CheckpointerShmem->ckpt_started; CheckpointerShmem->ckpt_flags |= flags; SpinLockRelease(&CheckpointerShmem->ckpt_lck); /* * Send signal to request checkpoint. It's possible that the checkpointer * hasn't started yet, or is in process of restarting, so we will retry a * few times if needed. Also, if not told to wait for the checkpoint to * occur, we consider failure to send the signal to be nonfatal and merely * LOG it. */ for (ntries = 0;; ntries++) { if (CheckpointerShmem->checkpointer_pid == 0) { if (ntries >= 20) /* max wait 2.0 sec */ { elog((flags & CHECKPOINT_WAIT) ? ERROR : LOG, "could not request checkpoint because checkpointer not running"); break; } } else if (kill(CheckpointerShmem->checkpointer_pid, SIGINT) != 0) { if (ntries >= 20) /* max wait 2.0 sec */ { elog((flags & CHECKPOINT_WAIT) ? ERROR : LOG, "could not signal for checkpoint: %m"); break; } } else break; /* signal sent successfully */ CHECK_FOR_INTERRUPTS(); pg_usleep(100000L); /* wait 0.1 sec, then retry */ } /* * If requested, wait for completion. We detect completion according to * the algorithm given above. */ if (flags & CHECKPOINT_WAIT) { int new_started, new_failed; /* Wait for a new checkpoint to start. */ for (;;) { SpinLockAcquire(&CheckpointerShmem->ckpt_lck); new_started = CheckpointerShmem->ckpt_started; SpinLockRelease(&CheckpointerShmem->ckpt_lck); if (new_started != old_started) break; CHECK_FOR_INTERRUPTS(); pg_usleep(100000L); } /* * We are waiting for ckpt_done >= new_started, in a modulo sense. */ for (;;) { int new_done; SpinLockAcquire(&CheckpointerShmem->ckpt_lck); new_done = CheckpointerShmem->ckpt_done; new_failed = CheckpointerShmem->ckpt_failed; SpinLockRelease(&CheckpointerShmem->ckpt_lck); if (new_done - new_started >= 0) break; CHECK_FOR_INTERRUPTS(); pg_usleep(100000L); } if (new_failed != old_failed) ereport(ERROR, (errmsg("checkpoint request failed"), errhint("Consult recent messages in the server log for details."))); } }
Datum ltree_in(PG_FUNCTION_ARGS) { char *buf = (char *) PG_GETARG_POINTER(0); char *ptr; nodeitem *list, *lptr; int num = 0, totallen = 0; int state = LTPRS_WAITNAME; ltree *result; ltree_level *curlevel; int charlen; int pos = 0; ptr = buf; while (*ptr) { charlen = pg_mblen(ptr); if (charlen == 1 && t_iseq(ptr, '.')) num++; ptr += charlen; } if (num + 1 > MaxAllocSize / sizeof(nodeitem)) ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("number of levels (%d) exceeds the maximum allowed (%d)", num + 1, (int) (MaxAllocSize / sizeof(nodeitem))))); list = lptr = (nodeitem *) palloc(sizeof(nodeitem) * (num + 1)); ptr = buf; while (*ptr) { charlen = pg_mblen(ptr); if (state == LTPRS_WAITNAME) { if (ISALNUM(ptr)) { lptr->start = ptr; lptr->wlen = 0; state = LTPRS_WAITDELIM; } else UNCHAR; } else if (state == LTPRS_WAITDELIM) { if (charlen == 1 && t_iseq(ptr, '.')) { lptr->len = ptr - lptr->start; if (lptr->wlen > 255) ereport(ERROR, (errcode(ERRCODE_NAME_TOO_LONG), errmsg("name of level is too long"), errdetail("Name length is %d, must " "be < 256, in position %d.", lptr->wlen, pos))); totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE); lptr++; state = LTPRS_WAITNAME; } else if (!ISALNUM(ptr)) UNCHAR; } else /* internal error */ elog(ERROR, "internal error in parser"); ptr += charlen; lptr->wlen++; pos++; } if (state == LTPRS_WAITDELIM) { lptr->len = ptr - lptr->start; if (lptr->wlen > 255) ereport(ERROR, (errcode(ERRCODE_NAME_TOO_LONG), errmsg("name of level is too long"), errdetail("Name length is %d, must " "be < 256, in position %d.", lptr->wlen, pos))); totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE); lptr++; } else if (!(state == LTPRS_WAITNAME && lptr == list)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("syntax error"), errdetail("Unexpected end of line."))); result = (ltree *) palloc0(LTREE_HDRSIZE + totallen); SET_VARSIZE(result, LTREE_HDRSIZE + totallen); result->numlevel = lptr - list; curlevel = LTREE_FIRST(result); lptr = list; while (lptr - list < result->numlevel) { curlevel->len = (uint16) lptr->len; memcpy(curlevel->name, lptr->start, lptr->len); curlevel = LEVEL_NEXT(curlevel); lptr++; } pfree(list); PG_RETURN_POINTER(result); }
int pgwin32_waitforsinglesocket(SOCKET s, int what, int timeout) { static HANDLE waitevent = INVALID_HANDLE_VALUE; static SOCKET current_socket = -1; static int isUDP = 0; HANDLE events[2]; int r; if (waitevent == INVALID_HANDLE_VALUE) { waitevent = CreateEvent(NULL, TRUE, FALSE, NULL); if (waitevent == INVALID_HANDLE_VALUE) ereport(ERROR, (errmsg_internal("Failed to create socket waiting event: %i", (int) GetLastError()))); } else if (!ResetEvent(waitevent)) ereport(ERROR, (errmsg_internal("Failed to reset socket waiting event: %i", (int) GetLastError()))); /* * make sure we don't multiplex this kernel event object with a different * socket from a previous call */ if (current_socket != s) { if ( current_socket != -1 ) WSAEventSelect(current_socket, waitevent, 0); isUDP = isDataGram(s); } current_socket = s; if (WSAEventSelect(s, waitevent, what) == SOCKET_ERROR) { TranslateSocketError(); return 0; } events[0] = pgwin32_signal_event; events[1] = waitevent; /* * Just a workaround of unknown locking problem with writing in UDP socket * under high load: Client's pgsql backend sleeps infinitely in * WaitForMultipleObjectsEx, pgstat process sleeps in pgwin32_select(). * So, we will wait with small timeout(0.1 sec) and if sockect is still * blocked, try WSASend (see comments in pgwin32_select) and wait again. */ if ((what & FD_WRITE) && isUDP) { for(;;) { r = WaitForMultipleObjectsEx(2, events, FALSE, 100, TRUE); if ( r == WAIT_TIMEOUT ) { char c; WSABUF buf; DWORD sent; buf.buf = &c; buf.len = 0; r = WSASend(s, &buf, 1, &sent, 0, NULL, NULL); if (r == 0) /* Completed - means things are fine! */ return 1; else if ( WSAGetLastError() != WSAEWOULDBLOCK ) { TranslateSocketError(); return 0; } } else break; } } else r = WaitForMultipleObjectsEx(2, events, FALSE, timeout, TRUE); if (r == WAIT_OBJECT_0 || r == WAIT_IO_COMPLETION) { pgwin32_dispatch_queued_signals(); errno = EINTR; return 0; } if (r == WAIT_OBJECT_0 + 1) return 1; if (r == WAIT_TIMEOUT) return 0; ereport(ERROR, (errmsg_internal("Bad return from WaitForMultipleObjects: %i (%i)", r, (int) GetLastError()))); return 0; }
/* * copydir: copy a directory * * If recurse is false, subdirectories are ignored. Anything that's not * a directory or a regular file is ignored. */ void copydir(char *fromdir, char *todir, bool recurse) { DIR *xldir; struct dirent *xlde; char fromfile[MAXPGPATH * 2]; char tofile[MAXPGPATH * 2]; if (MakePGDirectory(todir) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not create directory \"%s\": %m", todir))); xldir = AllocateDir(fromdir); while ((xlde = ReadDir(xldir, fromdir)) != NULL) { struct stat fst; /* If we got a cancel signal during the copy of the directory, quit */ CHECK_FOR_INTERRUPTS(); if (strcmp(xlde->d_name, ".") == 0 || strcmp(xlde->d_name, "..") == 0) continue; snprintf(fromfile, sizeof(fromfile), "%s/%s", fromdir, xlde->d_name); snprintf(tofile, sizeof(tofile), "%s/%s", todir, xlde->d_name); if (lstat(fromfile, &fst) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not stat file \"%s\": %m", fromfile))); if (S_ISDIR(fst.st_mode)) { /* recurse to handle subdirectories */ if (recurse) copydir(fromfile, tofile, true); } else if (S_ISREG(fst.st_mode)) copy_file(fromfile, tofile); } FreeDir(xldir); /* * Be paranoid here and fsync all files to ensure the copy is really done. * But if fsync is disabled, we're done. */ if (!enableFsync) return; xldir = AllocateDir(todir); while ((xlde = ReadDir(xldir, todir)) != NULL) { struct stat fst; if (strcmp(xlde->d_name, ".") == 0 || strcmp(xlde->d_name, "..") == 0) continue; snprintf(tofile, sizeof(tofile), "%s/%s", todir, xlde->d_name); /* * We don't need to sync subdirectories here since the recursive * copydir will do it before it returns */ if (lstat(tofile, &fst) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not stat file \"%s\": %m", tofile))); if (S_ISREG(fst.st_mode)) fsync_fname(tofile, false); } FreeDir(xldir); /* * It's important to fsync the destination directory itself as individual * file fsyncs don't guarantee that the directory entry for the file is * synced. Recent versions of ext4 have made the window much wider but * it's been true for ext3 and other filesystems in the past. */ fsync_fname(todir, true); }
int pgwin32_recv(SOCKET s, char *buf, int len, int f) { WSABUF wbuf; int r; DWORD b; DWORD flags = f; int n; if (pgwin32_poll_signals()) return -1; wbuf.len = len; wbuf.buf = buf; r = WSARecv(s, &wbuf, 1, &b, &flags, NULL, NULL); if (r != SOCKET_ERROR && b > 0) /* Read succeeded right away */ return b; if (r == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) { TranslateSocketError(); return -1; } /* No error, zero bytes (win2000+) or error+WSAEWOULDBLOCK (<=nt4) */ for (n = 0; n < 5; n++) { if (pgwin32_waitforsinglesocket(s, FD_READ | FD_CLOSE | FD_ACCEPT, INFINITE) == 0) return -1; /* errno already set */ r = WSARecv(s, &wbuf, 1, &b, &flags, NULL, NULL); if (r == SOCKET_ERROR) { if (WSAGetLastError() == WSAEWOULDBLOCK) { /* * There seem to be cases on win2k (at least) where WSARecv * can return WSAEWOULDBLOCK even when * pgwin32_waitforsinglesocket claims the socket is readable. * In this case, just sleep for a moment and try again. We try * up to 5 times - if it fails more than that it's not likely * to ever come back. */ pg_usleep(10000); continue; } TranslateSocketError(); return -1; } return b; } ereport(NOTICE, (errmsg_internal("Failed to read from ready socket (after retries)"))); errno = EWOULDBLOCK; return -1; }
Datum #else // _MSC_VER PGDLLEXPORT Datum #endif xyd_tsp(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; uint32_t call_cntr; uint32_t max_calls; TupleDesc tuple_desc; /**************************************************************************/ /* MODIFY AS NEEDED */ /* */ General_path_element_t *result_tuples = 0; size_t result_count = 0; /* */ /**************************************************************************/ if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); /**********************************************************************/ /* MODIFY AS NEEDED */ // CREATE OR REPLACE FUNCTION pgr_dijkstra( // sql text, // start_vid BIGINT, // end_vid BIGINT, // directed BOOLEAN default true, PGR_DBG("Calling process"); process( pgr_text2char(PG_GETARG_TEXT_P(0)), PG_GETARG_INT64(1), PG_GETARG_INT64(2), &result_tuples, &result_count); /* */ /**********************************************************************/ funcctx->max_calls = (uint32_t) result_count; funcctx->user_fctx = result_tuples; if (get_call_result_type(fcinfo, NULL, &tuple_desc) != TYPEFUNC_COMPOSITE) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); } funcctx->tuple_desc = tuple_desc; MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); call_cntr = funcctx->call_cntr; max_calls = funcctx->max_calls; tuple_desc = funcctx->tuple_desc; result_tuples = (General_path_element_t*) funcctx->user_fctx; if (call_cntr < max_calls) { HeapTuple tuple; Datum result; Datum *values; bool* nulls; /**********************************************************************/ /* MODIFY AS NEEDED */ // OUT seq INTEGER, // OUT node BIGINT, // OUT cost FLOAT, // OUT agg_cost FLOAT values = palloc(4 * sizeof(Datum)); nulls = palloc(4 * sizeof(bool)); size_t i; for (i = 0; i < 4; ++i) { nulls[i] = false; } // postgres starts counting from 1 values[0] = Int32GetDatum(call_cntr + 1); values[1] = Int64GetDatum(result_tuples[call_cntr].node); values[2] = Float8GetDatum(result_tuples[call_cntr].cost); values[3] = Float8GetDatum(result_tuples[call_cntr].agg_cost); /**********************************************************************/ tuple = heap_form_tuple(tuple_desc, values, nulls); result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } else { // cleanup if (result_tuples) free(result_tuples); SRF_RETURN_DONE(funcctx); } }
/* * Convert the last socket error code into errno */ static void TranslateSocketError(void) { switch (WSAGetLastError()) { case WSANOTINITIALISED: case WSAENETDOWN: case WSAEINPROGRESS: case WSAEINVAL: case WSAESOCKTNOSUPPORT: case WSAEFAULT: case WSAEINVALIDPROVIDER: case WSAEINVALIDPROCTABLE: case WSAEMSGSIZE: errno = EINVAL; break; case WSAEAFNOSUPPORT: errno = EAFNOSUPPORT; break; case WSAEMFILE: errno = EMFILE; break; case WSAENOBUFS: errno = ENOBUFS; break; case WSAEPROTONOSUPPORT: case WSAEPROTOTYPE: errno = EPROTONOSUPPORT; break; case WSAECONNREFUSED: errno = ECONNREFUSED; break; case WSAEINTR: errno = EINTR; break; case WSAENOTSOCK: errno = EBADFD; break; case WSAEOPNOTSUPP: errno = EOPNOTSUPP; break; case WSAEWOULDBLOCK: errno = EWOULDBLOCK; break; case WSAEACCES: errno = EACCES; break; case WSAENOTCONN: case WSAENETRESET: case WSAECONNRESET: case WSAESHUTDOWN: case WSAECONNABORTED: case WSAEDISCON: errno = ECONNREFUSED; /* ENOTCONN? */ break; default: ereport(NOTICE, (errmsg_internal("Unknown win32 socket error code: %i", WSAGetLastError()))); errno = EINVAL; } }
bool ora_lock_shmem(size_t size, int max_pipes, int max_events, int max_locks, bool reset) { int i; bool found; sh_memory *sh_mem; if (pipes == NULL) { sh_mem = ShmemInitStruct("dbms_pipe", size, &found); if (sh_mem == NULL) ereport(FATAL, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"), errdetail("Failed while allocation block %lu bytes in shared memory.", (unsigned long) size))); if (!found) { #if PG_VERSION_NUM >= 90600 sh_mem->tranche_id = LWLockNewTrancheId(); LWLockInitialize(&sh_mem->shmem_lock, sh_mem->tranche_id); { #if PG_VERSION_NUM >= 100000 LWLockRegisterTranche(sh_mem->tranche_id, "orafce"); #else static LWLockTranche tranche; tranche.name = "orafce"; tranche.array_base = &sh_mem->shmem_lock; tranche.array_stride = sizeof(LWLock); LWLockRegisterTranche(sh_mem->tranche_id, &tranche); #endif shmem_lockid = &sh_mem->shmem_lock; } #else shmem_lockid = sh_mem->shmem_lockid = LWLockAssign(); #endif LWLockAcquire(shmem_lockid, LW_EXCLUSIVE); sh_mem->size = size - sh_memory_size; ora_sinit(sh_mem->data, size, true); pipes = sh_mem->pipes = ora_salloc(max_pipes*sizeof(pipe)); sid = sh_mem->sid = 1; for (i = 0; i < max_pipes; i++) pipes[i].is_valid = false; events = sh_mem->events = ora_salloc(max_events*sizeof(alert_event)); locks = sh_mem->locks = ora_salloc(max_locks*sizeof(alert_lock)); for (i = 0; i < max_events; i++) { events[i].event_name = NULL; events[i].max_receivers = 0; events[i].receivers = NULL; events[i].messages = NULL; } for (i = 0; i < max_locks; i++) { locks[i].sid = -1; locks[i].echo = NULL; } } else if (pipes == NULL) { #if PG_VERSION_NUM >= 90600 #if PG_VERSION_NUM >= 100000 LWLockRegisterTranche(sh_mem->tranche_id, "orafce"); #else static LWLockTranche tranche; tranche.name = "orafce"; tranche.array_base = &sh_mem->shmem_lock; tranche.array_stride = sizeof(LWLock); LWLockRegisterTranche(sh_mem->tranche_id, &tranche); #endif shmem_lockid = &sh_mem->shmem_lock; #else shmem_lockid = sh_mem->shmem_lockid; #endif pipes = sh_mem->pipes; LWLockAcquire(shmem_lockid, LW_EXCLUSIVE); ora_sinit(sh_mem->data, sh_mem->size, reset); sid = ++(sh_mem->sid); events = sh_mem->events; locks = sh_mem->locks; } } else { LWLockAcquire(shmem_lockid, LW_EXCLUSIVE); } return pipes != NULL; }
/* * pgfdw_subxact_callback --- cleanup at subtransaction end. */ static void pgfdw_subxact_callback(SubXactEvent event, SubTransactionId mySubid, SubTransactionId parentSubid, void *arg) { HASH_SEQ_STATUS scan; ConnCacheEntry *entry; int curlevel; /* Nothing to do at subxact start, nor after commit. */ if (!(event == SUBXACT_EVENT_PRE_COMMIT_SUB || event == SUBXACT_EVENT_ABORT_SUB)) return; /* Quick exit if no connections were touched in this transaction. */ if (!xact_got_connection) return; /* * Scan all connection cache entries to find open remote subtransactions * of the current level, and close them. */ curlevel = GetCurrentTransactionNestLevel(); hash_seq_init(&scan, ConnectionHash); while ((entry = (ConnCacheEntry *) hash_seq_search(&scan))) { PGresult *res; char sql[100]; /* * We only care about connections with open remote subtransactions of * the current level. */ if (entry->conn == NULL || entry->xact_depth < curlevel) continue; if (entry->xact_depth > curlevel) elog(ERROR, "missed cleaning up remote subtransaction at level %d", entry->xact_depth); if (event == SUBXACT_EVENT_PRE_COMMIT_SUB) { /* Commit all remote subtransactions during pre-commit */ snprintf(sql, sizeof(sql), "RELEASE SAVEPOINT s%d", curlevel); do_sql_command(entry->conn, sql); } else { /* Assume we might have lost track of prepared statements */ entry->have_error = true; /* * If a command has been submitted to the remote server by using * an asynchronous execution function, the command might not have * yet completed. Check to see if a command is still being * processed by the remote server, and if so, request cancellation * of the command. */ if (PQtransactionStatus(entry->conn) == PQTRANS_ACTIVE) { PGcancel *cancel; char errbuf[256]; if ((cancel = PQgetCancel(entry->conn))) { if (!PQcancel(cancel, errbuf, sizeof(errbuf))) ereport(WARNING, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg("could not send cancel request: %s", errbuf))); PQfreeCancel(cancel); } } /* Rollback all remote subtransactions during abort */ snprintf(sql, sizeof(sql), "ROLLBACK TO SAVEPOINT s%d; RELEASE SAVEPOINT s%d", curlevel, curlevel); res = PQexec(entry->conn, sql); if (PQresultStatus(res) != PGRES_COMMAND_OK) pgfdw_report_error(WARNING, res, entry->conn, true, sql); else PQclear(res); } /* OK, we're outta that level of subtransaction */ entry->xact_depth--; } }