Re: Sending dbnumeric in dbrpcparam
Frediano Ziglio <[email protected]>
| Newsgroups | gmane.comp.db.tds.freetds |
|---|---|
| Message-ID | <[email protected]> |
2009/6/2 James K. Lowden <[email protected]>: > Damien Churchill wrote: >> >> Here is the code that I've tried: http://pastebin.com/f69a761d7 >> > >> > http://manuals.sybase.com/onlinebooks/group-cnarc/cng1110e/dblib/@Generic__BookTextView/34413;pt=39614 >> > >> > "The value of type indicates the datatype of *value. See >> > "Types" for more information. For types that have no C equivalent, >> > such as SYBDATETIME, SYBMONEY, SYBNUMERIC, or SYBDECIMAL, use >> > dbconvert_ps to initialize *value." >> >> Hmm, I gave this a try and it still fails with the same error. > > Um, that's not possible? The advice is to convert your numeric value to a > "known" type -- a C string, say -- and bind your parameter to that > (intermediate) buffer. Line 63 becomes: > > ret = dbrpcparam(dbproc, "@idecimal", 0, SYBCHAR, -1, strlen(tmp), (BYTE > *)tmp); > > At that point, it's just a plain-vanilla RPC call. > >> Would this only work with Sybase perhaps? > > It should work regardless of TDS protocol. The server interaction is > different, but the API (and its limitations) are the same. > > FWIW, there's no technical reason dbrpcparam couldn't deal directly with > these types. It's not specified by the vendors, but it's a reasonable > extension. In case you want to try your hand at it. > > HTH. > I tried to add test to our unittests (attached) and it seems that client library doesn't fill correctly needed informations for numeric (precision and scale). I did also a small change to dblib. However I would check if DBDECIMAL declaration is different between Sybase and Microsoft. It seems for instance that Sybase use big endian order while MS use little endian. freddy77 _______________________________________________ FreeTDS mailing list [email protected] http://lists.ibiblio.org/mailman/listinfo/freetds
numeric_test.diff
(application/octet-stream, 4.2 KB)
Index: freetds83/src/dblib/rpc.c
===================================================================
--- freetds83.orig/src/dblib/rpc.c
+++ freetds83/src/dblib/rpc.c
@@ -429,6 +429,12 @@ param_info_alloc(TDSSOCKET * tds, DBREMO
pcol->column_size = p->datalen;
}
}
+ if (temp_value && is_numeric_type(temp_type)) {
+ DBDECIMAL *dec = (DBDECIMAL*) temp_value;
+ pcol->column_prec = dec->precision;
+ pcol->column_scale = dec->scale;
+ temp_datalen = tds_numeric_bytes_per_prec[dec->precision];
+ }
pcol->on_server.column_size = pcol->column_size;
pcol->column_output = p->status;
Index: freetds83/src/dblib/unittests/Makefile.am
===================================================================
--- freetds83.orig/src/dblib/unittests/Makefile.am
+++ freetds83/src/dblib/unittests/Makefile.am
@@ -10,7 +10,7 @@ TESTS = t0001$(EXEEXT) t0002$(EXEEXT) t
bcp$(EXEEXT) thread$(EXEEXT) text_buffer$(EXEEXT)\
done_handling$(EXEEXT) timeout$(EXEEXT) \
hang$(EXEEXT) null$(EXEEXT) null2$(EXEEXT) \
- setnull$(EXEEXT)
+ setnull$(EXEEXT) numeric$(EXEEXT)
check_PROGRAMS = $(TESTS)
SQL_DIST = bcp.sql dbmorecmds.sql done_handling.sql rpc.sql \
@@ -53,6 +53,7 @@ hang_SOURCES = hang.c common.c common.h
null_SOURCES = null.c common.c common.h
null2_SOURCES = null2.c common.c common.h
setnull_SOURCES = setnull.c common.c common.h
+numeric_SOURCES = numeric.c common.c common.h
AM_CPPFLAGS = -DFREETDS_SRCDIR=\"$(srcdir)\" -I$(top_srcdir)/include
if MINGW32
Index: freetds83/src/dblib/unittests/.cvsignore
===================================================================
--- freetds83.orig/src/dblib/unittests/.cvsignore
+++ freetds83/src/dblib/unittests/.cvsignore
@@ -45,4 +45,5 @@ hang
null
null2
setnull
+numeric
Index: freetds83/src/dblib/unittests/numeric.sql
===================================================================
--- /dev/null
+++ freetds83/src/dblib/unittests/numeric.sql
@@ -0,0 +1,12 @@
+IF OBJECT_ID('testDecimal') IS NOT NULL DROP PROC testDecimal
+go
+CREATE PROCEDURE testDecimal
+ @idecimal NUMERIC(20,10)
+AS
+BEGIN
+ SELECT @idecimal
+END
+go
+IF OBJECT_ID('testDecimal') IS NOT NULL DROP PROC testDecimal
+go
+
Index: freetds83/src/dblib/unittests/numeric.c
===================================================================
--- /dev/null
+++ freetds83/src/dblib/unittests/numeric.c
@@ -0,0 +1,88 @@
+#define MSDBLIB 1
+#include "common.h"
+
+int
+main(int argc, char **argv)
+{
+ LOGINREC *login;
+ DBPROCESS *dbproc;
+ DBNUMERIC *numeric;
+ RETCODE ret;
+
+ read_login_info(argc, argv);
+
+ login = dblogin();
+
+ DBSETLUSER(login, USER);
+ DBSETLPWD(login, PASSWORD);
+ DBSETLAPP(login, "numeric");
+ dbsetmaxprocs(25);
+ DBSETLHOST(login, SERVER);
+
+ dbproc = dbopen(login, SERVER);
+ if (strlen(DATABASE))
+ dbuse(dbproc, DATABASE);
+
+ sql_cmd(dbproc);
+ dbsqlexec(dbproc);
+ while (dbresults(dbproc) != NO_MORE_RESULTS) {
+ /* nop */
+ }
+
+ sql_cmd(dbproc);
+ dbsqlexec(dbproc);
+ while (dbresults(dbproc) != NO_MORE_RESULTS) {
+ /* nop */
+ }
+
+ ret = dbcmd(dbproc,
+ "SET ARITHABORT ON;"
+ "SET CONCAT_NULL_YIELDS_NULL ON;"
+ "SET ANSI_NULLS ON;"
+ "SET ANSI_NULL_DFLT_ON ON;"
+ "SET ANSI_PADDING ON;"
+ "SET ANSI_WARNINGS ON;"
+ "SET ANSI_NULL_DFLT_ON ON;"
+ "SET CURSOR_CLOSE_ON_COMMIT ON;"
+ "SET QUOTED_IDENTIFIER ON");
+ printf("%d\n", ret);
+ ret = dbsqlexec(dbproc);
+ printf("%d\n", ret);
+
+ ret = dbcancel(dbproc);
+ printf("%d\n", ret);
+
+ ret = dbrpcinit(dbproc, "testDecimal", 0);
+ printf("%d\n", ret);
+
+ numeric = (DBDECIMAL *) malloc(sizeof(DBDECIMAL));
+ numeric->scale = 5;
+ numeric->precision = 6;
+#ifdef DBNTWIN32
+ numeric->sign = 0;
+ numeric->val[0] = 1;
+ numeric->val[1] = 2;
+ numeric->val[2] = 3;
+ numeric->val[3] = 4;
+ numeric->val[4] = 5;
+#else
+ numeric->array[0] = 5;
+ numeric->array[1] = 1;
+ numeric->array[2] = 2;
+ numeric->array[3] = 3;
+ numeric->array[4] = 4;
+ numeric->array[5] = 5;
+#endif
+
+ ret = dbrpcparam(dbproc, "@idecimal", 0, SYBDECIMAL, -1, sizeof(DBDECIMAL), (BYTE *) numeric);
+ printf("%d\n", ret);
+ ret = dbrpcsend(dbproc);
+ printf("%d\n", ret);
+ ret = dbsqlok(dbproc);
+ printf("%d\n", ret);
+
+ sql_cmd(dbproc);
+
+ printf("Succeed: %d\n", SUCCEED);
+ return 0;
+}