@@ -1638,8 +1638,9 @@ static int cloudsync_decode_value_cb (void *xdata, int index, int type, int64_t
16381638 return DBRES_OK ;
16391639}
16401640
1641- // Decode encoded bytea into a pgvalue_t matching the target type
1642- static pgvalue_t * cloudsync_decode_bytea_to_pgvalue (bytea * encoded , Oid target_typoid , const char * target_typname , bool * out_isnull ) {
1641+ // Decode encoded bytea into a pgvalue_t with the decoded base type.
1642+ // Type casting to the target column type is handled by the SQL statement.
1643+ static pgvalue_t * cloudsync_decode_bytea_to_pgvalue (bytea * encoded , bool * out_isnull ) {
16431644 // Decode input guardrails.
16441645 if (out_isnull ) * out_isnull = true;
16451646 if (!encoded ) return NULL ;
@@ -1652,37 +1653,34 @@ static pgvalue_t *cloudsync_decode_bytea_to_pgvalue (bytea *encoded, Oid target_
16521653 if (out_isnull ) * out_isnull = dv .isnull ;
16531654 if (dv .isnull ) return NULL ;
16541655
1655- // Map decoded C types into a PostgreSQL Datum.
1656- Oid argt [1 ] = {TEXTOID };
1657- Datum argv [1 ];
1658- char argn [1 ] = {' ' };
1659- bool argv_is_pointer = false; // Track if argv[0] needs pfree on error
1656+ // Map decoded C types into a PostgreSQL Datum with the base type.
1657+ // The SQL statement handles casting to the target column type via $n::typename.
1658+ Oid typoid = TEXTOID ;
1659+ Datum datum ;
16601660
16611661 switch (dv .dbtype ) {
16621662 case DBTYPE_INTEGER :
1663- argt [ 0 ] = INT8OID ;
1664- argv [ 0 ] = Int64GetDatum (dv .ival );
1663+ typoid = INT8OID ;
1664+ datum = Int64GetDatum (dv .ival );
16651665 break ;
16661666 case DBTYPE_FLOAT :
1667- argt [ 0 ] = FLOAT8OID ;
1668- argv [ 0 ] = Float8GetDatum (dv .dval );
1667+ typoid = FLOAT8OID ;
1668+ datum = Float8GetDatum (dv .dval );
16691669 break ;
16701670 case DBTYPE_TEXT : {
1671- argt [ 0 ] = TEXTOID ;
1671+ typoid = TEXTOID ;
16721672 Size tlen = dv .pval ? (Size )dv .len : 0 ;
16731673 text * t = (text * )palloc (VARHDRSZ + tlen );
16741674 SET_VARSIZE (t , VARHDRSZ + tlen );
16751675 if (tlen > 0 ) memmove (VARDATA (t ), dv .pval , tlen );
1676- argv [0 ] = PointerGetDatum (t );
1677- argv_is_pointer = true;
1676+ datum = PointerGetDatum (t );
16781677 } break ;
16791678 case DBTYPE_BLOB : {
1680- argt [ 0 ] = BYTEAOID ;
1679+ typoid = BYTEAOID ;
16811680 bytea * ba = (bytea * )palloc (VARHDRSZ + dv .len );
16821681 SET_VARSIZE (ba , VARHDRSZ + dv .len );
16831682 if (dv .len > 0 ) memcpy (VARDATA (ba ), dv .pval , (size_t )dv .len );
1684- argv [0 ] = PointerGetDatum (ba );
1685- argv_is_pointer = true;
1683+ datum = PointerGetDatum (ba );
16861684 } break ;
16871685 case DBTYPE_NULL :
16881686 if (out_isnull ) * out_isnull = true;
@@ -1695,44 +1693,7 @@ static pgvalue_t *cloudsync_decode_bytea_to_pgvalue (bytea *encoded, Oid target_
16951693
16961694 if (dv .pval ) pfree (dv .pval );
16971695
1698- // Cast to the target column type from the table schema.
1699- if (argt [0 ] == target_typoid ) {
1700- pgvalue_t * result = pgvalue_create (argv [0 ], target_typoid , -1 , InvalidOid , false);
1701- if (!result && argv_is_pointer ) {
1702- pfree (DatumGetPointer (argv [0 ]));
1703- }
1704- return result ;
1705- }
1706-
1707- StringInfoData castq ;
1708- initStringInfo (& castq );
1709- appendStringInfo (& castq , "SELECT $1::%s" , target_typname );
1710-
1711- int rc = SPI_execute_with_args (castq .data , 1 , argt , argv , argn , true, 1 );
1712- if (rc != SPI_OK_SELECT || SPI_processed != 1 || !SPI_tuptable ) {
1713- if (SPI_tuptable ) SPI_freetuptable (SPI_tuptable );
1714- pfree (castq .data );
1715- if (argv_is_pointer ) pfree (DatumGetPointer (argv [0 ]));
1716- ereport (ERROR , (errmsg ("cloudsync: failed to cast value to %s" , target_typname )));
1717- }
1718- pfree (castq .data );
1719-
1720- bool typed_isnull = false;
1721- // SPI_getbinval uses 1-based column indexing, but TupleDescAttr uses 0-based indexing
1722- Datum typed_value = SPI_getbinval (SPI_tuptable -> vals [0 ], SPI_tuptable -> tupdesc , 1 , & typed_isnull );
1723- int32 typmod = TupleDescAttr (SPI_tuptable -> tupdesc , 0 )-> atttypmod ;
1724- Oid collation = TupleDescAttr (SPI_tuptable -> tupdesc , 0 )-> attcollation ;
1725- if (!typed_isnull ) {
1726- Form_pg_attribute att = TupleDescAttr (SPI_tuptable -> tupdesc , 0 );
1727- typed_value = datumCopy (typed_value , att -> attbyval , att -> attlen );
1728- }
1729- if (SPI_tuptable ) {
1730- SPI_freetuptable (SPI_tuptable );
1731- SPI_tuptable = NULL ;
1732- }
1733-
1734- if (out_isnull ) * out_isnull = typed_isnull ;
1735- return pgvalue_create (typed_value , target_typoid , typmod , collation , typed_isnull );
1696+ return pgvalue_create (datum , typoid , -1 , InvalidOid , false);
17361697}
17371698
17381699PG_FUNCTION_INFO_V1 (cloudsync_encode_value );
@@ -2092,30 +2053,6 @@ static char * build_union_sql (void) {
20922053 return result ;
20932054}
20942055
2095- static Oid lookup_column_type_oid (const char * tbl , const char * col_name , const char * schema ) {
2096- // SPI_connect not needed here
2097- if (strcmp (col_name , CLOUDSYNC_TOMBSTONE_VALUE ) == 0 ) return BYTEAOID ;
2098-
2099- // lookup table OID with optional schema qualification
2100- Oid relid ;
2101- if (schema ) {
2102- Oid nspid = get_namespace_oid (schema , false);
2103- relid = get_relname_relid (tbl , nspid );
2104- } else {
2105- relid = RelnameGetRelid (tbl );
2106- }
2107- if (!OidIsValid (relid )) ereport (ERROR , (errmsg ("cloudsync: table \"%s\" not found (schema: %s)" , tbl , schema ? schema : "search_path" )));
2108-
2109- // find attribute
2110- int attnum = get_attnum (relid , col_name );
2111- if (attnum == InvalidAttrNumber ) ereport (ERROR , (errmsg ("cloudsync: column \"%s\" not found in table \"%s\"" , col_name , tbl )));
2112-
2113- Oid typoid = get_atttype (relid , attnum );
2114- if (!OidIsValid (typoid )) ereport (ERROR , (errmsg ("cloudsync: could not resolve type for %s.%s" , tbl , col_name )));
2115-
2116- return typoid ;
2117- }
2118-
21192056PG_FUNCTION_INFO_V1 (cloudsync_changes_select );
21202057Datum cloudsync_changes_select (PG_FUNCTION_ARGS ) {
21212058 FuncCallContext * funcctx ;
@@ -2307,19 +2244,12 @@ Datum cloudsync_changes_insert_trigger (PG_FUNCTION_ARGS) {
23072244 cloudsync_table_context * table = table_lookup (data , insert_tbl );
23082245 if (!table ) ereport (ERROR , (errmsg ("Unable to find table" )));
23092246
2310- // get real column type from tbl.col_name (skip tombstone sentinel)
2311- Oid target_typoid = InvalidOid ;
2312- char * target_typname = NULL ;
2313- if (!is_tombstone ) {
2314- target_typoid = lookup_column_type_oid (insert_tbl , insert_name , cloudsync_schema (data ));
2315- target_typname = format_type_be (target_typoid );
2316- }
2317-
23182247 if (SPI_connect () != SPI_OK_CONNECT ) ereport (ERROR , (errmsg ("cloudsync: SPI_connect failed in trigger" )));
23192248 spi_connected = true;
23202249
2250+ // Decode value to base type; SQL statement handles type casting via $n::typename
23212251 if (!is_tombstone ) {
2322- col_value = cloudsync_decode_bytea_to_pgvalue (insert_value_encoded , target_typoid , target_typname , NULL );
2252+ col_value = cloudsync_decode_bytea_to_pgvalue (insert_value_encoded , NULL );
23232253 }
23242254
23252255 int rc = DBRES_OK ;
0 commit comments