Dear DBI Developers,
Attached (assuming it survives the list software) is a first attempt at
supporting the "long varchar" data type in DBD::Ingres. It is a unified
diff against DBD::Ingres 0.34.
Please note firstly that it is NOT production ready and contains at least
two memory leaks currently.
It allows you to write arbitrary length long varchar table fields (at
least up to the size of an int on your platform). It supports literal
strings of up to 2000 chars inline, but for longer strings you must
use bind variables of type DBI::SQL_LONGVARCHAR.
E.g.,
$sth = $dbh->prepare("INSERT INTO mytab VALUES (?)");
$sth->bind_param(1, "z" x 50000, DBI::SQL_LONGVARCHAR);
$sth->execute;
Trying to do this on a regular varchar field may cause all hell to break loose.
For reading it implements the behaviour for LongReadLen and LongTruncOk out
of the DBI module documentation, in particular:
LongReadLen = n && LongTruncOk = 1
reads the first n or all chars
LongReadLen = n && LongTruncOk = 0
reads all chars if field shorter than or equal to n
raises error if field longer than n
LongReadLen = 0
returns undef (NULL) for all long varchar reads
Since I'm not really a DBI expert and am largely winging it here, I'd
appreciate if someone with more of a DBI clue could look it over.
In particular, I need to safemalloc 2 statement-handle scope structures
in dbd_describe() but I don't know the correct/safe place to free them
again. In the code these are clearly marked with XXX comments.
Also, I've really only guessed at the correct values for long varchar
in type_info_all based on some doc reading and what regular varchar
contains.
If the attachment does not work you can fetch the diff from:
http://ariel.ucs.unimelb.edu.au/~mib/DBD-Ingres-0.34-longvarchar.diff
Hope to get some feedback soon. Quite frankly, this all goes to show what
you can achieve with a need, example code, some late nights and the manual
handy.
- Mike
diff -uNr DBD-Ingres-0.34.orig/Ingres.pm DBD-Ingres-0.34/Ingres.pm
--- DBD-Ingres-0.34.orig/Ingres.pm Sat May 18 21:24:42 2002
+++ DBD-Ingres-0.34/Ingres.pm Tue Jul 9 19:00:47 2002
@@ -196,6 +196,8 @@
1, 1, 3, 0, 0,0,undef,0,0 ],
[ 'CHAR', DBI::SQL_CHAR, undef, "'","'", "length",
1, 1, 3, 0, 0,0,undef,0,0 ],
+ [ 'LONG VARCHAR', DBI::SQL_LONGVARCHAR, undef, "'","'", undef,
+ 1, 1, 0, 0, 0,0,undef,0,0 ],
];
return $ti;
}
diff -uNr DBD-Ingres-0.34.orig/dbdimp.psc DBD-Ingres-0.34/dbdimp.psc
--- DBD-Ingres-0.34.orig/dbdimp.psc Sat May 18 21:15:42 2002
+++ DBD-Ingres-0.34/dbdimp.psc Tue Jul 9 19:37:01 2002
@@ -23,6 +23,12 @@
static int cur_session; /* the 'current' Ingres session_id */
static int nxt_session; /* the next 'available' session_id */
+typedef struct {
+ char *str;
+ int len;
+ int truncated;
+} get_hdlr_arg_t;
+
static void dump_sqlda(sqlda)
IISQLDA* sqlda;
{
@@ -726,6 +732,40 @@
return 1;
}
+/* Get handler function for getting long varchar types. This is called
+ * by Ingres when a select is done on a long varchar field. */
+static int
+get_handler(get_hdlr_arg_t *arg)
+{
+ EXEC SQL BEGIN DECLARE SECTION;
+ int data_end, chunk, max_length;
+ const char *cp;
+ EXEC SQL END DECLARE SECTION;
+ int length, offset;
+
+ data_end = 0;
+ max_length = arg->len;
+ cp = arg->str;
+
+ arg->truncated = 0;
+
+ while (data_end == 0) {
+ EXEC SQL GET DATA (:cp = segment,
+ :chunk = segmentlength,
+ :data_end = dataend)
+ WITH MAXLENGTH = :max_length;
+ cp += chunk;
+ max_length -= chunk;
+ if (max_length <= 0 && data_end == 0) {
+ arg->truncated = 1;
+ EXEC SQL ENDDATA;
+ break;
+ }
+ }
+
+ return 1;
+}
+
int
dbd_describe(sth, imp_sth)
SV *sth;
@@ -795,6 +835,32 @@
var->sqldata = (char*)SvPVX(fbh->sv);
fbh->var_ptr.pv = var->sqldata;
break;
+ case IISQ_LVCH_TYPE: {
+ get_hdlr_arg_t *arg;
+ IISQLHDLR *hdlr;
+
+ /* set up bufferspace. */
+ fbh->len = DBIc_LongReadLen(imp_sth) + 1;
+ fbh->sv = newSV((STRLEN)fbh->len);
+ strcpy(fbh->type, "s");
+ (void)SvUPGRADE(fbh->sv, SVt_PV);
+ SvREADONLY_on(fbh->sv);
+ (void)SvPOK_only(fbh->sv);
+
+/* XXX not being freed at the moment */
+ arg = safemalloc(sizeof(get_hdlr_arg_t));
+ arg->len = DBIc_LongReadLen(imp_sth);
+ arg->str = (char*)SvPVX(fbh->sv);
+
+/* XXX not being freed at the moment */
+ hdlr = safemalloc(sizeof(IISQLHDLR));
+ hdlr->sqlarg = (char *)arg;
+ hdlr->sqlhdlr = get_handler;
+
+ var->sqllen = 0;
+ var->sqltype = IISQ_HDLR_TYPE;
+ var->sqldata = (char *)hdlr;
+ break; }
default: /* oh dear! */
croak("DBD::Ingres: field %d has unsupported type %d\n",
i+1, var->sqltype);
@@ -823,7 +889,41 @@
return 1;
}
+/* Put handler function for putting long varchar types. This is called
+ * by Ingres when in insert is done on a long varchar field using
+ * a type-specified SQL_LONGVARCHAR bind param. */
+static int
+put_handler(const char *str)
+{
+ EXEC SQL BEGIN DECLARE SECTION;
+ int data_end, chunk;
+ const char *cp;
+ EXEC SQL END DECLARE SECTION;
+ int length, offset;
+
+ data_end = 0;
+ length = strlen(str);
+ offset = 0;
+
+ while (offset < length) {
+ cp = str + offset;
+ chunk = length - offset;
+ if (chunk <= 1024)
+ data_end = 1;
+ else
+ chunk = 1024;
+
+ EXEC SQL PUT DATA (segment = :cp,
+ segmentlength = :chunk,
+ dataend = :data_end);
+
+ offset += chunk;
+ }
+
+ return 1;
+}
+
int
dbd_bind_ph (sth, imp_sth, param, value, sql_type, attribs, is_inout, maxlen)
SV *sth;
@@ -873,6 +973,8 @@
case SQL_CHAR:
case SQL_VARCHAR:
type = 3; break;
+ case SQL_LONGVARCHAR:
+ type = 4; break;
default:
croak("DBD::Ingres::bind_param: Unknown TYPE: %d, param_no %d",
sql_type, param_no);
@@ -952,7 +1054,35 @@
var->sqllen = strlen;
var->sqltype = IISQ_CHA_TYPE;
break; }
+ case 4: { /* long varchar */
+ STRLEN strlen;
+ IISQLHDLR *hdlr;
+ char *string = SvPV(value, strlen);
+ int hdlr_int = (sizeof(IISQLHDLR) + sizeof(int) - 1)/sizeof(int);
+ int need_int = 2 * hdlr_int + 1;
+
+ if (var->sqldata == (char *)&sv_undef) { /* initital allocation */
+ Newz(42, buf, need_int, int);
+ *buf = need_int;
+ hdlr = (IISQLHDLR *) (buf+hdlr_int);
+ } else {
+ buf = ((int *) var->sqldata)-1;
+ if (*buf < need_int) { /* need to reallocate? */
+ Renew(buf, need_int, int);
+ *buf = need_int;
+ }
+ hdlr = (IISQLHDLR *) (buf+hdlr_int);
+ }
+
+ hdlr->sqlarg = (char *)string;
+ hdlr->sqlhdlr = put_handler;
+ var->sqldata = (char *)hdlr;
+ var->sqlind = 0;
+ var->sqllen = strlen;
+ var->sqltype = IISQ_HDLR_TYPE;
+ break; }
}
+
if (!SvOK(value)) {
if (dbis->debug >= 3) PerlIO_printf(DBILOGFP, "bind(NULL)");
if (var->sqldata == (char *)&sv_undef) {
@@ -1112,6 +1242,12 @@
/* NULL value */
(void)SvOK_off(sv);
if (dbis->debug >= 3) PerlIO_printf(DBILOGFP, "NULL\n");
+ } else if (fbh->origtype == IISQ_LVCH_TYPE &&
+ DBIc_LongReadLen(imp_sth) == 0) {
+ /* if LongReadLen is 0 return undef (from DBI spec) */
+ (void)SvOK_off(sv);
+ if (dbis->debug >= 3)
+ PerlIO_printf(DBILOGFP, "SKIPPED LONG VARCHAR (NULL)\n");
} else {
switch (fbh->type[0]) {
case 'd':
@@ -1125,7 +1261,20 @@
if (dbis->debug >= 3)
PerlIO_printf(DBILOGFP, "Double: %lf\n", SvNV(sv));
break;
- case 's':
+ case 's': {
+ IISQLHDLR *hdlr;
+ get_hdlr_arg_t *arg;
+ if (fbh->origtype == IISQ_LVCH_TYPE &&
+ ! DBIc_has(imp_sth, DBIcf_LongTruncOk)) {
+ hdlr = (IISQLHDLR *)var->sqldata;
+ arg = (get_hdlr_arg_t *)hdlr->sqlarg;
+ if (arg->truncated != 0) {
+ (void)SvOK_off(sv);
+ error(sth, -7, "LongReadLen too small for untruncated long
+varchar data", 0);
+ break;
+ }
+ }
+
SvCUR(fbh->sv) = fbh->len;
SvPVX(fbh->sv)[fbh->len-1] = 0;
/* strip trailing blanks */
@@ -1143,7 +1292,7 @@
if (dbis->debug >= 3)
PerlIO_printf(DBILOGFP, "Text: '%s', Chop: %d\n",
SvPVX(sv), DBIc_has(imp_sth, DBIcf_ChopBlanks));
- break;
+ break; }
default:
croak("DBD::Ingres: wierd field-type '%s' in field no. %d?\n",
fbh->type, i);
@@ -1315,6 +1464,9 @@
case IISQ_VCH_TYPE:
type = SQL_VARCHAR;
break;
+ case IISQ_LVCH_TYPE:
+ type = SQL_LONGVARCHAR;
+ break;
default: /* oh dear! */
type = 0;
break;
@@ -1368,6 +1520,9 @@
case IISQ_TXT_TYPE:
case IISQ_VCH_TYPE:
len = imp_sth->fbh[i].origlen;
+ break;
+ case IISQ_LVCH_TYPE:
+ len = imp_sth->fbh[i].len - 1;
break;
default: /* oh dear! */
break;
Mike Battersby <[EMAIL PROTECTED]> The University of Melbourne
Fetch my pgp key from: http://ariel.ucs.unimelb.edu.au/~mib/pgpkey.txt