@@ -10,6 +10,7 @@ import (
1010
1111 "github.com/jackc/pgx/v5"
1212 "github.com/jackc/pgx/v5/pgconn"
13+ "github.com/jackc/pgx/v5/pgtype"
1314 "github.com/jackc/pgx/v5/pgxtest"
1415 "github.com/stretchr/testify/require"
1516)
@@ -452,6 +453,106 @@ func TestConnCopyFromJSON(t *testing.T) {
452453 ensureConnValid (t , conn )
453454}
454455
456+ func TestConnCopyFromTSVector (t * testing.T ) {
457+ t .Parallel ()
458+
459+ ctx , cancel := context .WithTimeout (context .Background (), 120 * time .Second )
460+ defer cancel ()
461+
462+ conn := mustConnectString (t , os .Getenv ("PGX_TEST_DATABASE" ))
463+ defer closeConn (t , conn )
464+
465+ pgxtest .SkipCockroachDB (t , conn , "CockroachDB handles tsvector escaping differently" )
466+
467+ tx , err := conn .Begin (ctx )
468+ require .NoError (t , err )
469+ defer tx .Rollback (ctx )
470+
471+ _ , err = tx .Exec (ctx , `create temporary table tmp_tsv (id int, t tsvector)` )
472+ require .NoError (t , err )
473+
474+ inputRows := [][]any {
475+ // Text format: core functionality.
476+ {1 , `'a':1A 'cat':5 'fat':2B,4C` }, // Multiple lexemes with positions and weights.
477+ {2 , `'bare'` }, // Single lexeme with no positions.
478+ {3 , `'multi':1,2,3,4,5` }, // Multiple positions (default weight D).
479+ {4 , `'test':1A,2B,3C,4D` }, // All four weights on one lexeme.
480+ {5 , `'word':1D` }, // Explicit weight D (normalizes to no suffix).
481+ {6 , `'high':16383A` }, // High position number (near 14-bit max).
482+
483+ // Text format: escaping.
484+ {7 , `'don''t'` }, // Quote escaping (doubled single quote).
485+ {8 , `'don\'t'` }, // Quote escaping (backslash).
486+ {9 , `'ab\\c'` }, // Backslash in lexeme.
487+ {10 , `'\ foo'` }, // Escaped space.
488+
489+ // Text format: special characters.
490+ {11 , `'café' 'naïve'` }, // Unicode lexemes.
491+ {12 , `'a:b' 'c,d'` }, // Delimiter-like characters (colon, comma).
492+
493+ // Struct format: tests binary encoding path.
494+ {13 , pgtype.TSVector {
495+ Lexemes : []pgtype.TSVectorLexeme {
496+ {Word : "alpha" , Positions : []pgtype.TSVectorPosition {{Position : 1 , Weight : pgtype .TSVectorWeightA }}},
497+ {Word : "beta" , Positions : []pgtype.TSVectorPosition {{Position : 2 , Weight : pgtype .TSVectorWeightB }}},
498+ {Word : "gamma" , Positions : nil },
499+ },
500+ Valid : true ,
501+ }},
502+ {14 , pgtype.TSVector {Valid : true }}, // Empty valid tsvector (no lexemes).
503+
504+ // NULL handling.
505+ {15 , pgtype.TSVector {Valid : false }}, // Invalid (NULL) TSVector struct.
506+ {16 , nil }, // Nil value.
507+ }
508+
509+ copyCount , err := conn .CopyFrom (ctx , pgx.Identifier {"tmp_tsv" }, []string {"id" , "t" }, pgx .CopyFromRows (inputRows ))
510+ require .NoError (t , err )
511+ require .EqualValues (t , len (inputRows ), copyCount )
512+
513+ rows , err := conn .Query (ctx , "select id, t::text from tmp_tsv order by id nulls last" )
514+ require .NoError (t , err )
515+
516+ var outputRows [][]any
517+ for rows .Next () {
518+ row , err := rows .Values ()
519+ require .NoError (t , err )
520+ outputRows = append (outputRows , row )
521+ }
522+ require .NoError (t , rows .Err ())
523+
524+ expectedOutputRows := [][]any {
525+ // Text format: core functionality.
526+ {int32 (1 ), `'a':1A 'cat':5 'fat':2B,4C` },
527+ {int32 (2 ), `'bare'` },
528+ {int32 (3 ), `'multi':1,2,3,4,5` },
529+ {int32 (4 ), `'test':1A,2B,3C,4` },
530+ {int32 (5 ), `'word':1` },
531+ {int32 (6 ), `'high':16383A` },
532+
533+ // Text format: escaping.
534+ {int32 (7 ), `'don''t'` },
535+ {int32 (8 ), `'don''t'` },
536+ {int32 (9 ), `'ab\\c'` },
537+ {int32 (10 ), `' foo'` },
538+
539+ // Text format: special characters.
540+ {int32 (11 ), `'café' 'naïve'` },
541+ {int32 (12 ), `'a:b' 'c,d'` },
542+
543+ // Struct format.
544+ {int32 (13 ), `'alpha':1A 'beta':2B 'gamma'` },
545+ {int32 (14 ), `` },
546+
547+ // NULL handling.
548+ {int32 (15 ), nil },
549+ {int32 (16 ), nil },
550+ }
551+ require .Equal (t , expectedOutputRows , outputRows )
552+
553+ ensureConnValid (t , conn )
554+ }
555+
455556type clientFailSource struct {
456557 count int
457558 err error
0 commit comments