1 /************************************************************************************
2    Copyright (C) 2013,2019 MariaDB Corporation AB
3 
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Library General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Library General Public License for more details.
13 
14    You should have received a copy of the GNU Library General Public
15    License along with this library; if not see <http://www.gnu.org/licenses>
16    or write to the Free Software Foundation, Inc.,
17    51 Franklin St., Fifth Floor, Boston, MA 02110, USA
18 *************************************************************************************/
19 #include <ma_odbc.h>
20 
21 extern MARIADB_CHARSET_INFO*  DmUnicodeCs;
22 
MADB_GetTableName(MADB_Stmt * Stmt)23 char *MADB_GetTableName(MADB_Stmt *Stmt)
24 {
25   char *TableName= NULL;
26  unsigned  int i= 0;
27   if (Stmt->TableName && Stmt->TableName[0])
28     return Stmt->TableName;
29   if (!mysql_stmt_field_count(Stmt->stmt))
30     return NULL;
31 
32   for (i=0; i < mysql_stmt_field_count(Stmt->stmt); i++)
33   {
34     if (Stmt->stmt->fields[i].org_table)
35     {
36       if (!TableName)
37         TableName= Stmt->stmt->fields[i].org_table;
38       if (strcmp(TableName, Stmt->stmt->fields[i].org_table))
39       {
40         MADB_SetError(&Stmt->Error, MADB_ERR_HY000, "Couldn't identify unique table name", 0);
41         return NULL;
42       }
43     }
44   }
45   if (TableName)
46     Stmt->TableName= _strdup(TableName);
47   return TableName;
48 }
49 
MADB_GetCatalogName(MADB_Stmt * Stmt)50 char *MADB_GetCatalogName(MADB_Stmt *Stmt)
51 {
52   char *CatalogName= NULL;
53   unsigned int i= 0;
54   if (Stmt->CatalogName && Stmt->CatalogName[0])
55     return Stmt->CatalogName;
56   if (!mysql_stmt_field_count(Stmt->stmt))
57     return NULL;
58 
59   for (i=0; i < mysql_stmt_field_count(Stmt->stmt); i++)
60   {
61     if (Stmt->stmt->fields[i].org_table)
62     {
63       if (!CatalogName)
64         CatalogName= Stmt->stmt->fields[i].db;
65       if (strcmp(CatalogName, Stmt->stmt->fields[i].db))
66       {
67         MADB_SetError(&Stmt->Error, MADB_ERR_HY000, "Couldn't identify unique catalog name", 0);
68         return NULL;
69       }
70     }
71   }
72   if (CatalogName)
73     Stmt->CatalogName= _strdup(CatalogName);
74   return CatalogName;
75 }
76 
MADB_DynStrAppendQuoted(MADB_DynString * DynString,char * String)77 my_bool MADB_DynStrAppendQuoted(MADB_DynString *DynString, char *String)
78 {
79   if (MADB_DynstrAppendMem(DynString, "`", 1) ||
80     MADB_DynstrAppend(DynString, String) ||
81     MADB_DynstrAppendMem(DynString, "`", 1))
82   {
83     return TRUE;
84   }
85   return FALSE;
86 }
87 
MADB_DynStrUpdateSet(MADB_Stmt * Stmt,MADB_DynString * DynString)88 my_bool MADB_DynStrUpdateSet(MADB_Stmt *Stmt, MADB_DynString *DynString)
89 {
90   int             i, IgnoredColumns= 0;
91   MADB_DescRecord *Record;
92 
93   if (MADB_DYNAPPENDCONST(DynString, " SET "))
94   {
95     MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0);
96     return TRUE;
97   }
98   // ???? memcpy(&Stmt->Da->Apd->Header, &Stmt->Ard->Header, sizeof(MADB_Header));
99   for (i=0; i < MADB_STMT_COLUMN_COUNT(Stmt); i++)
100   {
101     SQLLEN *IndicatorPtr= NULL;
102     Record= MADB_DescGetInternalRecord(Stmt->Ard, i, MADB_DESC_READ);
103     if (Record->IndicatorPtr)
104       IndicatorPtr= (SQLLEN *)GetBindOffset(Stmt->Ard, Record, Record->IndicatorPtr, Stmt->DaeRowNumber > 1 ? Stmt->DaeRowNumber-1 : 0,
105                                             sizeof(SQLLEN)/*Record->OctetLength*/);
106     if ((IndicatorPtr && *IndicatorPtr == SQL_COLUMN_IGNORE) || !Record->inUse)
107     {
108       IgnoredColumns++;
109       continue;
110     }
111 
112     if ((i - IgnoredColumns) && MADB_DYNAPPENDCONST(DynString, ","))
113     {
114       MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0);
115       return TRUE;
116     }
117     if (MADB_DynStrAppendQuoted(DynString, Stmt->stmt->fields[i].org_name))
118     {
119       MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0);
120       return TRUE;
121     }
122     if (MADB_DYNAPPENDCONST(DynString, "=?"))
123     {
124       MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0);
125       return TRUE;
126     }
127   }
128   if (IgnoredColumns == mysql_stmt_field_count(Stmt->stmt))
129   {
130     MADB_SetError(&Stmt->Error, MADB_ERR_21S02, NULL, 0);
131     return TRUE;
132   }
133   return FALSE;
134 }
135 
MADB_DynStrInsertSet(MADB_Stmt * Stmt,MADB_DynString * DynString)136 my_bool MADB_DynStrInsertSet(MADB_Stmt *Stmt, MADB_DynString *DynString)
137 {
138   MADB_DynString  ColVals;
139   int             i, NeedComma= 0;
140   MADB_DescRecord *Record;
141 
142   MADB_InitDynamicString(&ColVals, "VALUES (", 32, 32);
143   if (MADB_DYNAPPENDCONST(DynString, " ("))
144   {
145     goto dynerror;
146 
147     return TRUE;
148   }
149 
150   /* We use only columns, that have been bound, and are not IGNORED */
151   for (i= 0; i < MADB_STMT_COLUMN_COUNT(Stmt); i++)
152   {
153     Record= MADB_DescGetInternalRecord(Stmt->Ard, i, MADB_DESC_READ);
154     if (!Record->inUse || MADB_ColumnIgnoredInAllRows(Stmt->Ard, Record) == TRUE)
155     {
156       continue;
157     }
158 
159     if ((NeedComma) &&
160         (MADB_DYNAPPENDCONST(DynString, ",") || MADB_DYNAPPENDCONST(&ColVals, ",")))
161       goto dynerror;
162 
163     if (MADB_DynStrAppendQuoted(DynString, Stmt->stmt->fields[i].org_name) ||
164         MADB_DYNAPPENDCONST(&ColVals, "?"))
165        goto dynerror;
166 
167     NeedComma= 1;
168   }
169   if (MADB_DYNAPPENDCONST(DynString, ") " ) ||
170       MADB_DYNAPPENDCONST(&ColVals, ")") ||
171       MADB_DynstrAppend(DynString, ColVals.str))
172     goto dynerror;
173   MADB_DynstrFree(&ColVals);
174   return FALSE;
175 dynerror:
176   MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0);
177   MADB_DynstrFree(&ColVals);
178   return TRUE;
179 }
180 
MADB_DynStrGetColumns(MADB_Stmt * Stmt,MADB_DynString * DynString)181 my_bool MADB_DynStrGetColumns(MADB_Stmt *Stmt, MADB_DynString *DynString)
182 {
183   unsigned int i;
184   if (MADB_DYNAPPENDCONST(DynString, " ("))
185   {
186     MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0);
187     return TRUE;
188   }
189   for (i=0; i < mysql_stmt_field_count(Stmt->stmt); i++)
190   {
191     if (i && MADB_DYNAPPENDCONST(DynString, ", "))
192     {
193       MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0);
194       return TRUE;
195     }
196     if (MADB_DynStrAppendQuoted(DynString, Stmt->stmt->fields[i].org_name))
197     {
198       MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0);
199       return TRUE;
200     }
201   }
202   if (MADB_DYNAPPENDCONST(DynString, " )"))
203   {
204     MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0);
205     return TRUE;
206   }
207   return FALSE;
208 }
209 
MADB_DynStrGetWhere(MADB_Stmt * Stmt,MADB_DynString * DynString,char * TableName,my_bool ParameterMarkers)210 my_bool MADB_DynStrGetWhere(MADB_Stmt *Stmt, MADB_DynString *DynString, char *TableName, my_bool ParameterMarkers)
211 {
212   int UniqueCount=0, PrimaryCount= 0;
213   int i, Flag= 0;
214   char *Column= NULL, *Escaped= NULL;
215   SQLLEN StrLength;
216   unsigned long EscapedLength;
217 
218   for (i= 0; i < MADB_STMT_COLUMN_COUNT(Stmt); i++)
219   {
220     MYSQL_FIELD *field= mysql_fetch_field_direct(FetchMetadata(Stmt), i);
221     if (field->flags & PRI_KEY_FLAG)
222       PrimaryCount++;
223     if (field->flags & UNIQUE_KEY_FLAG)
224       UniqueCount++;
225   }
226   /* We need to use all columns, otherwise it will be difficult to map fields for Positioned Update */
227   if (PrimaryCount && PrimaryCount != MADB_KeyTypeCount(Stmt->Connection, TableName, PRI_KEY_FLAG))
228     PrimaryCount= 0;
229   if (UniqueCount && UniqueCount != MADB_KeyTypeCount(Stmt->Connection, TableName, UNIQUE_KEY_FLAG))
230     UniqueCount= 0;
231 
232   /* if no primary or unique key is in the cursor, the cursor must contain all
233      columns from table in TableName */
234   if (!PrimaryCount && !UniqueCount)
235   {
236     char      StmtStr[256];
237     MADB_Stmt *CountStmt;
238     int       FieldCount= 0;
239 
240     MA_SQLAllocHandle(SQL_HANDLE_STMT, Stmt->Connection, (SQLHANDLE*)&CountStmt);
241     _snprintf(StmtStr, 256, "SELECT * FROM `%s` LIMIT 0", TableName);
242     CountStmt->Methods->ExecDirect(CountStmt, (char *)StmtStr, SQL_NTS);
243     FieldCount= mysql_stmt_field_count(((MADB_Stmt *)CountStmt)->stmt);
244     CountStmt->Methods->StmtFree(CountStmt, SQL_DROP);
245 
246     if (FieldCount != MADB_STMT_COLUMN_COUNT(Stmt))
247     {
248       MADB_SetError(&Stmt->Error, MADB_ERR_S1000, "Can't build index for update/delete", 0);
249       return TRUE;
250     }
251   }
252   if (MADB_DYNAPPENDCONST(DynString, " WHERE 1"))
253     goto memerror;
254   for (i= 0; i < MADB_STMT_COLUMN_COUNT(Stmt); i++)
255   {
256     MYSQL_FIELD *field= mysql_fetch_field_direct(Stmt->metadata, i);
257     if (field->flags & Flag || !Flag)
258     {
259       if (MADB_DYNAPPENDCONST(DynString, " AND ") ||
260           MADB_DynStrAppendQuoted(DynString, field->org_name))
261           goto memerror;
262       if (ParameterMarkers)
263       {
264         if (MADB_DYNAPPENDCONST(DynString, "=?"))
265           goto memerror;
266       }
267       else
268       {
269         if (!SQL_SUCCEEDED(Stmt->Methods->GetData(Stmt, i+1, SQL_C_CHAR, NULL, 0, &StrLength, TRUE)))
270         {
271           MADB_FREE(Column);
272           return TRUE;
273         }
274         if (StrLength < 0)
275         {
276            if (MADB_DYNAPPENDCONST(DynString, " IS NULL"))
277              goto memerror;
278         }
279         else
280         {
281           Column= MADB_CALLOC(StrLength + 1);
282           Stmt->Methods->GetData(Stmt,i+1, SQL_C_CHAR, Column, StrLength + 1, &StrLength, TRUE);
283           Escaped = MADB_CALLOC(2 * StrLength + 1);
284           EscapedLength= mysql_real_escape_string(Stmt->Connection->mariadb, Escaped, Column, (unsigned long)StrLength);
285 
286           if (MADB_DYNAPPENDCONST(DynString, "= '") ||
287             MADB_DynstrAppend(DynString, Escaped) ||//, EscapedLength) ||
288             MADB_DYNAPPENDCONST(DynString, "'"))
289           {
290             goto memerror;
291           }
292           MADB_FREE(Column);
293           MADB_FREE(Escaped);
294         }
295       }
296     }
297   }
298   if (MADB_DYNAPPENDCONST(DynString, " LIMIT 1"))
299     goto memerror;
300   MADB_FREE(Column);
301 
302   return FALSE;
303 
304 memerror:
305   MADB_FREE(Column);
306   MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0);
307 
308   return TRUE;
309 }
310 
311 
MADB_DynStrGetValues(MADB_Stmt * Stmt,MADB_DynString * DynString)312 my_bool MADB_DynStrGetValues(MADB_Stmt *Stmt, MADB_DynString *DynString)
313 {
314   unsigned int i;
315   if (MADB_DYNAPPENDCONST(DynString, " VALUES("))
316   {
317     MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0);
318     return TRUE;
319   }
320   for (i=0; i < mysql_stmt_field_count(Stmt->stmt); i++)
321   {
322     if (MADB_DynstrAppend(DynString, (i) ? ",?" : "?"))
323     {
324       MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0);
325       return TRUE;
326     }
327   }
328   if (MADB_DYNAPPENDCONST(DynString, ")"))
329   {
330     MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0);
331     return TRUE;
332   }
333   return FALSE;
334 }
335 
MADB_GetInsertStatement(MADB_Stmt * Stmt)336 char *MADB_GetInsertStatement(MADB_Stmt *Stmt)
337 {
338   char *StmtStr;
339   size_t Length= 1024;
340   char *p;
341   char *TableName;
342   unsigned int i;
343 
344   if (!(StmtStr= MADB_CALLOC(1024)))
345   {
346     MADB_SetError(&Stmt->Error, MADB_ERR_HY013, NULL, 0);
347     return NULL;
348   }
349   if (!(TableName= MADB_GetTableName(Stmt)))
350     goto error;
351   p= StmtStr;
352 
353   p+= _snprintf(StmtStr, 1024, "INSERT INTO `%s` (", TableName);
354   for (i=0; i < mysql_stmt_field_count(Stmt->stmt); i++)
355   {
356     if (strlen(StmtStr) > Length - NAME_LEN - 4/* comma + 2 ticks + terminating NULL */)
357     {
358       Length+= 1024;
359       if (!(StmtStr= MADB_REALLOC(StmtStr, Length)))
360       {
361         MADB_SetError(&Stmt->Error, MADB_ERR_HY013, NULL, 0);
362         goto error;
363       }
364     }
365     p+= _snprintf(p, Length - strlen(StmtStr), "%s`%s`", (i==0) ? "" : ",", Stmt->stmt->fields[i].org_name);
366   }
367   p+= _snprintf(p, Length - strlen(StmtStr), ") VALUES (");
368 
369   if (strlen(StmtStr) > Length - mysql_stmt_field_count(Stmt->stmt)*2 - 1)/* , and ? for each column  + (- 1 comma for 1st column + closing ')')
370                                                                             + terminating NULL */
371   {
372     Length= strlen(StmtStr) + mysql_stmt_field_count(Stmt->stmt)*2 + 1;
373     if (!(StmtStr= MADB_REALLOC(StmtStr, Length)))
374     {
375       MADB_SetError(&Stmt->Error, MADB_ERR_HY013, NULL, 0);
376       goto error;
377     }
378   }
379 
380   for (i=0; i < mysql_stmt_field_count(Stmt->stmt); i++)
381   {
382     p+= _snprintf(p, Length - strlen(StmtStr), "%s?", (i==0) ? "" : ",");
383   }
384   p+= _snprintf(p, Length - strlen(StmtStr), ")");
385   return StmtStr;
386 
387 error:
388   if (StmtStr)
389     MADB_FREE(StmtStr);
390   return NULL;
391 }
392 
393 
MADB_ValidateStmt(MADB_QUERY * Query)394 my_bool MADB_ValidateStmt(MADB_QUERY *Query)
395 {
396   return Query->QueryType != MADB_QUERY_SET_NAMES;
397 }
398 
399 
MADB_ToLower(const char * src,char * buff,size_t buff_size)400 char *MADB_ToLower(const char *src, char *buff, size_t buff_size)
401 {
402   size_t i= 0;
403 
404   if (buff_size > 0)
405   {
406     while (*src && i < buff_size)
407     {
408       buff[i++]= tolower(*src++);
409     }
410 
411     buff[i == buff_size ? i - 1 : i]= '\0';
412   }
413   return buff;
414 }
415 
416 
InitClientCharset(Client_Charset * cc,const char * name)417 int InitClientCharset(Client_Charset *cc, const char * name)
418 {
419   /* There is no legal charset names longer than 31 chars */
420   char lowered[32];
421   cc->cs_info= mariadb_get_charset_by_name(MADB_ToLower(name, lowered, sizeof(lowered)));
422 
423   if (cc->cs_info == NULL)
424   {
425     return 1;
426   }
427 
428   cc->CodePage= cc->cs_info->codepage;
429 
430   return 0;
431 }
432 
433 
CopyClientCharset(Client_Charset * Src,Client_Charset * Dst)434 void CopyClientCharset(Client_Charset * Src, Client_Charset * Dst)
435 {
436   Dst->CodePage= Src->CodePage;
437   Dst->cs_info= Src->cs_info;
438 }
439 
440 
CloseClientCharset(Client_Charset * cc)441 void CloseClientCharset(Client_Charset *cc)
442 {
443 }
444 
445 
446 /* Hmmm... Length in characters is SQLLEN, octet length SQLINTEGER */
MbstrOctetLen(const char * str,SQLLEN * CharLen,MARIADB_CHARSET_INFO * cs)447 SQLLEN MbstrOctetLen(const char *str, SQLLEN *CharLen, MARIADB_CHARSET_INFO *cs)
448 {
449   SQLLEN result= 0, inChars= *CharLen;
450 
451   if (str)
452   {
453     if (cs->mb_charlen == NULL)
454     {
455       /* Charset uses no more than a byte per char. Result is strlen or umber of chars */
456       if (*CharLen < 0)
457       {
458         result= (SQLLEN)strlen(str);
459         *CharLen= result;
460       }
461       else
462       {
463         result= *CharLen;
464       }
465       return result;
466     }
467     else
468     {
469       while (inChars > 0 || (inChars < 0 && *str))
470       {
471         result+= cs->mb_charlen(0 + *str);
472         --inChars;
473         str+= cs->mb_charlen(*str);
474       }
475     }
476   }
477 
478   if (*CharLen < 0)
479   {
480     *CharLen-= inChars;
481   }
482   return result;
483 }
484 
485 
486 /* Number of characters in given number of bytes */
MbstrCharLen(const char * str,SQLINTEGER OctetLen,MARIADB_CHARSET_INFO * cs)487 SQLLEN MbstrCharLen(const char *str, SQLINTEGER OctetLen, MARIADB_CHARSET_INFO *cs)
488 {
489   SQLLEN       result= 0;
490   const char   *ptr= str;
491   unsigned int charlen;
492 
493   if (str)
494   {
495     if (cs->mb_charlen == NULL || cs->char_maxlen == 1)
496     {
497       return OctetLen;
498     }
499     while (ptr < str + OctetLen)
500     {
501       charlen= cs->mb_charlen((unsigned char)*ptr);
502       if (charlen == 0)
503       {
504         /* Dirty hack to avoid dead loop - Has to be the error! */
505         charlen= 1;
506       }
507 
508       /* Skipping thru 0 bytes */
509       while (charlen > 0 && *ptr == '\0')
510       {
511           --charlen;
512           ++ptr;
513       }
514 
515       /* Stopping if current character is terminating NULL - charlen == 0 means all bytes of current char was 0 */
516       if (charlen == 0)
517       {
518         return result;
519       }
520       /* else we increment ptr for number of left bytes */
521       ptr+= charlen;
522       ++result;
523     }
524   }
525 
526   return result;
527 }
528 
529 
530 /* Length of NT SQLWCHAR string in characters */
SqlwcsCharLen(SQLWCHAR * str,SQLLEN octets)531 SQLINTEGER SqlwcsCharLen(SQLWCHAR *str, SQLLEN octets)
532 {
533   SQLINTEGER result= 0;
534   SQLWCHAR   *end=   octets != (SQLLEN)-1 ? str + octets/sizeof(SQLWCHAR) : (SQLWCHAR*)octets /*for simplicity - the address to be always bigger */;
535 
536   if (str)
537   {
538     while (str < end && *str)
539     {
540       str+= (DmUnicodeCs->mb_charlen(*str))/sizeof(SQLWCHAR);
541 
542       if (str > end)
543       {
544         break;
545       }
546       ++result;
547     }
548   }
549   return result;
550 }
551 
552 
553 /* Length in SQLWCHAR units
554    @buff_length[in] - size of the str buffer or negative number  */
SqlwcsLen(SQLWCHAR * str,SQLLEN buff_length)555 SQLLEN SqlwcsLen(SQLWCHAR *str, SQLLEN buff_length)
556 {
557   SQLINTEGER result= 0;
558 
559   if (str)
560   {
561     /* If buff_length is negative - we will never hit 1st condition, otherwise we hit it after last character
562        of the buffer is processed */
563     while ((--buff_length) != -1 && *str)
564     {
565       ++result;
566       ++str;
567     }
568   }
569   return result;
570 }
571 
572 /* Length of a string with respect to specified buffer size
573 @buff_length[in] - size of the str buffer or negative number  */
SafeStrlen(SQLCHAR * str,SQLLEN buff_length)574 SQLLEN SafeStrlen(SQLCHAR *str, SQLLEN buff_length)
575 {
576   SQLINTEGER result= 0;
577 
578   if (str)
579   {
580     /* If buff_length is negative - we will never hit 1st condition, otherwise we hit it after last character
581     of the buffer is processed */
582     while ((--buff_length) != -1 && *str)
583     {
584       ++result;
585       ++str;
586     }
587   }
588   return result;
589 }