1717 * under the License.
1818 */
1919
20- #include < chrono>
21- #include < cstdlib>
22- #include < iostream>
23- #include < vector>
20+ #include " insert.hh"
2421
25- #include < libpq-fe .h>
22+ #include < arpa/inet .h>
2623
27- class ConnectionFinisher {
28- public:
29- ConnectionFinisher (PGconn* connection) : connection_(connection) {}
30- ~ConnectionFinisher () { PQfinish (connection_); }
31-
32- private:
33- PGconn* connection_;
34- };
35-
36- class ResultClearner {
37- public:
38- ResultClearner (PGresult* result) : result_(result) {}
39- ~ResultClearner () { PQclear (result_); }
40-
41- private:
42- PGresult* result_;
43- };
24+ namespace {
25+ template <typename Type>
26+ void
27+ write_binary_data (std::ostream& stream, Type value)
28+ {
29+ stream.write (reinterpret_cast <const char *>(&value), sizeof (Type));
30+ }
31+ }; // namespace
4432
4533int
4634main (int argc, char ** argv)
@@ -78,33 +66,31 @@ main(int argc, char** argv)
7866 }
7967 }
8068
81- std::vector<std::string> buffers ;
69+ std::vector<std::vector<Value>> records ;
8270 {
83- auto result = PQexec (connection, " COPY data TO STDOUT (FORMAT binary) " );
71+ auto result = PQexec (connection, " SELECT * FROM data " );
8472 ResultClearner resultClearner (result);
85- if (PQresultStatus (result) != PGRES_COPY_OUT )
73+ if (PQresultStatus (result) != PGRES_TUPLES_OK )
8674 {
87- std::cerr << " failed to copy to : " << PQerrorMessage (connection) << std::endl;
75+ std::cerr << " failed to select : " << PQerrorMessage (connection) << std::endl;
8876 return EXIT_FAILURE;
8977 }
90- while (true )
78+ auto nTuples = PQntuples (result);
79+ auto nFields = PQnfields (result);
80+ for (int iTuple = 0 ; iTuple < nTuples; iTuple++)
9181 {
92- char * data;
93- auto size = PQgetCopyData (connection, &data, 0 );
94- if (size == -1 )
95- {
96- break ;
97- }
98- if (size == -2 )
82+ std::vector<Value> values;
83+ for (int iField = 0 ; iField < nFields; iField++)
9984 {
100- std::cerr << " failed to read copy data: " << PQerrorMessage (connection)
101- << std::endl;
102- return EXIT_FAILURE;
85+ if (!append_value (values, result, iTuple, iField))
86+ {
87+ return EXIT_FAILURE;
88+ }
10389 }
104- buffers.emplace_back (data, size);
105- free (data);
90+ records.push_back (std::move (values));
10691 }
10792 }
93+
10894 auto before = std::chrono::steady_clock::now ();
10995 {
11096 auto result = PQexec (connection, " COPY data_insert FROM STDOUT (FORMAT binary)" );
@@ -115,15 +101,58 @@ main(int argc, char** argv)
115101 << std::endl;
116102 return EXIT_FAILURE;
117103 }
118- for ( const auto & buffer : buffers)
104+ std::ostringstream copyDataStream;
119105 {
120- auto copyDataResult = PQputCopyData (connection, buffer.data (), buffer.size ());
121- if (copyDataResult == -1 )
106+ // See the "Binary Format" section in
107+ // https://www.postgresql.org/docs/current/sql-copy.html for
108+ // details.
109+
110+ const char signature[] = " PGCOPY\n\377\r\n " ;
111+ // The last '\0' is also part of the signature.
112+ copyDataStream << std::string_view (signature, sizeof (signature));
113+ const uint32_t flags = 0 ;
114+ write_binary_data (copyDataStream, htonl (flags));
115+ const uint32_t headerExtensionAreaLength = 0 ;
116+ write_binary_data (copyDataStream, htonl (headerExtensionAreaLength));
117+ auto nRecords = records.size ();
118+ for (size_t iRecord = 0 ; iRecord < nRecords; ++iRecord)
122119 {
123- std::cerr << " failed to put copy data: " << PQerrorMessage (connection)
124- << std::endl;
125- return EXIT_FAILURE;
120+ const auto & values = records[iRecord];
121+ const auto nValues = values.size ();
122+ write_binary_data (copyDataStream, htons (nValues));
123+ for (size_t iValue = 0 ; iValue < nValues; ++iValue)
124+ {
125+ const auto & value = values[iValue];
126+ if (std::holds_alternative<std::monostate>(value))
127+ {
128+ write_binary_data (copyDataStream,
129+ htonl (static_cast <uint32_t >(-1 )));
130+ }
131+ else if (std::holds_alternative<int32_t >(value))
132+ {
133+ const auto & int32Value = std::get<int32_t >(value);
134+ write_binary_data (copyDataStream, htonl (sizeof (int32_t )));
135+ write_binary_data (copyDataStream,
136+ htonl (static_cast <uint32_t >(int32Value)));
137+ }
138+ else if (std::holds_alternative<std::string>(value))
139+ {
140+ const auto & stringValue = std::get<std::string>(value);
141+ write_binary_data (copyDataStream, htonl (stringValue.size ()));
142+ copyDataStream << stringValue;
143+ }
144+ }
126145 }
146+ const uint16_t fileTrailer = -1 ;
147+ write_binary_data (copyDataStream, htons (fileTrailer));
148+ }
149+ const auto & copyData = copyDataStream.str ();
150+ auto copyDataResult = PQputCopyData (connection, copyData.data (), copyData.size ());
151+ if (copyDataResult == -1 )
152+ {
153+ std::cerr << " failed to put copy data: " << PQerrorMessage (connection)
154+ << std::endl;
155+ return EXIT_FAILURE;
127156 }
128157 auto copyEndResult = PQputCopyEnd (connection, nullptr );
129158 if (copyEndResult == -1 )
0 commit comments