Line data Source code
1 : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2 : * Copyright (C) 2014 Frediano Ziglio
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 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, write to the
16 : * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 : * Boston, MA 02111-1307, USA.
18 : */
19 :
20 : #include <config.h>
21 :
22 : #include <stdarg.h>
23 : #include <stdio.h>
24 :
25 : #if HAVE_STDLIB_H
26 : #include <stdlib.h>
27 : #endif /* HAVE_STDLIB_H */
28 :
29 : #if HAVE_STRING_H
30 : #include <string.h>
31 : #endif /* HAVE_STRING_H */
32 :
33 : #include <assert.h>
34 : #include <ctype.h>
35 :
36 : #include <freetds/odbc.h>
37 : #include <freetds/tds/iconv.h>
38 : #include <freetds/tds/convert.h>
39 : #include <odbcss.h>
40 :
41 : #define SET_INFO(type, prefix, suffix) do { \
42 : drec->sql_desc_literal_prefix = prefix; \
43 : drec->sql_desc_literal_suffix = suffix; \
44 : drec->sql_desc_type_name = type; \
45 : return; \
46 : } while(0)
47 : #define SET_INFO2(type, prefix, suffix, len) do { \
48 : drec->sql_desc_length = (len); \
49 : SET_INFO(type, prefix, suffix); \
50 : } while(0)
51 :
52 : /* For date/time types - ODBC specification says that SQL_DESC_LENGTH
53 : * should be the character string representation size.
54 : * https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlsetdescfield-function
55 : */
56 : #define SET_INFO_DT(type, prefix, suffix) SET_INFO2(type, prefix, suffix, drec->sql_desc_display_size)
57 :
58 : static int
59 18107 : odbc_datetime_display_size(const TDSCOLUMN *col, const TDS_DBC *dbc)
60 : {
61 : /* This logic should match that of odbc_tds2sql() code block which
62 : * selects the format string and precision.
63 : *
64 : * "precision" here refers to the number of digits to be generated by the
65 : * TDS-specific %z format specifier, which represents fractions of a second.
66 : *
67 : * This value SHOULD be set in col->column_prec. It is so, for MS types.
68 : * However, the non-MS types sometimes do not actually set column_prec,
69 : * so odbc_tds2sql() hardcodes precisions for those types.
70 : */
71 :
72 : int prec;
73 : const char *fmt;
74 18107 : int extra = 0;
75 :
76 18107 : switch (tds_get_conversion_type(col->on_server.column_type, col->on_server.column_size)) {
77 26 : case SYBMSDATETIMEOFFSET:
78 : /* Offset formatting hardcodes a " +HH:MM" suffix */
79 26 : extra = 7;
80 26 : prec = col->column_prec;
81 : goto datetime;
82 46 : case SYBMSDATETIME2:
83 46 : prec = col->column_prec;
84 : goto datetime;
85 : case SYB5BIGDATETIME:
86 : prec = 6;
87 : goto datetime;
88 1264 : case SYBDATETIME:
89 1264 : prec = 3;
90 : goto datetime;
91 130 : case SYBDATETIME4:
92 130 : prec = 0;
93 1476 : datetime:
94 2952 : fmt = tds_dstr_cstr(&dbc->datetime_fmt);
95 : break;
96 82 : case SYBMSTIME:
97 82 : prec = col->column_prec;
98 : goto time;
99 : case SYB5BIGTIME:
100 : prec = 6;
101 : goto time;
102 36 : case SYBTIME:
103 36 : prec = 3;
104 128 : time:
105 256 : fmt = tds_dstr_cstr(&dbc->time_fmt);
106 : break;
107 126 : case SYBMSDATE:
108 : case SYBDATE:
109 126 : prec = 0;
110 252 : fmt = tds_dstr_cstr(&dbc->date_fmt);
111 : break;
112 : default:
113 : return 0;
114 : }
115 :
116 1730 : return tds_strftime_maxsize(fmt, prec) + extra;
117 : }
118 :
119 : static void
120 240 : data_msdatetime_set_type_info(TDSCOLUMN *col, struct _drecord *drec, const TDS_DBC *dbc)
121 : {
122 240 : drec->sql_desc_display_size = odbc_datetime_display_size(col, dbc);
123 :
124 240 : switch (col->on_server.column_type) {
125 82 : case SYBMSTIME:
126 82 : drec->sql_desc_octet_length = sizeof(SQL_SS_TIME2_STRUCT);
127 82 : drec->sql_desc_concise_type = SQL_SS_TIME2;
128 82 : SET_INFO_DT("time", "'", "'");
129 86 : case SYBMSDATE:
130 86 : drec->sql_desc_octet_length = sizeof(DATE_STRUCT);
131 86 : drec->sql_desc_concise_type = SQL_TYPE_DATE;
132 86 : SET_INFO_DT("date", "'", "'");
133 26 : case SYBMSDATETIMEOFFSET:
134 26 : drec->sql_desc_octet_length = sizeof(SQL_SS_TIMESTAMPOFFSET_STRUCT);
135 26 : drec->sql_desc_concise_type = SQL_SS_TIMESTAMPOFFSET;
136 26 : SET_INFO_DT("datetimeoffset", "'", "'");
137 46 : case SYBMSDATETIME2:
138 46 : drec->sql_desc_octet_length = sizeof(TIMESTAMP_STRUCT);
139 46 : drec->sql_desc_concise_type = SQL_TYPE_TIMESTAMP;
140 46 : drec->sql_desc_datetime_interval_code = SQL_CODE_TIMESTAMP;
141 46 : SET_INFO_DT("datetime2", "'", "'");
142 : default:
143 : break;
144 : }
145 : }
146 :
147 : static void
148 606 : data_variant_set_type_info(TDSCOLUMN *col TDS_UNUSED, struct _drecord *drec, const TDS_DBC *dbc TDS_UNUSED)
149 : {
150 606 : drec->sql_desc_concise_type = SQL_SS_VARIANT;
151 606 : drec->sql_desc_display_size = 8000;
152 606 : drec->sql_desc_octet_length = 0;
153 606 : SET_INFO2("sql_variant", "", "", 8000);
154 : }
155 :
156 : static void
157 836 : data_numeric_set_type_info(TDSCOLUMN *col, struct _drecord *drec, const TDS_DBC *dbc TDS_UNUSED)
158 : {
159 836 : const char *type_name = col->on_server.column_type == SYBNUMERIC ? "numeric" : "decimal";
160 :
161 836 : drec->sql_desc_concise_type = SQL_NUMERIC;
162 836 : drec->sql_desc_octet_length = col->column_prec + 2;
163 836 : drec->sql_desc_display_size = col->column_prec + 2;
164 836 : drec->sql_desc_num_prec_radix = 10;
165 836 : SET_INFO2(type_name, "", "", col->column_prec);
166 : }
167 :
168 : static void
169 8 : data_clrudt_set_type_info(TDSCOLUMN *col, struct _drecord *drec, const TDS_DBC *dbc TDS_UNUSED)
170 : {
171 8 : drec->sql_desc_concise_type = SQL_LONGVARBINARY;
172 : /* TODO ??? */
173 8 : drec->sql_desc_display_size = col->column_size * 2;
174 8 : }
175 :
176 : static void
177 20 : data_sybbigtime_set_type_info(TDSCOLUMN *col, struct _drecord *drec, const TDS_DBC *dbc TDS_UNUSED)
178 : {
179 20 : drec->sql_desc_display_size = odbc_datetime_display_size(col, dbc);
180 :
181 20 : if (col->on_server.column_type == SYB5BIGTIME) {
182 10 : drec->sql_desc_concise_type = SQL_SS_TIME2;
183 10 : drec->sql_desc_octet_length = sizeof(SQL_SS_TIME2_STRUCT);
184 10 : drec->sql_desc_precision = 6;
185 10 : drec->sql_desc_scale = 6;
186 10 : drec->sql_desc_datetime_interval_code = SQL_CODE_TIMESTAMP;
187 10 : SET_INFO_DT("bigtime", "'", "'");
188 : }
189 :
190 10 : assert(col->on_server.column_type == SYB5BIGDATETIME);
191 :
192 10 : drec->sql_desc_concise_type = SQL_TYPE_TIMESTAMP;
193 10 : drec->sql_desc_octet_length = sizeof(TIMESTAMP_STRUCT);
194 10 : drec->sql_desc_precision = 6;
195 10 : drec->sql_desc_scale = 6;
196 10 : drec->sql_desc_datetime_interval_code = SQL_CODE_TIMESTAMP;
197 10 : SET_INFO_DT("bigdatetime", "'", "'");
198 : }
199 :
200 : static void
201 0 : data_mstabletype_set_type_info(TDSCOLUMN *col TDS_UNUSED, struct _drecord *drec, const TDS_DBC *dbc TDS_UNUSED)
202 : {
203 0 : drec->sql_desc_concise_type = SQL_SS_TABLE;
204 0 : drec->sql_desc_octet_length = 0;
205 0 : }
206 :
207 : static void
208 17847 : data_generic_set_type_info(TDSCOLUMN *col, struct _drecord *drec, const TDS_DBC *dbc)
209 : {
210 17847 : TDS_SERVER_TYPE col_type = col->on_server.column_type;
211 17847 : int col_size = col->on_server.column_size;
212 17847 : const int datetime_display_size = odbc_datetime_display_size(col, dbc);
213 :
214 17847 : drec->sql_desc_display_size = 26;
215 :
216 17847 : switch (tds_get_conversion_type(col_type, col_size)) {
217 40 : case XSYBNCHAR:
218 40 : drec->sql_desc_concise_type = SQL_WCHAR;
219 40 : drec->sql_desc_display_size = col->on_server.column_size / 2;
220 40 : SET_INFO2("nchar", "'", "'", col->on_server.column_size / 2);
221 :
222 156 : case XSYBCHAR:
223 : case SYBCHAR:
224 156 : drec->sql_desc_concise_type = SQL_CHAR;
225 156 : drec->sql_desc_display_size = col->on_server.column_size;
226 156 : SET_INFO("char", "'", "'");
227 :
228 : /* TODO really sure ?? SYBNVARCHAR sybase only ?? */
229 2383 : case SYBNVARCHAR:
230 : case XSYBNVARCHAR:
231 2383 : drec->sql_desc_concise_type = SQL_WVARCHAR;
232 2383 : drec->sql_desc_display_size = col->on_server.column_size / 2;
233 2383 : drec->sql_desc_length = col->on_server.column_size / 2u;
234 2383 : if (col->char_conv) {
235 2381 : unsigned char bytes_per_char = TDS_CLAMP(col->char_conv->to.charset.max_bytes_per_char, 2, 3);
236 2381 : drec->sql_desc_octet_length = (col->on_server.column_size / 2u) * bytes_per_char;
237 : }
238 2383 : if (is_blob_col(col)) {
239 8 : drec->sql_desc_display_size = SQL_SS_LENGTH_UNLIMITED;
240 8 : drec->sql_desc_octet_length = drec->sql_desc_length =
241 : SQL_SS_LENGTH_UNLIMITED;
242 : }
243 2383 : SET_INFO("nvarchar", "'", "'");
244 :
245 3824 : case XSYBVARCHAR:
246 : case SYBVARCHAR:
247 3824 : drec->sql_desc_concise_type = SQL_VARCHAR;
248 3824 : drec->sql_desc_display_size = col->on_server.column_size;
249 3824 : if (is_blob_col(col)) {
250 36 : drec->sql_desc_display_size = SQL_SS_LENGTH_UNLIMITED;
251 36 : drec->sql_desc_octet_length = drec->sql_desc_length =
252 : SQL_SS_LENGTH_UNLIMITED;
253 : }
254 3824 : SET_INFO("varchar", "'", "'");
255 :
256 574 : case SYBNTEXT:
257 574 : drec->sql_desc_concise_type = SQL_WLONGVARCHAR;
258 574 : drec->sql_desc_display_size = col->on_server.column_size / 2;
259 574 : SET_INFO2("ntext", "'", "'", col->on_server.column_size / 2);
260 :
261 796 : case SYBTEXT:
262 796 : drec->sql_desc_concise_type = SQL_LONGVARCHAR;
263 796 : drec->sql_desc_display_size = col->on_server.column_size;
264 796 : SET_INFO("text", "'", "'");
265 :
266 370 : case SYBBIT:
267 : case SYBBITN:
268 370 : drec->sql_desc_concise_type = SQL_BIT;
269 370 : drec->sql_desc_display_size = 1;
270 370 : drec->sql_desc_unsigned = SQL_TRUE;
271 370 : SET_INFO2("bit", "", "", 1);
272 :
273 : #if (ODBCVER >= 0x0300)
274 214 : case SYB5INT8:
275 : case SYBINT8:
276 : /* TODO return numeric for odbc2 and convert bigint to numeric */
277 214 : drec->sql_desc_concise_type = SQL_BIGINT;
278 214 : drec->sql_desc_display_size = 20;
279 214 : SET_INFO2("bigint", "", "", 19);
280 : #endif
281 :
282 2455 : case SYBINT4:
283 2455 : drec->sql_desc_concise_type = SQL_INTEGER;
284 2455 : drec->sql_desc_display_size = 11; /* -1000000000 */
285 2455 : SET_INFO2("int", "", "", 10);
286 :
287 2541 : case SYBINT2:
288 2541 : drec->sql_desc_concise_type = SQL_SMALLINT;
289 2541 : drec->sql_desc_display_size = 6; /* -10000 */
290 2541 : SET_INFO2("smallint", "", "", 5);
291 :
292 314 : case SYBUINT1:
293 : case SYBINT1:
294 314 : drec->sql_desc_unsigned = SQL_TRUE;
295 314 : case SYBSINT1: /* TODO not another type_name ?? */
296 314 : drec->sql_desc_concise_type = SQL_TINYINT;
297 314 : drec->sql_desc_display_size = 3; /* 255 */
298 314 : SET_INFO2("tinyint", "", "", 3);
299 :
300 : #if (ODBCVER >= 0x0300)
301 36 : case SYBUINT8:
302 36 : drec->sql_desc_unsigned = SQL_TRUE;
303 36 : drec->sql_desc_concise_type = SQL_BIGINT;
304 36 : drec->sql_desc_display_size = 20;
305 : /* TODO return numeric for odbc2 and convert bigint to numeric */
306 36 : SET_INFO2("unsigned bigint", "", "", 20);
307 : #endif
308 :
309 36 : case SYBUINT4:
310 36 : drec->sql_desc_unsigned = SQL_TRUE;
311 36 : drec->sql_desc_concise_type = SQL_INTEGER;
312 36 : drec->sql_desc_display_size = 10;
313 36 : SET_INFO2("unsigned int", "", "", 10);
314 :
315 38 : case SYBUINT2:
316 38 : drec->sql_desc_unsigned = SQL_TRUE;
317 38 : drec->sql_desc_concise_type = SQL_SMALLINT;
318 38 : drec->sql_desc_display_size = 5; /* 65535 */
319 38 : SET_INFO2("unsigned smallint", "", "", 5);
320 :
321 316 : case SYBREAL:
322 316 : drec->sql_desc_concise_type = SQL_REAL;
323 316 : drec->sql_desc_display_size = 14;
324 316 : SET_INFO2("real", "", "", dbc->env->attr.odbc_version == SQL_OV_ODBC3 ? 24 : 7);
325 :
326 812 : case SYBFLT8:
327 812 : drec->sql_desc_concise_type = SQL_DOUBLE;
328 812 : drec->sql_desc_display_size = 24; /* FIXME -- what should the correct size be? */
329 812 : SET_INFO2("float", "", "", dbc->env->attr.odbc_version == SQL_OV_ODBC3 ? 53 : 15);
330 :
331 356 : case SYBMONEY:
332 : /* TODO check money format returned by proprietary ODBC, scale == 4 but we use 2 digits */
333 356 : drec->sql_desc_concise_type = SQL_DECIMAL;
334 356 : drec->sql_desc_octet_length = 21;
335 356 : drec->sql_desc_display_size = 21;
336 356 : drec->sql_desc_precision = 19;
337 356 : drec->sql_desc_scale = 4;
338 356 : SET_INFO2("money", "$", "", 19);
339 :
340 100 : case SYBMONEY4:
341 100 : drec->sql_desc_concise_type = SQL_DECIMAL;
342 100 : drec->sql_desc_octet_length = 12;
343 100 : drec->sql_desc_display_size = 12;
344 100 : drec->sql_desc_precision = 10;
345 100 : drec->sql_desc_scale = 4;
346 100 : SET_INFO2("money", "$", "", 10);
347 :
348 1264 : case SYBDATETIME:
349 1264 : drec->sql_desc_concise_type = SQL_TYPE_TIMESTAMP;
350 1264 : drec->sql_desc_display_size = datetime_display_size;
351 1264 : drec->sql_desc_octet_length = sizeof(TIMESTAMP_STRUCT);
352 1264 : drec->sql_desc_precision = 3;
353 1264 : drec->sql_desc_scale = 3;
354 1264 : drec->sql_desc_datetime_interval_code = SQL_CODE_TIMESTAMP;
355 1264 : SET_INFO_DT("datetime", "'", "'");
356 :
357 130 : case SYBDATETIME4:
358 : /* MS ODBC driver gives 19 for display_size and 16 for length,
359 : * the default display string has length 19.
360 : * This appears to be a MS bug that we don't need to replicate,
361 : * the two length values should match. */
362 130 : drec->sql_desc_concise_type = SQL_TYPE_TIMESTAMP;
363 130 : drec->sql_desc_display_size = datetime_display_size;
364 130 : drec->sql_desc_octet_length = sizeof(TIMESTAMP_STRUCT);
365 130 : drec->sql_desc_datetime_interval_code = SQL_CODE_TIMESTAMP;
366 130 : SET_INFO_DT("datetime", "'", "'");
367 :
368 : /* The following two types are just Sybase types but as mainly our ODBC
369 : * driver is much more compatible with Windows use attributes similar
370 : * to MS one. For instance Sybase ODBC returns TIME into a TIME_STRUCT
371 : * however this truncate the precision to 0 as TIME does not have
372 : * fraction of seconds. Also Sybase ODBC have different concepts for
373 : * PRECISION for many types and making these 2 types compatibles with
374 : * Sybase would break this driver compatibility.
375 : */
376 36 : case SYBTIME:
377 36 : drec->sql_desc_concise_type = SQL_SS_TIME2;
378 36 : drec->sql_desc_octet_length = sizeof(SQL_SS_TIME2_STRUCT);
379 36 : drec->sql_desc_display_size = datetime_display_size;
380 36 : drec->sql_desc_precision = 3;
381 36 : drec->sql_desc_scale = 3;
382 36 : SET_INFO_DT("time", "'", "'");
383 :
384 40 : case SYBDATE:
385 40 : drec->sql_desc_octet_length = sizeof(DATE_STRUCT);
386 40 : drec->sql_desc_concise_type = SQL_TYPE_DATE;
387 : /* we always format using yyyy-mm-dd, see convert_tds2sql.c */
388 40 : drec->sql_desc_display_size = datetime_display_size;
389 40 : SET_INFO_DT("date", "'", "'");
390 :
391 58 : case XSYBBINARY:
392 : case SYBBINARY:
393 58 : drec->sql_desc_concise_type = SQL_BINARY;
394 58 : drec->sql_desc_display_size = col->column_size * 2;
395 : /* handle TIMESTAMP using usertype */
396 58 : if (col->column_usertype == 80)
397 8 : SET_INFO("timestamp", "0x", "");
398 50 : SET_INFO("binary", "0x", "");
399 :
400 76 : case SYBLONGBINARY:
401 76 : if (col->column_usertype == USER_UNICHAR_TYPE) {
402 4 : drec->sql_desc_concise_type = SQL_WCHAR;
403 4 : drec->sql_desc_display_size = col->on_server.column_size / 2;
404 4 : SET_INFO2("unichar", "'", "'", col->on_server.column_size / 2);
405 : }
406 72 : if (col->column_usertype == USER_UNIVARCHAR_TYPE) {
407 62 : drec->sql_desc_concise_type = SQL_WVARCHAR;
408 62 : drec->sql_desc_display_size = col->on_server.column_size / 2;
409 62 : SET_INFO2("univarchar", "'", "'", col->on_server.column_size / 2);
410 : }
411 : case SYBIMAGE:
412 374 : drec->sql_desc_concise_type = SQL_LONGVARBINARY;
413 374 : drec->sql_desc_display_size = col->column_size * 2;
414 374 : SET_INFO("image", "0x", "");
415 :
416 432 : case XSYBVARBINARY:
417 : case SYBVARBINARY:
418 432 : drec->sql_desc_concise_type = SQL_VARBINARY;
419 432 : drec->sql_desc_display_size = col->column_size * 2;
420 432 : if (is_blob_col(col)) {
421 40 : drec->sql_desc_display_size = SQL_SS_LENGTH_UNLIMITED;
422 40 : drec->sql_desc_octet_length = drec->sql_desc_length =
423 : SQL_SS_LENGTH_UNLIMITED;
424 : }
425 432 : SET_INFO("varbinary", "0x", "");
426 :
427 : case SYBINTN:
428 : case SYBDATETIMN:
429 : case SYBFLTN:
430 : case SYBMONEYN:
431 : case SYBUINTN:
432 : case SYBTIMEN:
433 : case SYBDATEN:
434 0 : assert(0);
435 :
436 : case SYBVOID:
437 : case SYBINTERVAL:
438 : case SYBUNITEXT:
439 : case SYBXML:
440 : case SYBMSUDT:
441 : break;
442 :
443 : #if (ODBCVER >= 0x0300)
444 74 : case SYBUNIQUE:
445 : #ifdef SQL_GUID
446 74 : drec->sql_desc_concise_type = SQL_GUID;
447 : #else
448 : drec->sql_desc_concise_type = SQL_CHAR;
449 : #endif
450 74 : drec->sql_desc_display_size = 36;
451 : /* FIXME for Sybase ?? */
452 74 : SET_INFO2("uniqueidentifier", "'", "'", 36);
453 : #endif
454 :
455 12 : case SYBMSXML:
456 12 : drec->sql_desc_concise_type = SQL_SS_XML;
457 12 : drec->sql_desc_display_size = SQL_SS_LENGTH_UNLIMITED;
458 12 : drec->sql_desc_octet_length = drec->sql_desc_length =
459 : SQL_SS_LENGTH_UNLIMITED;
460 12 : SET_INFO("xml", "'", "'");
461 : /* types already handled in other types, just to silent warnings */
462 : case SYBNUMERIC:
463 : case SYBDECIMAL:
464 : case SYBVARIANT:
465 : case SYBMSDATE:
466 : case SYBMSTIME:
467 : case SYBMSDATETIME2:
468 : case SYBMSDATETIMEOFFSET:
469 : case SYB5BIGDATETIME:
470 : case SYB5BIGTIME:
471 : case SYBMSTABLE:
472 : break;
473 : }
474 0 : SET_INFO("", "", "");
475 : }
476 :
477 : static void
478 0 : data_invalid_set_type_info(TDSCOLUMN *col TDS_UNUSED, struct _drecord *drec TDS_UNUSED, const TDS_DBC *dbc TDS_UNUSED)
479 : {
480 0 : }
481 :
482 : void
483 19557 : odbc_set_sql_type_info(TDSCOLUMN *col, struct _drecord *drec, const TDS_DBC *dbc)
484 : {
485 19557 : drec->sql_desc_precision = col->column_prec;
486 19557 : drec->sql_desc_scale = col->column_scale;
487 19557 : drec->sql_desc_unsigned = SQL_FALSE;
488 19557 : drec->sql_desc_octet_length = drec->sql_desc_length = col->on_server.column_size;
489 19557 : drec->sql_desc_num_prec_radix = 0;
490 19557 : drec->sql_desc_datetime_interval_code = 0;
491 :
492 19557 : ((TDS_FUNCS *) col->funcs)->set_type_info(col, drec, dbc);
493 :
494 19557 : drec->sql_desc_type = drec->sql_desc_concise_type;
495 19557 : if (drec->sql_desc_concise_type == SQL_TYPE_TIMESTAMP)
496 1450 : drec->sql_desc_type = SQL_DATETIME;
497 19557 : }
498 :
499 : # define TDS_DEFINE_FUNCS(name) \
500 : const TDS_FUNCS tds_ ## name ## _funcs = { \
501 : TDS_COMMON_FUNCS(name), \
502 : data_ ## name ## _set_type_info, \
503 : }
504 : TDS_DEFINE_FUNCS(invalid);
505 : TDS_DEFINE_FUNCS(generic);
506 : TDS_DEFINE_FUNCS(numeric);
507 : TDS_DEFINE_FUNCS(variant);
508 : TDS_DEFINE_FUNCS(msdatetime);
509 : TDS_DEFINE_FUNCS(clrudt);
510 : TDS_DEFINE_FUNCS(sybbigtime);
511 : TDS_DEFINE_FUNCS(mstabletype);
|