FOSSology  4.4.0
Open Source License Compliance by Open Source Software
sqlCopy.c
Go to the documentation of this file.
1 /*
2  SPDX-FileCopyrightText: © 2011 Hewlett-Packard Development Company, L.P.
3 
4  SPDX-License-Identifier: LGPL-2.1-only
5 */
6 
35 #include "sqlCopy.h"
36 
51 psqlCopy_t fo_sqlCopyCreate(PGconn* pGconn, char* TableName, int BufSize, int NumColumns, ...)
52 {
53  psqlCopy_t pCopy;
54  va_list ColumnNameArg;
55  int ColIdx;
56  int ColStrLength;
57  char* ColStr;
58 
59  va_start(ColumnNameArg, NumColumns);
60 
61  /* Allocate the structure */
62  pCopy = malloc(sizeof(sqlCopy_t));
63  if (!pCopy)
64  {
65  va_end(ColumnNameArg);
66  ERROR_RETURN("sqlCopy malloc")
67  }
68 
69  /* Allocate storage for the data buffer */
70  if (BufSize < 1) BufSize = 1;
71  pCopy->DataBuf = calloc(BufSize, sizeof(char));
72 
73  /* Save TableName */
74  pCopy->TableName =g_strdup(TableName);
75 
76  /* check for malloc failures */
77  if ((!pCopy->DataBuf) || (!pCopy->TableName))
78  {
79  fo_sqlCopyDestroy(pCopy, 0);
80  va_end(ColumnNameArg);
81  ERROR_RETURN("sqlCopyCreate")
82  }
83 
84  /* Save the DB connection */
85  pCopy->pGconn = pGconn;
86 
87  /* Save the data buffer size */
88  pCopy->BufSize = BufSize;
89 
90  /* Data buffer is empty */
91  pCopy->DataIdx = 0;
92 
93  /* Build the column name string pCopy->ColumnNames */
94  ColStrLength = 0;
95  pCopy->ColumnNames[0] = 0;
96  for (ColIdx = 0; ColIdx < NumColumns; ColIdx++)
97  {
98  ColStr = va_arg(ColumnNameArg, char *);
99  ColStrLength += strlen(ColStr) + 1; /* extra 1 for the comma */
100  if (ColStrLength < sizeof(pCopy->ColumnNames))
101  {
102  if (ColIdx != 0) strncat(pCopy->ColumnNames, ",", 2);
103  strncat(pCopy->ColumnNames, ColStr, ColStrLength);
104  }
105  else
106  {
107  fo_sqlCopyDestroy(pCopy, 0);
108  va_end(ColumnNameArg);
109  ERROR_RETURN("pCopy->ColumnNames size too small")
110  }
111  }
112  va_end(ColumnNameArg);
113  return (pCopy);
114 } /* End fo_sqlCopyCreate() */
115 
116 
117 #ifdef DEBUG
118 int tmp_printhex(char * str)
119 {
120  while(*str) printf("%02x", *str++);
121  return(0);
122 }
123 #endif
124 
125 #define growby 128
126 
146 int fo_sqlCopyAdd(psqlCopy_t pCopy, char* DataRow)
147 {
148  int NewRowLen;
149  int rncount = 0;
150  char* dptr = DataRow;
151  char* NewRow = 0, * nptr;
152 
153  /* As of Postgresql 8.4, COPY will not accept embedded literal carriage returns
154  * or line feeds. Use "\r" and "\n" instead.
155  * Count how many we need to get rid of (and make space for).
156  */
157  while (*dptr)
158  {
159  if (((*dptr == '\n') || (*dptr == '\r')) && (*(dptr + 1))) rncount++;
160  dptr++;
161  }
162 
163  /* Substitute any literal '\n' or '\r' for string "\n", "\r"
164  * (except for trailing \n which is required)
165  */
166  if (rncount)
167  {
168  NewRowLen = strlen(DataRow) + rncount;
169  NewRow = malloc(NewRowLen + 1); // plus 1 for potential required \n at end
170  if (!NewRow)
171  ERROR_RETURN("fo_sqlCopyAdd: out of memory");
172  nptr = NewRow;
173  dptr = DataRow;
174  while (*dptr && *(dptr + 1))
175  {
176  if (*dptr == '\n')
177  {
178  *nptr++ = '\\';
179  *nptr = 'n';
180  }
181  else if (*dptr == '\r')
182  {
183  *nptr++ = '\\';
184  *nptr = 'r';
185  }
186  else
187  *nptr = *dptr;
188  ++dptr;
189  ++nptr;
190  }
191  *nptr = 0; // null terminator
192  DataRow = NewRow;
193  }
194 
195  /* Does new record fit in DataBuf? */
196  NewRowLen = strlen(DataRow);
197  if ((pCopy->BufSize - pCopy->DataIdx) < (NewRowLen + 1))
198  {
199  /* if DataIdx is zero, then DataBuf isn't big enough to hold
200  * this record. In this case make DataBuf larger.
201  */
202  if (pCopy->DataIdx == 0)
203  {
204  pCopy->DataBuf = realloc(pCopy->DataBuf, NewRowLen + growby);
205  if (!pCopy->DataBuf)
206  ERROR_RETURN("fo_sqlCopyAdd: Realloc for DataBuf failed");
207  pCopy->BufSize = NewRowLen + growby;
208  }
209  else
210  {
211  /* Execute a copy to make room in DataBuf */
212  fo_sqlCopyExecute(pCopy);
213  }
214  }
215 
216  /* copy in DataRow */
217  strcpy(pCopy->DataBuf + pCopy->DataIdx, DataRow);
218  pCopy->DataIdx += NewRowLen;
219 
220  /* If the DataRow was missing a terminating newline, add one */
221  if (DataRow[NewRowLen - 1] != '\n')
222  {
223  pCopy->DataBuf[pCopy->DataIdx++] = '\n';
224  pCopy->DataBuf[pCopy->DataIdx] = 0; // new null terminator
225  }
226 
227  if (NewRow) free(NewRow);
228  return (1);
229 }
230 
242 {
243  char copystmt[2048];
244  PGresult* result;
245 
246  /* check pCopy */
247  if (!pCopy)
248  ERROR_RETURN("Null pCopy");
249  if (pCopy->DataIdx == 0) return (1); /* nothing to copy */
250 
251  /* Start the Copy command */
252  sprintf(copystmt, "COPY %s(%s) from stdin",
253  pCopy->TableName,
254  pCopy->ColumnNames);
255  result = PQexec(pCopy->pGconn, copystmt);
256  if (PGRES_COPY_IN == PQresultStatus(result))
257  {
258  PQclear(result);
259  if (PQputCopyData(pCopy->pGconn, pCopy->DataBuf, pCopy->DataIdx) != 1)
260  ERROR_RETURN(PQresultErrorMessage(result))
261  }
262  else if (fo_checkPQresult(pCopy->pGconn, result, copystmt, __FILE__, __LINE__)) return 0;
263 
264 
265  /* End copy */
266  if (PQputCopyEnd(pCopy->pGconn, NULL) == 1)
267  {
268  result = PQgetResult(pCopy->pGconn);
269  if (fo_checkPQcommand(pCopy->pGconn, result, "copy end", __FILE__, __LINE__)) return 0;
270  }
271  PQclear(result);
272 
273  /* reset DataBuf */
274  pCopy->DataIdx = 0;
275 
276  return (1);
277 }
278 
279 
293 void fo_sqlCopyDestroy(psqlCopy_t pCopy, int ExecuteFlag)
294 {
295  if (!pCopy) return;
296  if (ExecuteFlag) fo_sqlCopyExecute(pCopy);
297  if (pCopy->TableName) free(pCopy->TableName);
298  if (pCopy->DataBuf) free(pCopy->DataBuf);
299  free(pCopy);
300 }
301 
302 
313 void fo_sqlCopyPrint(psqlCopy_t pCopy, int PrintBytes)
314 {
315  int idx;
316 
317  printf("========== fo_sqlCopyPrint Start ================\n");
318  printf("pCopy: %lx, TableName: %s, BufSize: %d, DataIdx: %d\n",
319  (long) pCopy, pCopy->TableName, pCopy->BufSize, pCopy->DataIdx);
320  printf(" ColumnNames: %s\n", pCopy->ColumnNames);
321 
322  if (PrintBytes == 0) PrintBytes = pCopy->DataIdx;
323  for (idx = 0; idx < PrintBytes; idx++) putchar(pCopy->DataBuf[idx]);
324 
325  printf("========== fo_sqlCopyPrint End ================");
326 }
int fo_checkPQresult(PGconn *pgConn, PGresult *result, char *sql, char *FileID, int LineNumb)
Check the result status of a postgres SELECT.
Definition: libfossdb.c:170
int fo_checkPQcommand(PGconn *pgConn, PGresult *result, char *sql, char *FileID, int LineNumb)
Check the result status of a postgres commands (not select) If an error occured, write the error to s...
Definition: libfossdb.c:204
int fo_sqlCopyAdd(psqlCopy_t pCopy, char *DataRow)
Add a data row to an sqlCopy Use '\N' to pass in a null.
Definition: sqlCopy.c:146
void fo_sqlCopyPrint(psqlCopy_t pCopy, int PrintBytes)
Print the sqlCopy_struct.
Definition: sqlCopy.c:313
#define growby
Grow DataBuf by this number of bytes.
Definition: sqlCopy.c:125
void fo_sqlCopyDestroy(psqlCopy_t pCopy, int ExecuteFlag)
Destructor for sqlCopy_struct.
Definition: sqlCopy.c:293
psqlCopy_t fo_sqlCopyCreate(PGconn *pGconn, char *TableName, int BufSize, int NumColumns,...)
Constructor for sqlCopy_struct.
Definition: sqlCopy.c:51
int fo_sqlCopyExecute(psqlCopy_t pCopy)
Execute the copy (ie insert the buffered records into the database).
Definition: sqlCopy.c:241
char ColumnNames[1024]
Definition: sqlCopy.h:32
char * TableName
Definition: sqlCopy.h:30
PGconn * pGconn
Definition: sqlCopy.h:28