diff --git a/src/common.cpp b/src/common.cpp index a7f9816..5439098 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -228,3 +228,63 @@ void datum_to_jsonb(Datum value, Oid typoid, bool isnull, FmgrInfo *outfunc, pushJsonbValue(&parseState, iskey ? WJB_KEY : WJB_VALUE, &jb); } +/* + * string_to_int + * Convert string to integer. + * + * This is modified copy of pg_atoi() function which was removed from Postgres 15. + */ +int32 +string_to_int32(const char *s) +{ + long l; + char *badp; + + /* + * Some versions of strtol treat the empty string as an error, but some + * seem not to. Make an explicit test to be sure we catch it. + */ + if (s == NULL) + elog(ERROR, "NULL pointer"); + if (*s == 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type %s: \"%s\"", + "integer", s))); + + errno = 0; + l = strtol(s, &badp, 10); + + /* We made no progress parsing the string, so bail out */ + if (s == badp) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type %s: \"%s\"", + "integer", s))); + + if (errno == ERANGE +#if defined(HAVE_LONG_INT_64) + /* won't get ERANGE on these with 64-bit longs... */ + || l < INT_MIN || l > INT_MAX +#endif + ) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value \"%s\" is out of range for type %s", s, + "integer"))); + + /* + * Skip any trailing whitespace; if anything but whitespace remains before + * the terminating character, bail out + */ + while (*badp && *badp != '\0' && isspace((unsigned char) *badp)) + badp++; + + if (*badp && *badp != '\0') + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type %s: \"%s\"", + "integer", s))); + + return (int32) l; +} diff --git a/src/common.hpp b/src/common.hpp index 23c695d..3cfd8e8 100644 --- a/src/common.hpp +++ b/src/common.hpp @@ -63,5 +63,6 @@ char *tolowercase(const char *input, char *output); arrow::Type::type get_arrow_list_elem_type(arrow::DataType *type); void datum_to_jsonb(Datum value, Oid typoid, bool isnull, FmgrInfo *outfunc, JsonbParseState *result, bool iskey); +int32 string_to_int32(const char *s); #endif diff --git a/src/parquet_impl.cpp b/src/parquet_impl.cpp index 02a792c..4e2a607 100644 --- a/src/parquet_impl.cpp +++ b/src/parquet_impl.cpp @@ -368,7 +368,7 @@ row_group_matches_filter(parquet::Statistics *stats, */ /* - * Extract the key type (we don't check correctness here as we've + * Extract the key type (we don't check correctness here as we've * already done this in `extract_rowgroups_list()`) */ auto strct = arrow_type->fields()[0]; @@ -1035,7 +1035,7 @@ get_table_options(Oid relid, ParquetFdwPlanState *fdw_private) else if (strcmp(def->defname, "max_open_files") == 0) { /* check that int value is valid */ - fdw_private->max_open_files = pg_atoi(defGetString(def), sizeof(int32), '\0'); + fdw_private->max_open_files = string_to_int32(defGetString(def)); } else if (strcmp(def->defname, "files_in_order") == 0) { @@ -1087,7 +1087,7 @@ parquetGetForeignRelSize(PlannerInfo *root, fdw_private->filenames = NIL; foreach (lc, filenames_orig) { - char *filename = strVal((Value *) lfirst(lc)); + char *filename = strVal(lfirst(lc)); List *rowgroups = extract_rowgroups_list(filename, tupleDesc, filters, &matched_rows, &total_rows); @@ -1233,7 +1233,7 @@ parquetGetForeignPaths(PlannerInfo *root, * for the foreign table Postgres will assume that the returned data is * already sorted. In the function parquetBeginForeignScan() we make sure * that the data from parquet files are sorted. - * + * * We need to make sure that we don't add PathKey for an attribute which is * not passed by ORDER BY. We will stop building pathkeys as soon as we see * that an attribute on ORDER BY and "sorted" doesn't match, since in that @@ -1515,16 +1515,16 @@ parquetBeginForeignScan(ForeignScanState *node, int /* eflags */) attrs_sorted = (List *) lfirst(lc); break; case 3: - use_mmap = (bool) intVal((Value *) lfirst(lc)); + use_mmap = (bool) intVal(lfirst(lc)); break; case 4: - use_threads = (bool) intVal((Value *) lfirst(lc)); + use_threads = (bool) intVal(lfirst(lc)); break; case 5: - reader_type = (ReaderType) intVal((Value *) lfirst(lc)); + reader_type = (ReaderType) intVal(lfirst(lc)); break; case 6: - max_open_files = intVal((Value *) lfirst(lc)); + max_open_files = intVal(lfirst(lc)); break; case 7: rowgroups_list = (List *) lfirst(lc); @@ -1587,7 +1587,7 @@ parquetBeginForeignScan(ForeignScanState *node, int /* eflags */) forboth (lc, filenames, lc2, rowgroups_list) { - char *filename = strVal((Value *) lfirst(lc)); + char *filename = strVal(lfirst(lc)); List *rowgroups = (List *) lfirst(lc2); festate->add_file(filename, rowgroups); @@ -1703,7 +1703,7 @@ parquetAcquireSampleRowsFunc(Relation relation, int /* elevel */, foreach (lc, fdw_private.filenames) { - char *filename = strVal((Value *) lfirst(lc)); + char *filename = strVal(lfirst(lc)); try { @@ -1822,7 +1822,7 @@ parquetExplainForeignScan(ForeignScanState *node, ExplainState *es) fdw_private = ((ForeignScan *) node->ss.ps.plan)->fdw_private; filenames = (List *) linitial(fdw_private); - reader_type = (ReaderType) intVal((Value *) list_nth(fdw_private, 5)); + reader_type = (ReaderType) intVal(list_nth(fdw_private, 5)); rowgroups_list = (List *) llast(fdw_private); switch (reader_type) @@ -1846,7 +1846,7 @@ parquetExplainForeignScan(ForeignScanState *node, ExplainState *es) forboth(lc, filenames, lc2, rowgroups_list) { - char *filename = strVal((Value *) lfirst(lc)); + char *filename = strVal(lfirst(lc)); List *rowgroups = (List *) lfirst(lc2); bool is_first = true; @@ -2063,7 +2063,7 @@ parquet_fdw_validator_impl(PG_FUNCTION_ARGS) foreach(lc, filenames) { struct stat stat_buf; - char *fn = strVal((Value *) lfirst(lc)); + char *fn = strVal(lfirst(lc)); if (stat(fn, &stat_buf) != 0) { @@ -2081,7 +2081,7 @@ parquet_fdw_validator_impl(PG_FUNCTION_ARGS) else if (strcmp(def->defname, "files_func") == 0) { Oid jsonboid = JSONBOID; - List *funcname = stringToQualifiedNameList(defGetString(def)); + List *funcname = stringToQualifiedNameList(defGetString(def)); Oid funcoid; Oid rettype; @@ -2099,7 +2099,7 @@ parquet_fdw_validator_impl(PG_FUNCTION_ARGS) } else if (strcmp(def->defname, "files_func_arg") == 0) { - /* + /* * Try to convert the string value into JSONB to validate it is * properly formatted. */ @@ -2120,7 +2120,7 @@ parquet_fdw_validator_impl(PG_FUNCTION_ARGS) else if (strcmp(def->defname, "max_open_files") == 0) { /* check that int value is valid */ - pg_atoi(defGetString(def), sizeof(int32), '\0'); + string_to_int32(defGetString(def)); } else if (strcmp(def->defname, "files_in_order") == 0) {