FreeTDS API: How to add a new type

FreeTDS API

How to add a new type

# Introduction

Adding a new type in FreeTDS is a quite complicated task involving different tasks.

To see an example you can look at commit id adb893f1381fd3ea40564c775e30dc8cdc81dcf2 ("Implement big(date)time types") and parent changes in the source repository.

# libTDS changes

- protocol. First thing to do is add the type to the protocol. A type usually have some mnemonic constant and a structure. Declare them in `include/freetds/proto.h` file. Note that here you should declare the structure the server use not the structure to hold the data in libTDS.
  Cfr commit id a74a06e1f97f3137f6cf1bc7319dd7a2cfb52b1f.

- base information. Add the type to `misc/types.csv` file (I use LibreOffice Calc to do it). This table maintain the base information for a type.
  Cfr commit id 680cb3371e042bb372cbc5e6feb4054e50d40c1a.

- data. There should be some code to handle this type to/from the server. This code is implemented in `include/freetds/data.h` and `src/tds/data.c`. You can either add a new set of functions to handle this new type or add the type handling do another set of types depending on how complicated is that type. One thing you have to to at this step is determine how you store that type in libTDS. This is quite important at upper level libraries will have to use these structures or even present these data to client code (like DB-Library usually do). Due to the way FreeTDS works now you would get a linker error in the ODBC part. You can either ignore the error and proceed with libTDS, add the code to ODBC or disable temporarily ODBC.
  Cfr commit id 680cb3371e042bb372cbc5e6feb4054e50d40c1a.

- enable the type from server. In order to receive the new type from the server you have to tell the server that we support that type. This can be either done changing the protocol (usually Microsoft) or enabling some flags (capabilities for Sybase).
  Cfr commit id a498703ff9e309c656b19dd990f4cad0283a47c7.

- conversions. Conversions are not hard to write but usually require quite a bit of coding. After extending CONV_RESULT type in `include/freetds/convert.h` and adding the type to the script that generate the conversion tables in `src/tds/tds_willconvert.pl` you have to write the big part in `src/tds/covnert.c`. You have to implement all kind of conversions you declared in the previous file. Reuse the functions that are there (for instance there are some parser functions). Also if there are similar types it could be helpful to convert first your type to a super type then use the conversion for that type. For instance for SMALLINT type (`tds_convert_int2`) the type is just readed and then `tds_convert_int` is called which handle any int (actually 32 bit integer). Same for data where the `TDS_DATETIMEALL` structure is used. Note that conversions to binary (which usually are implemented) are done in another function (`tds_convert_to_binary`).
  Cfr commit id 9ed52cb78f725607ac109c8c284ca7c4658d87a9.

- string definition. Add string for your type to `src/tds/token.c` in `tds_prtype`.
  Cfr commit id ac0d3b46db7d98436cd76f906b7d455f7651faae.

- conversion tests. You probably will have done some mistake with conversions but don't mind, there are some tests which will help sorting this out. `src/tds/unittests/convert.c` try any possible combination of conversion to check if all conversion are implemented (it does not check the conversions themself). `src/tds/unittests/t0007.c` test that your conversion are working. Just add manually the conversions you want to try.
  Cfr commit id abcc09c9a88acd0e9a45b46dab3ca44309917a02.

- parameter. Add type/parameter declaration in `tds_get_column_declaration` in `src/tds/query.c`. Also do any necessary step to initialize the parameter to send to server.
  Cfr commit id 54fdd3233e430c045cf5524ac385770738d9e92c, commit id 88cfea19d91245372779b8893a2d62b42696cd49.

- emulated prepared/rpc. If needed handle your type in `tds_put_param_as_string` in `src/tds/query.c`.
  Cfr commit id 017b7bf2fee0f09847e64546d27382d2f2b756f4.

# ODBC changes

ODBC is the most complicated library to add a type to. Usually its data are different from libTDS so you have to add additional code for conversions which are not required by other libraries.

- type information. Every type in ODBC have related information. These information are set in `src/odbc/odbc_data.c`. Depending on the changes you did for data in libTDS you should handle the new type.
  Cfr commit id 71e189e206dc9b6f6513e0aa0e4133a4f8dec110.

- type information test. Related to the previous change there is `src/odbc/unittests/describecol.c` test. Add a test case for new type. You should attempt to run same test also on proprietary library if possible.
  Cfr commit id 8a8ec16a6a514a5d6ac66c2470eff51f6a8d4a53.

- conversions from odbc. Define how the ODBC type should convert to the server and implement the conversion.
  Cfr commit id 29606cbf413c44e49ddfcfb8a93b8a6bd2565a84, commit id 87c84e20a594472a72990b12d4a1451b22e6714b.

- conversions to binary. Binary representation in ODBC are usually different from server ones. If so implement the proper conversions.
  Cfr commit id 56009f35d3e0def339a0c5cb98d006e5e710d523.

- conversions to characters. Same problem for character types.
  Cfr commit id 25ff091880dabc32f28a73f09bf31c01314aca2f.

- conversion test. You probably want to test ODBC conversions. This can be done changing `src/odbc/unittests/data.c` test and `src/odbc/unittests/genparams.c`.
  Cfr commit id e69f7d564dac44884f7c5f0106cceafce4af168b.

# CT-Library changes

This is quite easy as usual the conversion in libTDS are fine for this library.

- define type in `include/cspublic.h`
- implement conversion in `src/ctlib/cs.h`
- set corrent conversion from cs types to server in `src/ctlib/ct.c`

Cfr commit id c5e71e5ad4a557038ecedcec457e2531ab02a77b.

# DB-Library changes

A bit more complicated than CT-Library but not that much.

- add type and binding type to `include/sybdb.h`
- add NULL handling in `dbgetnull`, `dbsetnull` and `default_null_representation` in `src/dblib/dblib.c`
- add binding to dbbindtype
- add support for conversion from/to server
- add printable size
- return correct type string

Cfr commit id 99dd126e0eb248dd3079b2a7cf97437fe3bcd163.

# Applications changes

datacopy application requires some changes too to support new types so add them to `src/apps/datacopy.c`.
Cfr commit id e59c48ac39c76abb036651f8ec238090eef321c9.