@@ -91,6 +91,49 @@ mod tests {
9191 test_table. assert_expected_and_result_rows ( ) ;
9292 }
9393
94+ #[ pg_test]
95+ fn test_s3_uri_with_special_chars ( ) {
96+ object_store_cache_clear ( ) ;
97+
98+ let test_bucket_name: String =
99+ std:: env:: var ( "AWS_S3_TEST_BUCKET" ) . expect ( "AWS_S3_TEST_BUCKET not found" ) ;
100+
101+ let s3_uri = format ! ( "s3://{test_bucket_name}/so\\ mek*ey/**testme.parquet" ) ;
102+
103+ let copy_to_command = format ! (
104+ "COPY (SELECT a FROM generate_series(1,10) a) TO '{s3_uri}' WITH (format parquet);"
105+ ) ;
106+ Spi :: run ( & copy_to_command) . unwrap ( ) ;
107+
108+ let create_table = "create table test_table(a int);" ;
109+ Spi :: run ( create_table) . unwrap ( ) ;
110+
111+ let copy_from_command = format ! ( "COPY test_table FROM '{s3_uri}' WITH (format parquet);" ) ;
112+ Spi :: run ( copy_from_command. as_str ( ) ) . unwrap ( ) ;
113+
114+ let count_query = "select count(*) from test_table;" ;
115+ let result = Spi :: get_one :: < i64 > ( count_query) . unwrap ( ) . unwrap ( ) ;
116+ assert_eq ! ( result, 10 ) ;
117+ }
118+
119+ #[ pg_test]
120+ #[ should_panic( expected = "no files found that match the pattern" ) ]
121+ fn test_s3_with_nonexistent_pattern_uri ( ) {
122+ object_store_cache_clear ( ) ;
123+
124+ let test_bucket_name: String =
125+ std:: env:: var ( "AWS_S3_TEST_BUCKET" ) . expect ( "AWS_S3_TEST_BUCKET not found" ) ;
126+
127+ let s3_uri_pattern = format ! ( "s3://{test_bucket_name}/notexists/**" ) ;
128+
129+ let create_table = "create table test_table(a int);" ;
130+ Spi :: run ( create_table) . unwrap ( ) ;
131+
132+ let copy_from_command =
133+ format ! ( "COPY test_table FROM '{s3_uri_pattern}' WITH (format parquet);" ) ;
134+ Spi :: run ( copy_from_command. as_str ( ) ) . unwrap ( ) ;
135+ }
136+
94137 #[ pg_test]
95138 fn test_s3_from_config_file ( ) {
96139 object_store_cache_clear ( ) ;
@@ -370,22 +413,6 @@ mod tests {
370413 . unwrap ( ) ;
371414 }
372415
373- #[ pg_test]
374- #[ should_panic( expected = "no files found that match the pattern" ) ]
375- fn test_s3_empty_pattern ( ) {
376- object_store_cache_clear ( ) ;
377-
378- let test_bucket_name: String =
379- std:: env:: var ( "AWS_S3_TEST_BUCKET" ) . expect ( "AWS_S3_TEST_BUCKET not found" ) ;
380-
381- let create_table = "create table test_table(id int);" ;
382- Spi :: run ( create_table) . unwrap ( ) ;
383-
384- let s3_uri_pattern = format ! ( "s3://{test_bucket_name}/dummy*.parquet" ) ;
385- let copy_from_command = format ! ( "COPY test_table FROM '{}';" , s3_uri_pattern) ;
386- Spi :: run ( copy_from_command. as_str ( ) ) . unwrap ( ) ;
387- }
388-
389416 #[ pg_test]
390417 #[ should_panic( expected = "column count mismatch" ) ]
391418 fn test_s3_pattern_schema_mismatch ( ) {
@@ -491,6 +518,52 @@ mod tests {
491518 test_table. assert_expected_and_result_rows ( ) ;
492519 }
493520
521+ #[ pg_test]
522+ #[ cfg( not( rhel8) ) ]
523+ fn test_azure_uri_with_special_chars ( ) {
524+ object_store_cache_clear ( ) ;
525+
526+ let test_container_name: String = std:: env:: var ( "AZURE_TEST_CONTAINER_NAME" )
527+ . expect ( "AZURE_TEST_CONTAINER_NAME not found" ) ;
528+
529+ let azure_uri = format ! ( "az://{test_container_name}/so\\ mek*ey/**testme.parquet" ) ;
530+
531+ let copy_to_command = format ! (
532+ "COPY (SELECT a FROM generate_series(1,10) a) TO '{azure_uri}' WITH (format parquet);"
533+ ) ;
534+ Spi :: run ( & copy_to_command) . unwrap ( ) ;
535+
536+ let create_table = "create table test_table(a int);" ;
537+ Spi :: run ( create_table) . unwrap ( ) ;
538+
539+ let copy_from_command =
540+ format ! ( "COPY test_table FROM '{azure_uri}' WITH (format parquet);" ) ;
541+ Spi :: run ( copy_from_command. as_str ( ) ) . unwrap ( ) ;
542+
543+ let count_query = "select count(*) from test_table;" ;
544+ let result = Spi :: get_one :: < i64 > ( count_query) . unwrap ( ) . unwrap ( ) ;
545+ assert_eq ! ( result, 10 ) ;
546+ }
547+
548+ #[ pg_test]
549+ #[ cfg( not( rhel8) ) ]
550+ #[ should_panic( expected = "no files found that match the pattern" ) ]
551+ fn test_azure_with_nonexistent_pattern_uri ( ) {
552+ object_store_cache_clear ( ) ;
553+
554+ let test_container_name: String = std:: env:: var ( "AZURE_TEST_CONTAINER_NAME" )
555+ . expect ( "AZURE_TEST_CONTAINER_NAME not found" ) ;
556+
557+ let azure_uri_pattern = format ! ( "az://{test_container_name}/notexists/**" ) ;
558+
559+ let create_table = "create table test_table(a int);" ;
560+ Spi :: run ( create_table) . unwrap ( ) ;
561+
562+ let copy_from_command =
563+ format ! ( "COPY test_table FROM '{azure_uri_pattern}' WITH (format parquet);" ) ;
564+ Spi :: run ( copy_from_command. as_str ( ) ) . unwrap ( ) ;
565+ }
566+
494567 #[ pg_test]
495568 #[ cfg( not( rhel8) ) ]
496569 fn test_azure_from_config_file ( ) {
@@ -767,32 +840,46 @@ mod tests {
767840 }
768841
769842 #[ pg_test]
770- #[ should_panic( expected = "list operation on http(s) object stores is not supported" ) ]
771- fn test_http_uri_glob_pattern ( ) {
843+ fn test_http_uri_with_special_chars ( ) {
772844 object_store_cache_clear ( ) ;
773845
774846 let http_endpoint: String =
775847 std:: env:: var ( "HTTP_ENDPOINT" ) . expect ( "HTTP_ENDPOINT not found" ) ;
776848
777- let http_uri =
778- format ! ( "{http_endpoint}/test_http_uri_glob_pattern/some/pg_parquet_test.parquet" ) ;
779-
780- let http_uri_pattern = format ! ( "{http_endpoint}/test_http_uri_glob_pattern/**/*.parquet" ) ;
849+ let http_uri = format ! ( "{http_endpoint}/so\\ mek*ey/**testme.parquet" ) ;
781850
782- let mut copy_options = HashMap :: new ( ) ;
783- copy_options. insert (
784- "file_size_bytes" . to_string ( ) ,
785- CopyOptionValue :: StringOption ( "1MB" . to_string ( ) ) ,
851+ let copy_to_command = format ! (
852+ "COPY (SELECT a FROM generate_series(1,10) a) TO '{http_uri}' WITH (format parquet);"
786853 ) ;
854+ Spi :: run ( & copy_to_command) . unwrap ( ) ;
787855
788- let test_table = TestTable :: < i32 > :: new ( "int4" . into ( ) )
789- . with_uri ( http_uri)
790- . with_uri_pattern ( http_uri_pattern)
791- . with_order_by_col ( "a" . into ( ) )
792- . with_copy_to_options ( copy_options) ;
856+ let create_table = "create table test_table(a int);" ;
857+ Spi :: run ( create_table) . unwrap ( ) ;
793858
794- test_table. insert ( "INSERT INTO test_expected select i from generate_series(1, 1000000) i;" ) ;
795- test_table. assert_expected_and_result_rows ( ) ;
859+ let copy_from_command = format ! ( "COPY test_table FROM '{http_uri}' WITH (format parquet);" ) ;
860+ Spi :: run ( copy_from_command. as_str ( ) ) . unwrap ( ) ;
861+
862+ let count_query = "select count(*) from test_table;" ;
863+ let result = Spi :: get_one :: < i64 > ( count_query) . unwrap ( ) . unwrap ( ) ;
864+ assert_eq ! ( result, 10 ) ;
865+ }
866+
867+ #[ pg_test]
868+ #[ should_panic( expected = "failed to get object store metadata for uri" ) ]
869+ fn test_http_with_nonexistent_pattern_uri ( ) {
870+ object_store_cache_clear ( ) ;
871+
872+ let http_endpoint: String =
873+ std:: env:: var ( "HTTP_ENDPOINT" ) . expect ( "HTTP_ENDPOINT not found" ) ;
874+
875+ let http_uri_pattern = format ! ( "{http_endpoint}/notexists/**" ) ;
876+
877+ let create_table = "create table test_table(a int);" ;
878+ Spi :: run ( create_table) . unwrap ( ) ;
879+
880+ let copy_from_command =
881+ format ! ( "COPY test_table FROM '{http_uri_pattern}' WITH (format parquet);" ) ;
882+ Spi :: run ( copy_from_command. as_str ( ) ) . unwrap ( ) ;
796883 }
797884
798885 #[ pg_test]
@@ -839,6 +926,49 @@ mod tests {
839926 test_table. assert_expected_and_result_rows ( ) ;
840927 }
841928
929+ #[ pg_test]
930+ fn test_gcs_uri_with_special_chars ( ) {
931+ object_store_cache_clear ( ) ;
932+
933+ let test_bucket_name: String =
934+ std:: env:: var ( "GOOGLE_TEST_BUCKET" ) . expect ( "GOOGLE_TEST_BUCKET not found" ) ;
935+
936+ let gcs_uri = format ! ( "gs://{test_bucket_name}/so\\ mek*ey/**testme.parquet" ) ;
937+
938+ let copy_to_command = format ! (
939+ "COPY (SELECT a FROM generate_series(1,10) a) TO '{gcs_uri}' WITH (format parquet);"
940+ ) ;
941+ Spi :: run ( & copy_to_command) . unwrap ( ) ;
942+
943+ let create_table = "create table test_table(a int);" ;
944+ Spi :: run ( create_table) . unwrap ( ) ;
945+
946+ let copy_from_command = format ! ( "COPY test_table FROM '{gcs_uri}' WITH (format parquet);" ) ;
947+ Spi :: run ( copy_from_command. as_str ( ) ) . unwrap ( ) ;
948+
949+ let count_query = "select count(*) from test_table;" ;
950+ let result = Spi :: get_one :: < i64 > ( count_query) . unwrap ( ) . unwrap ( ) ;
951+ assert_eq ! ( result, 10 ) ;
952+ }
953+
954+ #[ pg_test]
955+ #[ should_panic( expected = "no files found that match the pattern" ) ]
956+ fn test_gcs_with_nonexistent_pattern_uri ( ) {
957+ object_store_cache_clear ( ) ;
958+
959+ let test_bucket_name: String =
960+ std:: env:: var ( "GOOGLE_TEST_BUCKET" ) . expect ( "GOOGLE_TEST_BUCKET not found" ) ;
961+
962+ let gcs_uri_pattern = format ! ( "gs://{test_bucket_name}/notexists/**" ) ;
963+
964+ let create_table = "create table test_table(a int);" ;
965+ Spi :: run ( create_table) . unwrap ( ) ;
966+
967+ let copy_from_command =
968+ format ! ( "COPY test_table FROM '{gcs_uri_pattern}' WITH (format parquet);" ) ;
969+ Spi :: run ( copy_from_command. as_str ( ) ) . unwrap ( ) ;
970+ }
971+
842972 #[ pg_test]
843973 #[ should_panic( expected = "404 Not Found" ) ]
844974 fn test_gcs_write_wrong_bucket ( ) {
0 commit comments