1 /********************************************************************/
2 /*                                                                  */
3 /*  sql_db2.c     Database access functions for Db2.                */
4 /*  Copyright (C) 1989 - 2020  Thomas Mertes                        */
5 /*                                                                  */
6 /*  This file is part of the Seed7 Runtime Library.                 */
7 /*                                                                  */
8 /*  The Seed7 Runtime Library is free software; you can             */
9 /*  redistribute it and/or modify it under the terms of the GNU     */
10 /*  Lesser General Public License as published by the Free Software */
11 /*  Foundation; either version 2.1 of the License, or (at your      */
12 /*  option) any later version.                                      */
13 /*                                                                  */
14 /*  The Seed7 Runtime Library is distributed in the hope that it    */
15 /*  will be useful, but WITHOUT ANY WARRANTY; without even the      */
16 /*  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR */
17 /*  PURPOSE.  See the GNU Lesser General Public License for more    */
18 /*  details.                                                        */
19 /*                                                                  */
20 /*  You should have received a copy of the GNU Lesser General       */
21 /*  Public License along with this program; if not, write to the    */
22 /*  Free Software Foundation, Inc., 51 Franklin Street,             */
23 /*  Fifth Floor, Boston, MA  02110-1301, USA.                       */
24 /*                                                                  */
25 /*  Module: Seed7 Runtime Library                                   */
26 /*  File: seed7/src/sql_db2.c                                       */
27 /*  Changes: 2019 - 2020  Thomas Mertes                             */
28 /*  Content: Database access functions for Db2.                     */
29 /*                                                                  */
30 /********************************************************************/
31 
32 #define LOG_FUNCTIONS 0
33 #define VERBOSE_EXCEPTIONS 0
34 
35 #include "version.h"
36 
37 #include "stdlib.h"
38 #include "stdio.h"
39 #include "string.h"
40 #include "time.h"
41 #include "limits.h"
42 #ifdef DB2_INCLUDE
43 #include DB2_INCLUDE
44 #endif
45 
46 #include "common.h"
47 #include "striutl.h"
48 #include "heaputl.h"
49 #include "numutl.h"
50 #include "str_rtl.h"
51 #include "tim_rtl.h"
52 #include "big_drv.h"
53 #include "rtl_err.h"
54 #include "dll_drv.h"
55 #include "sql_base.h"
56 #include "sql_drv.h"
57 
58 #ifdef DB2_INCLUDE
59 
60 #ifdef DB2_DLL
61 #define CLI_DLL DB2_DLL
62 #endif
63 
64 #define ALLOW_EXECUTE_SUCCESS_WITH_INFO
65 #define ALLOW_FETCH_SUCCESS_WITH_INFO
66 
67 #include "sql_cli.c"
68 
69 typedef struct {
70     wstriType hostname;
71     memSizeType hostnameLength;
72     intType port;
73     wstriType database;
74     memSizeType databaseLength;
75     wstriType uid;
76     memSizeType uidLength;
77     wstriType pwd;
78     memSizeType pwdLength;
79     wstriType connectionString;
80     memSizeType connectionStringLength;
81   } connectDataRecord, *connectDataType;
82 
83 #define DEFAULT_PORT 50000
84 
85 
86 
createConnectionString(connectDataType connectData)87 static boolType createConnectionString (connectDataType connectData)
88 
89   {
90     const wcharType databaseKey[] = {'D', 'A', 'T', 'A', 'B', 'A', 'S', 'E', '=', '\0'};
91     const wcharType hostnameKey[] = {'H', 'O', 'S', 'T', 'N', 'A', 'M', 'E', '=', '\0'};
92     const wcharType localhost[] = {'l', 'o', 'c', 'a', 'l', 'h', 'o', 's', 't', '\0'};
93     const wcharType portKey[] = {'P', 'O', 'R', 'T', '=', '\0'};
94     const wcharType protocolKeyAndValue[] = {'P', 'R', 'O', 'T', 'O', 'C', 'O', 'L', '=', 'T', 'C', 'P', 'I', 'P', '\0'};
95     const wcharType uidKey[] = {'U', 'I', 'D', '=', '\0'};
96     const wcharType pwdKey[] = {'P', 'W', 'D', '=', '\0'};
97     const_wstriType hostname;
98     memSizeType hostnameLength;
99     intType port;
100     char portName[INTTYPE_DECIMAL_SIZE + NULL_TERMINATION_LEN];
101     memSizeType portNameLength;
102     memSizeType connectionStringLength;
103     wstriType connectionString;
104     memSizeType pos = 0;
105     boolType okay = FALSE;
106 
107   /* createConnectionString */
108     logFunction(printf("createConnectionString([hostname=\"");
109                 printWstri(connectData->hostname);
110                 printf("\", port=" FMT_D ", database=\"", connectData->port);
111                 printWstri(connectData->database);
112                 printf("\"])\n"););
113 
114     if (connectData->hostnameLength == 0) {
115       hostname = localhost;
116       hostnameLength = STRLEN(localhost);
117     } else {
118       hostname = connectData->hostname;
119       hostnameLength = connectData->hostnameLength;
120     } /* if */
121     if (connectData->port == 0) {
122       port = DEFAULT_PORT;
123     } else {
124       port = connectData->port;
125     } /* if */
126     portNameLength = (memSizeType) sprintf(portName, FMT_D, port);
127     connectData->connectionString = NULL;
128     connectData->connectionStringLength = 0;
129 
130     if (likely(connectData->databaseLength <= SHRT_MAX &&
131                connectData->hostnameLength <= SHRT_MAX &&
132                connectData->uidLength <= SHRT_MAX &&
133                connectData->pwdLength <= SHRT_MAX)) {
134       connectionStringLength = STRLEN(databaseKey) + connectData->databaseLength +
135                                1 + STRLEN(hostnameKey) + hostnameLength +
136                                1 + STRLEN(portKey) + portNameLength +
137                                1 + STRLEN(protocolKeyAndValue);
138       if (connectData->uidLength != 0) {
139         connectionStringLength += 1 + STRLEN(uidKey) + connectData->uidLength;
140       } /* if */
141       if (connectData->pwdLength != 0) {
142         connectionStringLength += 1 + STRLEN(pwdKey) + connectData->pwdLength;
143       } /* if */
144 
145       if (likely(connectionStringLength <= SHRT_MAX &&
146                  ALLOC_WSTRI(connectionString, connectionStringLength))) {
147         connectData->connectionString = connectionString;
148         connectData->connectionStringLength = connectionStringLength;
149 
150         memcpy(&connectionString[pos], databaseKey, STRLEN(databaseKey) * sizeof(SQLWCHAR));
151         pos += STRLEN(databaseKey);
152         memcpy(&connectionString[pos], connectData->database, connectData->databaseLength * sizeof(SQLWCHAR));
153         pos += connectData->databaseLength;
154 
155         connectionString[pos++] = ';';
156         memcpy(&connectionString[pos], hostnameKey, STRLEN(hostnameKey) * sizeof(SQLWCHAR));
157         pos += STRLEN(hostnameKey);
158         memcpy(&connectionString[pos], hostname, hostnameLength * sizeof(SQLWCHAR));
159         pos += hostnameLength;
160 
161         connectionString[pos++] = ';';
162         memcpy(&connectionString[pos], portKey, STRLEN(portKey) * sizeof(SQLWCHAR));
163         pos += STRLEN(portKey);
164         memcpy_to_wstri(&connectionString[pos], portName, portNameLength);
165         pos += portNameLength;
166 
167         connectionString[pos++] = ';';
168         memcpy(&connectionString[pos], protocolKeyAndValue, STRLEN(protocolKeyAndValue) * sizeof(SQLWCHAR));
169         pos += STRLEN(protocolKeyAndValue);
170 
171         if (connectData->uidLength != 0) {
172           connectionString[pos++] = ';';
173           memcpy(&connectionString[pos], uidKey, STRLEN(uidKey) * sizeof(SQLWCHAR));
174           pos += STRLEN(uidKey);
175           memcpy(&connectionString[pos], connectData->uid, connectData->uidLength * sizeof(SQLWCHAR));
176           pos += connectData->uidLength;
177         } /* if */
178 
179         if (connectData->pwdLength != 0) {
180           connectionString[pos++] = ';';
181           memcpy(&connectionString[pos], pwdKey, STRLEN(pwdKey) * sizeof(SQLWCHAR));
182           pos += STRLEN(pwdKey);
183           memcpy(&connectionString[pos], connectData->pwd, connectData->pwdLength * sizeof(SQLWCHAR));
184           pos += connectData->pwdLength;
185         } /* if */
186 
187         connectionString[pos] = '\0';
188         okay = TRUE;
189       } /* if */
190     } /* if */
191     logFunction(printf("createConnectionString --> %d (connectionString=\"", okay);
192                 printWstri(connectData->connectionString);
193                 printf("\")\n"););
194     return okay;
195   } /* createConnectionString */
196 
197 
198 
doOpenDb2(connectDataType connectData,errInfoType * err_info)199 static databaseType doOpenDb2 (connectDataType connectData, errInfoType *err_info)
200 
201   {
202     SQLHENV sql_environment;
203     SQLHDBC sql_connection;
204     SQLRETURN returnCode;
205     SQLSMALLINT outConnectionStringLength;
206     databaseType database;
207 
208   /* doOpenDb2 */
209     logFunction(printf("doOpenDb2(*, %d)\n", *err_info););
210     if (unlikely(connectData->databaseLength > SHRT_MAX ||
211                  connectData->uidLength > SHRT_MAX ||
212                  connectData->pwdLength > SHRT_MAX ||
213                  connectData->connectionStringLength > SHRT_MAX)) {
214       *err_info = MEMORY_ERROR;
215       database = NULL;
216     } else {
217       *err_info = prepareSqlConnection(&sql_environment, &sql_connection);
218       if (unlikely(*err_info != OKAY_NO_ERROR)) {
219         database = NULL;
220       } else {
221         if (connectData->hostnameLength == 0 && connectData->port == 0) {
222           returnCode = SQLConnectW(sql_connection,
223               (SQLWCHAR *) connectData->database, (SQLSMALLINT) connectData->databaseLength,
224               (SQLWCHAR *) connectData->uid, (SQLSMALLINT) connectData->uidLength,
225               (SQLWCHAR *) connectData->pwd, (SQLSMALLINT) connectData->pwdLength);
226           if ((returnCode != SQL_SUCCESS &&
227                returnCode != SQL_SUCCESS_WITH_INFO)) {
228             setDbErrorMsg("sqlOpenDb2", "SQLConnectW",
229                           SQL_HANDLE_DBC, sql_connection);
230             logError(printf("sqlOpenDb2: SQLConnectW:\n%s\n",
231                             dbError.message););
232           } /* if */
233         } else {
234           returnCode = SQL_ERROR;
235         } /* if */
236         if (returnCode != SQL_SUCCESS &&
237             returnCode != SQL_SUCCESS_WITH_INFO) {
238           returnCode = SQLDriverConnectW(sql_connection,
239                                          NULL, /* GetDesktopWindow(), */
240                                          (SQLWCHAR *) connectData->connectionString,
241                                          (SQLSMALLINT) connectData->connectionStringLength,
242                                          NULL, /* outConnectionString */
243                                          0,
244                                          &outConnectionStringLength,
245                                          SQL_DRIVER_NOPROMPT);
246         } /* if */
247         if (returnCode != SQL_SUCCESS &&
248             returnCode != SQL_SUCCESS_WITH_INFO) {
249           if (connectData->hostnameLength != 0 || connectData->port != 0) {
250             setDbErrorMsg("sqlOpenDb2", "SQLDriverConnect",
251                           SQL_HANDLE_DBC, sql_connection);
252             logError(printf("sqlOpenDb2: SQLDriverConnect:\n%s\n",
253                             dbError.message););
254           } /* if */
255           *err_info = DATABASE_ERROR;
256           SQLFreeHandle(SQL_HANDLE_DBC, sql_connection);
257           SQLFreeHandle(SQL_HANDLE_ENV, sql_environment);
258           database = NULL;
259         } else {
260           database = createDbRecord(sql_environment, sql_connection, DB_CATEGORY_DB2, err_info);
261         } /* if */
262       } /* if */
263     } /* if */
264     logFunction(printf("doOpenDb2 --> " FMT_U_MEM " (err_info=%d)\n",
265                        (memSizeType) database, *err_info););
266     return database;
267   } /* doOpenDb2 */
268 
269 
270 
sqlOpenDb2(const const_striType host,intType port,const const_striType dbName,const const_striType user,const const_striType password)271 databaseType sqlOpenDb2 (const const_striType host, intType port,
272     const const_striType dbName, const const_striType user,
273     const const_striType password)
274 
275   {
276     connectDataRecord connectData;
277     errInfoType err_info = OKAY_NO_ERROR;
278     databaseType database;
279 
280   /* sqlOpenDb2 */
281     logFunction(printf("sqlOpenDb2(\"%s\", ",
282                        striAsUnquotedCStri(host));
283                 printf(FMT_D ", \"%s\", ",
284                        port, striAsUnquotedCStri(dbName));
285                 printf("\"%s\", ", striAsUnquotedCStri(user));
286                 printf("\"%s\")\n", striAsUnquotedCStri(password)););
287     if (!findDll()) {
288       logError(printf("sqlOpenDb2: findDll() failed\n"););
289       err_info = DATABASE_ERROR;
290       database = NULL;
291     } else {
292       connectData.port = port;
293       connectData.hostname = stri_to_wstri_buf(host, &connectData.hostnameLength, &err_info);
294       if (unlikely(connectData.hostname == NULL)) {
295         database = NULL;
296       } else {
297         connectData.database = stri_to_wstri_buf(dbName, &connectData.databaseLength, &err_info);
298         if (unlikely(connectData.database == NULL)) {
299           database = NULL;
300         } else {
301           connectData.uid = stri_to_wstri_buf(user, &connectData.uidLength, &err_info);
302           if (unlikely(connectData.uid == NULL)) {
303             database = NULL;
304           } else {
305             connectData.pwd = stri_to_wstri_buf(password, &connectData.pwdLength, &err_info);
306             if (unlikely(connectData.pwd == NULL)) {
307               database = NULL;
308             } else {
309               if (unlikely(!createConnectionString(&connectData))) {
310                 err_info = MEMORY_ERROR;
311                 database = NULL;
312               } else {
313                 database = doOpenDb2(&connectData, &err_info);
314                 UNALLOC_WSTRI(connectData.connectionString, connectData.connectionStringLength);
315               } /* if */
316               UNALLOC_WSTRI(connectData.pwd, connectData.pwdLength);
317             } /* if */
318             UNALLOC_WSTRI(connectData.uid, connectData.uidLength);
319           } /* if */
320           UNALLOC_WSTRI(connectData.database, connectData.databaseLength);
321         } /* if */
322         UNALLOC_WSTRI(connectData.hostname, connectData.hostnameLength);
323       } /* if */
324     } /* if */
325     if (unlikely(err_info != OKAY_NO_ERROR)) {
326       raise_error(err_info);
327     } /* if */
328     logFunction(printf("sqlOpenDb2 --> " FMT_U_MEM "\n",
329                        (memSizeType) database););
330     return database;
331   } /* sqlOpenDb2 */
332 
333 #else
334 
335 
336 
sqlOpenDb2(const const_striType host,intType port,const const_striType dbName,const const_striType user,const const_striType password)337 databaseType sqlOpenDb2 (const const_striType host, intType port,
338     const const_striType dbName, const const_striType user,
339     const const_striType password)
340 
341   { /* sqlOpenDb2 */
342     logError(printf("sqlOpenDb2(\"%s\", ",
343                     striAsUnquotedCStri(host));
344              printf(FMT_D ", \"%s\", ",
345                     port, striAsUnquotedCStri(dbName));
346              printf("\"%s\", ", striAsUnquotedCStri(user));
347              printf("\"%s\"): Db2 driver not present.\n",
348                     striAsUnquotedCStri(password)););
349     raise_error(RANGE_ERROR);
350     return NULL;
351   } /* sqlOpenDb2 */
352 
353 #endif
354