From 2a2e35c15e004c8a28e203ada51164cc5985b048 Mon Sep 17 00:00:00 2001 From: BLM Date: Thu, 16 Jul 2015 14:56:23 -0500 Subject: [PATCH] Started working on alternate parsing function --- pg_libphonenumber.cpp | 123 +++++++++++++++++++++++---------- pg_libphonenumber.sql.template | 5 ++ 2 files changed, 92 insertions(+), 36 deletions(-) diff --git a/pg_libphonenumber.cpp b/pg_libphonenumber.cpp index 2d3f2f2..b4adc58 100755 --- a/pg_libphonenumber.cpp +++ b/pg_libphonenumber.cpp @@ -13,6 +13,33 @@ using namespace i18n::phonenumbers; PhoneNumberUtil *phoneUtil = PhoneNumberUtil::GetInstance(); +//Raised when a phone number can't be parsed +class ParseException : std::exception { + public: + ParseException(PhoneNumberUtil::ErrorType et) : error_type(et) {} + + const char* what() const throw() override { + using PNU = i18n::phonenumbers::PhoneNumberUtil; + switch(error_type) { + case PNU::NO_PARSING_ERROR: + //This case shouldn't occur in correctly-functioning code. + assert(0); + return "Parsed successfully"; + case PNU::NOT_A_NUMBER: + return "String does not appear to contain a phone number."; + case PNU::INVALID_COUNTRY_CODE_ERROR: + return "Invalid country code"; + //TODO: handle more error cases specifically. + default: + //We have some generic parsing error. + return "Unrecognized number format"; + } + }; + + private: + PhoneNumberUtil::ErrorType error_type; +}; + //Utility functions for error handling void reportOutOfMemory() { @@ -20,11 +47,11 @@ void reportOutOfMemory() { (errcode(ERRCODE_OUT_OF_MEMORY))); } -void reportParsingError(const char* phone_number, const char* msg = "") { +void reportParseError(const char* phone_number, ParseException& exception) { ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("unable to parse '%s' as a phone number", phone_number), - errdetail("%s", msg))); + errdetail("%s", exception.what()))); } void reportGenericError(std::exception& exception) { @@ -40,8 +67,36 @@ void logInfo(const char* msg) { errmsg("%s", msg))); } +/* + * Utility functions + */ + +//Internal function used by phone_number_in and parse_phone_number +PhoneNumber* parsePhoneNumber(const char* number_str, const char* country) throw () { + PhoneNumber *number; + + number = (PhoneNumber*)palloc0(sizeof(PhoneNumber)); + //TODO: can this be removed? (palloc normally handles this, right?) + if(number == nullptr) { + throw std::bad_alloc(); + } + new(number) PhoneNumber(); + + PhoneNumberUtil::ErrorType error; + error = phoneUtil->Parse(number_str, country, number); + if(error != PhoneNumberUtil::NO_PARSING_ERROR) { + throw ParseException(error); + } + //TODO: check number validity. + return number; +} + //TODO: handle non-exception thrown types? (shouldn't happen, but you never know...) -//TODO: check null args? (PG_ARGISNULL) Make non-strict? +//TODO: check null args? (PG_ARGISNULL). Make non-strict? + +/* + * Extension functions + */ extern "C" { #ifdef PG_MODULE_MAGIC @@ -54,45 +109,40 @@ extern "C" { Datum phone_number_in(PG_FUNCTION_ARGS) { + const char *number_str = PG_GETARG_CSTRING(0); + try { - const char *str = PG_GETARG_CSTRING(0); - PhoneNumber *number; - - number = (PhoneNumber*)palloc0(sizeof(PhoneNumber)); - //TODO: can this be removed? (palloc normally handles this, right?) - if(number == nullptr) { - reportOutOfMemory(); - PG_RETURN_NULL(); - } - new(number) PhoneNumber(); - - using PNU = i18n::phonenumbers::PhoneNumberUtil; - PNU::ErrorType error; - error = phoneUtil->Parse(str, "US", number); - switch(error) { - case PNU::NO_PARSING_ERROR: - //The number was parsed correctly; return it. - PG_RETURN_POINTER(number); - break; - case PNU::NOT_A_NUMBER: - reportParsingError(str, "String does not appear to contain a phone number."); - break; - case PNU::INVALID_COUNTRY_CODE_ERROR: - reportParsingError(str, "Invalid country code"); - break; - //TODO: handle more error cases specifically. - default: - //We have some generic parsing error. - reportParsingError(str); - } - //TODO: check number validity. + PG_RETURN_POINTER(parsePhoneNumber(number_str, "US")); } catch(std::bad_alloc& e) { reportOutOfMemory(); - } catch(std::exception& e) { + } catch(ParseException e) { + reportParseError(number_str, e); + } catch (std::exception& e) { + reportGenericError(e); + } + + PG_RETURN_NULL(); + } + + PGDLLEXPORT PG_FUNCTION_INFO_V1(parse_phone_number); + + PGDLLEXPORT + Datum + parse_phone_number(PG_FUNCTION_ARGS) + { + const char *number_str = PG_GETARG_CSTRING(0); + const char *country = PG_GETARG_CSTRING(1); + + try { + PG_RETURN_POINTER(parsePhoneNumber(number_str, country)); + } catch(std::bad_alloc& e) { + reportOutOfMemory(); + } catch(ParseException e) { + reportParseError(number_str, e); + } catch (std::exception& e) { reportGenericError(e); } - //If we get here, we couldn't return a valid number. PG_RETURN_NULL(); } @@ -126,6 +176,7 @@ extern "C" { } catch (std::exception& e) { reportGenericError(e); } + PG_RETURN_NULL(); } diff --git a/pg_libphonenumber.sql.template b/pg_libphonenumber.sql.template index 7289af7..65f68ae 100644 --- a/pg_libphonenumber.sql.template +++ b/pg_libphonenumber.sql.template @@ -7,6 +7,10 @@ CREATE FUNCTION phone_number_in(cstring) RETURNS phone_number LANGUAGE c IMMUTABLE STRICT AS 'pg_libphonenumber', 'phone_number_in'; +CREATE FUNCTION parse_phone_number(cstring, cstring) RETURNS phone_number + LANGUAGE c IMMUTABLE STRICT + AS 'pg_libphonenumber', 'parse_phone_number'; + CREATE FUNCTION phone_number_out(phone_number) RETURNS cstring LANGUAGE c IMMUTABLE STRICT AS 'pg_libphonenumber', 'phone_number_out'; @@ -30,3 +34,4 @@ CREATE TYPE phone_number ( STORAGE = plain ); +-- vim:syntax=sql