From 87bd1e8987ba216cb66f680094939cae333f2077 Mon Sep 17 00:00:00 2001
From: Ingela Anderton Andin
Date: Thu, 1 Apr 2010 09:29:21 +0000
Subject: OTP-8511 Timestamps in ODBC
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Now supports SQL_TYPE_TIMESTAMP on the format {{YY, MM, DD}, {HH, MM, SS}}.
Thanks to Juhani Ränkimies.
---
lib/odbc/AUTHORS | 3 +-
lib/odbc/c_src/odbcserver.c | 142 +++++++++++++++++++++++++----------
lib/odbc/c_src/odbcserver.h | 1 +
lib/odbc/doc/src/databases.xml | 8 +-
lib/odbc/doc/src/getting_started.xml | 16 ++--
lib/odbc/doc/src/notes.xml | 58 ++++++++------
lib/odbc/doc/src/odbc.xml | 2 +-
lib/odbc/src/odbc.erl | 10 +++
lib/odbc/src/odbc_internal.hrl | 1 +
lib/odbc/vsn.mk | 2 +-
10 files changed, 163 insertions(+), 80 deletions(-)
diff --git a/lib/odbc/AUTHORS b/lib/odbc/AUTHORS
index 22813f36ce..38f72244ef 100644
--- a/lib/odbc/AUTHORS
+++ b/lib/odbc/AUTHORS
@@ -6,4 +6,5 @@ Original Authors:
Contributors:
Scott Lystig Fritchie - input/output variables for stored procedures
willi@talk24.com - Some 64 bits adjustments
-Juhani Ränkimies - SQL_WCHAR and SQL_WVARCHAR support
\ No newline at end of file
+Juhani Ränkimies - SQL_WCHAR and SQL_WVARCHAR support
+Juhani Ränkimies - TIMESTAMP support
\ No newline at end of file
diff --git a/lib/odbc/c_src/odbcserver.c b/lib/odbc/c_src/odbcserver.c
index 5865cddcda..c9627e9d05 100644
--- a/lib/odbc/c_src/odbcserver.c
+++ b/lib/odbc/c_src/odbcserver.c
@@ -89,7 +89,8 @@
InOrOut = [ERL_ODBC_IN | ERL_ODBC_OUT | ERL_ODBC_INOUT]
Datatype - USER_INT | USER_SMALL_INT | {USER_DECIMAL, Precision, Scale} |
{USER_NMERIC, Precision, Scale} | {USER_CHAR, Max} | {USER_VARCHAR, Max} |
- {USER_WVARCHAR, Max} | {USER_FLOAT, Precision} | USER_REAL | USER_DOUBLE
+ {USER_WVARCHAR, Max} | {USER_FLOAT, Precision} | USER_REAL | USER_DOUBLE |
+ USER_TIMESTAMP
Scale - integer
Precision - integer
Max - integer
@@ -379,7 +380,7 @@ DWORD WINAPI database_handler(const char *port)
shutdown(socket, 2);
close_socket(socket);
clean_socket_lib();
- DO_EXIT(EXIT_SUCCESS);
+ /* Exit will be done by suervisor thread */
}
/* Description: Calls the appropriate function to handle the database
@@ -1080,6 +1081,7 @@ static db_result_msg encode_out_params(db_state *state,
int j = 0;
param_array column;
db_result_msg msg;
+ TIMESTAMP_STRUCT* ts;
msg = encode_empty_message();
ei_x_encode_tuple_header(&dynamic_buffer(state), 3);
@@ -1109,6 +1111,18 @@ static db_result_msg encode_out_params(db_state *state,
} else {
void* values = retrive_param_values(&column);
switch(column.type.c) {
+ case SQL_C_TYPE_TIMESTAMP:
+ ts = (TIMESTAMP_STRUCT*) values;
+ ei_x_encode_tuple_header(&dynamic_buffer(state), 2);
+ ei_x_encode_tuple_header(&dynamic_buffer(state), 3);
+ ei_x_encode_long(&dynamic_buffer(state), (long)(ts->year));
+ ei_x_encode_long(&dynamic_buffer(state), (long)(ts->month));
+ ei_x_encode_long(&dynamic_buffer(state), (long)(ts->day));
+ ei_x_encode_tuple_header(&dynamic_buffer(state), 3);
+ ei_x_encode_long(&dynamic_buffer(state), (long)(ts->hour));
+ ei_x_encode_long(&dynamic_buffer(state), (long)(ts->minute));
+ ei_x_encode_long(&dynamic_buffer(state), (long)(ts->second));
+ break;
case SQL_C_CHAR:
if binary_strings(state) {
ei_x_encode_binary(&dynamic_buffer(state),
@@ -1380,11 +1394,24 @@ static db_result_msg encode_row_count(SQLINTEGER num_of_rows,
static void encode_column_dyn(db_column column, int column_nr,
db_state *state)
{
+ TIMESTAMP_STRUCT* ts;
if (column.type.len == 0 ||
column.type.strlen_or_indptr == SQL_NULL_DATA) {
ei_x_encode_atom(&dynamic_buffer(state), "null");
} else {
switch(column.type.c) {
+ case SQL_C_TYPE_TIMESTAMP:
+ ts = (TIMESTAMP_STRUCT*)column.buffer;
+ ei_x_encode_tuple_header(&dynamic_buffer(state), 2);
+ ei_x_encode_tuple_header(&dynamic_buffer(state), 3);
+ ei_x_encode_ulong(&dynamic_buffer(state), ts->year);
+ ei_x_encode_ulong(&dynamic_buffer(state), ts->month);
+ ei_x_encode_ulong(&dynamic_buffer(state), ts->day);
+ ei_x_encode_tuple_header(&dynamic_buffer(state), 3);
+ ei_x_encode_ulong(&dynamic_buffer(state), ts->hour);
+ ei_x_encode_ulong(&dynamic_buffer(state), ts->minute);
+ ei_x_encode_ulong(&dynamic_buffer(state), ts->second);
+ break;
case SQL_C_CHAR:
if binary_strings(state) {
ei_x_encode_binary(&dynamic_buffer(state),
@@ -1491,7 +1518,7 @@ static void encode_data_type(SQLINTEGER sql_type, SQLINTEGER size,
ei_x_encode_atom(&dynamic_buffer(state), "SQL_TYPE_TIME");
break;
case SQL_TYPE_TIMESTAMP:
- ei_x_encode_atom(&dynamic_buffer(state), "SQL_TYPE_TIMESTAMP");
+ ei_x_encode_atom(&dynamic_buffer(state), "sql_timestamp");
break;
case SQL_BIGINT:
ei_x_encode_atom(&dynamic_buffer(state), "SQL_BIGINT");
@@ -1542,8 +1569,10 @@ static Boolean decode_params(db_state *state, byte *buffer, int *index, param_ar
{
int erl_type, size;
long bin_size, l64;
+ long val;
param_array* param;
-
+ TIMESTAMP_STRUCT* ts;
+
ei_get_type(buffer, index, &erl_type, &size);
param = &(*params)[i];
@@ -1564,50 +1593,67 @@ static Boolean decode_params(db_state *state, byte *buffer, int *index, param_ar
}
break;
case SQL_C_WCHAR:
- ei_decode_binary(buffer, index, &(param->values.string[param->offset]), &bin_size);
- param->offset += param->type.len;
- param->type.strlen_or_indptr_array[j] = SQL_NTS;
- break;
+ ei_decode_binary(buffer, index, &(param->values.string[param->offset]), &bin_size);
+ param->offset += param->type.len;
+ param->type.strlen_or_indptr_array[j] = SQL_NTS;
+ break;
+ case SQL_C_TYPE_TIMESTAMP:
+ ts = (TIMESTAMP_STRUCT*) param->values.string;
+ ei_decode_tuple_header(buffer, index, &val);
+ ei_decode_long(buffer, index, &val);
+ ts[j].year = (SQLUSMALLINT)val;
+ ei_decode_long(buffer, index, &val);
+ ts[j].month = (SQLUSMALLINT)val;
+ ei_decode_long(buffer, index, &val);
+ ts[j].day = (SQLUSMALLINT)val;
+ ei_decode_long(buffer, index, &val);
+ ts[j].hour = (SQLUSMALLINT)val;
+ ei_decode_long(buffer, index, &val);
+ ts[j].minute = (SQLUSMALLINT)val;
+ ei_decode_long(buffer, index, &val);
+ ts[j].second = (SQLUSMALLINT)val;
+ ts[j].fraction = (SQLINTEGER)0;
+ break;
case SQL_C_SLONG:
- if(!((erl_type == ERL_SMALL_INTEGER_EXT) ||
- (erl_type == ERL_INTEGER_EXT) ||
- (erl_type == ERL_SMALL_BIG_EXT) ||
- (erl_type == ERL_LARGE_BIG_EXT))) {
- return FALSE;
- }
-
- if(ei_decode_long(buffer, index, &l64)) {
- return FALSE;
- }
-
- /* For 64-bit platforms we downcast 8-byte long
- * to 4-byte SQLINTEGER, checking for overflow */
-
- if(l64>INT_MAX || l64INT_MAX || l64values.integer[j]=(SQLINTEGER)l64;
break;
-
+
case SQL_C_DOUBLE:
- if((erl_type != ERL_FLOAT_EXT)) {
- return FALSE;
- }
- ei_decode_double(buffer, index, &(param->values.floating[j]));
- break;
-
+ if((erl_type != ERL_FLOAT_EXT)) {
+ return FALSE;
+ }
+ ei_decode_double(buffer, index, &(param->values.floating[j]));
+ break;
+
case SQL_C_BIT:
if((erl_type != ERL_ATOM_EXT)) {
- return FALSE;
+ return FALSE;
}
ei_decode_boolean(buffer, index, &(param->values.bool[j]));
break;
-
+
default:
- return FALSE;
+ return FALSE;
}
-
+
return TRUE;
}
@@ -1987,7 +2033,7 @@ static void init_driver(int erl_auto_commit_mode, int erl_trace_driver,
db_state *state)
{
- int auto_commit_mode, trace_driver, use_srollable_cursors;
+ int auto_commit_mode, trace_driver;
if(erl_auto_commit_mode == ON) {
auto_commit_mode = SQL_AUTOCOMMIT_ON;
@@ -2013,18 +2059,19 @@ static void init_driver(int erl_auto_commit_mode, int erl_trace_driver,
environment_handle(state),
&connection_handle(state))))
DO_EXIT(EXIT_ALLOC);
+ /* By default Erlang handles all timeouts */
if(!sql_success(SQLSetConnectAttr(connection_handle(state),
SQL_ATTR_CONNECTION_TIMEOUT,
(SQLPOINTER)TIME_OUT, 0)))
- DO_EXIT(EXIT_CONNECTION);
+ DO_EXIT(EXIT_CONNECTION);
if(!sql_success(SQLSetConnectAttr(connection_handle(state),
SQL_ATTR_AUTOCOMMIT,
(SQLPOINTER)auto_commit_mode, 0)))
- DO_EXIT(EXIT_CONNECTION);
+ DO_EXIT(EXIT_CONNECTION);
if(!sql_success(SQLSetConnectAttr(connection_handle(state),
SQL_ATTR_TRACE,
(SQLPOINTER)trace_driver, 0)))
- DO_EXIT(EXIT_CONNECTION);
+ DO_EXIT(EXIT_CONNECTION);
}
static void init_param_column(param_array *params, byte *buffer, int *index,
@@ -2126,6 +2173,14 @@ static void init_param_column(param_array *params, byte *buffer, int *index,
(byte *)safe_malloc(num_param_values * sizeof(byte) * params->type.len);
break;
+ case USER_TIMESTAMP:
+ params->type.sql = SQL_TYPE_TIMESTAMP;
+ params->type.len = sizeof(TIMESTAMP_STRUCT);
+ params->type.c = SQL_C_TYPE_TIMESTAMP;
+ params->type.col_size = (SQLUINTEGER)19;//;sizeof(TIMESTAMP_STRUCT);
+ params->values.string =
+ (TIMESTAMP_STRUCT *)safe_malloc(num_param_values * params->type.len);
+ break;
case USER_FLOAT:
params->type.sql = SQL_FLOAT;
params->type.c = SQL_C_DOUBLE;
@@ -2297,12 +2352,16 @@ static db_result_msg map_sql_2_c_column(db_column* column)
break;
case SQL_TYPE_DATE:
case SQL_TYPE_TIME:
- case SQL_TYPE_TIMESTAMP:
column -> type.len = (column -> type.col_size) +
sizeof(byte);
column -> type.c = SQL_C_CHAR;
column -> type.strlen_or_indptr = SQL_NTS;
break;
+ case SQL_TYPE_TIMESTAMP:
+ column -> type.len = sizeof(TIMESTAMP_STRUCT);
+ column -> type.c = SQL_C_TYPE_TIMESTAMP;
+ column -> type.strlen_or_indptr = (SQLINTEGER)NULL;
+ break;
case SQL_BIGINT:
column -> type.len = DEC_NUM_LENGTH;
column -> type.c = SQL_C_CHAR;
@@ -2389,6 +2448,7 @@ static void * retrive_param_values(param_array *Param)
switch(Param->type.c) {
case SQL_C_CHAR:
case SQL_C_WCHAR:
+ case SQL_C_TYPE_TIMESTAMP:
return (void *)Param->values.string;
case SQL_C_SLONG:
return (void *)Param->values.integer;
diff --git a/lib/odbc/c_src/odbcserver.h b/lib/odbc/c_src/odbcserver.h
index 3b5a2b4d4d..e6d8df1f58 100644
--- a/lib/odbc/c_src/odbcserver.h
+++ b/lib/odbc/c_src/odbcserver.h
@@ -113,6 +113,7 @@
#define USER_TINY_INT 11
#define USER_WCHAR 12
#define USER_WVARCHAR 13
+#define USER_TIMESTAMP 14
/*------------------------ TYPDEFS ----------------------------------*/
diff --git a/lib/odbc/doc/src/databases.xml b/lib/odbc/doc/src/databases.xml
index 2c1b932084..9776736909 100644
--- a/lib/odbc/doc/src/databases.xml
+++ b/lib/odbc/doc/src/databases.xml
@@ -99,7 +99,7 @@
Note that when the value of the data to input is a string, it
has to be quoted with '. Example:
-\011odbc:sql_query(Ref, "INSERT INTO EMPLOYEE VALUES(1, 'Jane', 'Doe', 'F')").
+odbc:sql_query(Ref, "INSERT INTO EMPLOYEE VALUES(1, 'Jane', 'Doe', 'F')").
You may also input data using param_query/[3,4] and then
@@ -198,7 +198,7 @@ when p >= 16
SQL_TYPE_TIMESTAMP |
- String |
+ {{YY, MM, DD}, {HH, MM, SS}} |
SQL_LONGVARCHAR |
@@ -262,8 +262,8 @@ when p >= 16
customers of that department.
CREATE PROCEDURE DepartmentInfo (@DepartmentID INT) AS
-\011SELECT * FROM Employee WHERE department = @DepartmentID
-\011SELECT * FROM Customers WHERE department = @DepartmentID
+ SELECT * FROM Employee WHERE department = @DepartmentID
+ SELECT * FROM Customers WHERE department = @DepartmentID
diff --git a/lib/odbc/doc/src/getting_started.xml b/lib/odbc/doc/src/getting_started.xml
index 864c3a7b65..d543ef64d6 100644
--- a/lib/odbc/doc/src/getting_started.xml
+++ b/lib/odbc/doc/src/getting_started.xml
@@ -4,7 +4,7 @@
- 20022009
+ 20022010
Ericsson AB. All Rights Reserved.
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
Getting started
@@ -108,15 +108,15 @@
to insert many rows in one go.
6 > odbc:param_query(Ref,"INSERT INTO EMPLOYEE (NR, FIRSTNAME, "
-\011 "LASTNAME, GENDER) VALUES(?, ?, ?, ?)",
-\011 [{sql_integer,[2,3,4,5,6,7,8]},
-\011 {{sql_varchar, 20},
+ "LASTNAME, GENDER) VALUES(?, ?, ?, ?)",
+ [{sql_integer,[2,3,4,5,6,7,8]},
+ {{sql_varchar, 20},
["John", "Monica", "Ross", "Rachel",
"Piper", "Prue", "Louise"]},
-\011 {{sql_varchar, 20},
+ {{sql_varchar, 20},
["Doe","Geller","Geller", "Green",
"Halliwell", "Halliwell", "Lane"]},
-\011 {{sql_char, 1}, ["M","F","M","F","F","F","F"]}]).
+ {{sql_char, 1}, ["M","F","M","F","F","F","F"]}]).
{updated, 7}
Fetch all data in the table employee
diff --git a/lib/odbc/doc/src/notes.xml b/lib/odbc/doc/src/notes.xml
index 29fed0e286..09d78c3248 100644
--- a/lib/odbc/doc/src/notes.xml
+++ b/lib/odbc/doc/src/notes.xml
@@ -20,7 +20,7 @@
under the License.
-
+
ODBC Release Notes
otp_appnotes
nil
@@ -30,41 +30,51 @@
This document describes the changes made to the odbc application.
-
+
ODBC 2.10.8
-
- Improvements and New Features
-
- -
+
+ Improvements and New Features
+
+ -
- ODBC now handles the types SQL_WCHAR and SQL_WVARCHAR.
- ODBC also has a new connection option to return all
- strings as binaries and also expect strings to be
- binaries in the param_query function. This provides some
- but not a full unicode support.
+ ODBC now handles the types SQL_WCHAR and SQL_WVARCHAR.
+ Thanks to Juhani Ränkimies. ODBC also has a new
+ connection option to return all strings as binaries and
+ also expect strings to be binaries in the param_query
+ function. These changes provides some unicode support.
- Own Id: OTP-7452
+ Own Id: OTP-7452
-
-
-
-
-
- ODBC 2.10.7
-
- Fixed Bugs and Malfunctions
-
-
- The odbc application can now be compiled on FreeBSD.
- (Thanks to Kenji Rikitake.)
+ Now supports SQL_TYPE_TIMESTAMP on the format {{YY, MM,
+ DD}, {HH, MM, SS}}. Thanks to Juhani Ränkimies.
+
+ *** POTENTIAL INCOMPATIBILITY ***
- Own Id: OTP-8444
+ Own Id: OTP-8511
+
+
+ ODBC 2.10.7
+
+ Fixed Bugs and Malfunctions
+
+ -
+
+ The odbc application can now be compiled on FreeBSD.
+ (Thanks to Kenji Rikitake.)
+
+ Own Id: OTP-8444
+
+
+
+
+
Improvements and New Features
-
diff --git a/lib/odbc/doc/src/odbc.xml b/lib/odbc/doc/src/odbc.xml
index 3efbac8dd4..70d8cfbe22 100644
--- a/lib/odbc/doc/src/odbc.xml
+++ b/lib/odbc/doc/src/odbc.xml
@@ -311,7 +311,7 @@
capital letters, alas it is not currently supported by the
param_query function. Too know which Erlang data type
corresponds to an ODBC data type see the Erlang to ODBC
- data typemapping in the User's Guide.
+ data type mapping in the User's Guide.
diff --git a/lib/odbc/src/odbc.erl b/lib/odbc/src/odbc.erl
index f8914639e6..eb27a471ec 100644
--- a/lib/odbc/src/odbc.erl
+++ b/lib/odbc/src/odbc.erl
@@ -931,6 +931,16 @@ fix_params({sql_double, InOut, Values}) ->
{?USER_DOUBLE, fix_inout(InOut), Values};
fix_params({sql_bit, InOut, Values}) ->
{?USER_BOOLEAN, fix_inout(InOut), Values};
+fix_params({'sql_timestamp', InOut, Values}) ->
+ NewValues =
+ case (catch
+ lists:map(fun({{Year,Month,Day},{Hour,Minute,Second}}) ->
+ {Year,Month,Day,Hour,Minute,Second}
+ end, Values)) of
+ Result ->
+ Result
+ end,
+ {?USER_TIMESTAMP, fix_inout(InOut), NewValues};
%% default is IN %%%
fix_params({Type, Values}) ->
fix_params({Type, in, Values}).
diff --git a/lib/odbc/src/odbc_internal.hrl b/lib/odbc/src/odbc_internal.hrl
index 10ed139338..aa60120f9a 100644
--- a/lib/odbc/src/odbc_internal.hrl
+++ b/lib/odbc/src/odbc_internal.hrl
@@ -71,6 +71,7 @@
-define(USER_TINY_INT, 11).
-define(USER_WCHAR, 12).
-define(USER_WVARCHAR, 13).
+-define(USER_TIMESTAMP, 14).
%% INPUT & OUTPUT TYPE
-define(IN, 0).
diff --git a/lib/odbc/vsn.mk b/lib/odbc/vsn.mk
index 7af25db25b..d813466437 100644
--- a/lib/odbc/vsn.mk
+++ b/lib/odbc/vsn.mk
@@ -1,6 +1,6 @@
ODBC_VSN = 2.10.8
-TICKETS = OTP-7452
+TICKETS = OTP-7452 OTP-8511
TICKETS_2.10.6 = \
OTP-8250 \
--
cgit v1.2.3