root/trunk/sdbo/odbc_stmt.d

Revision 15, 34.3 kB (checked in by toaster, 8 years ago)

documentation changes

Line 
1 /********************************************************
2  *                                                      *
3  * Copyright (c) 2004 Wolfgang Borgsmüller (wb@sapo.pt) *
4  *                                                      *
5  * Usage of the works is permitted provided that this   *
6  * instrument is retained with the works, so that any   *
7  * entity that uses the works is notified of this       *
8  * instrument.                                          *
9  *                                                      *
10  * DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY.          *
11  *                                                      *
12  * [2004, Fair License: rhid.com/fair]                  *
13  ********************************************************/
14
15 /*! \if never
16 */
17  
18 module sdbo.odbc_stmt;
19
20 private import std.stdio;
21 private import std.string;
22
23
24
25 version(linux) {
26     version = bundled_headers;
27 }
28
29 version(bundled_headers) {
30     private import sdbo._odbc_headers.sql;
31     private import sdbo._odbc_headers.sqlext;
32     private import sdbo._odbc_headers.sqltypes;
33     version(Windows)
34         private import sdbo._odbc_headers.odbc32dll;
35    
36 } else {
37     private import std.c.windows.sql;
38     private import std.c.windows.sqlext;
39     private import std.c.windows.sqltypes;
40     private import std.c.windows.odbc32dll;
41 }
42
43 private import sdbo.odbc_env;
44 private import sdbo.odbc_dbc;
45 private import sdbo.odbc_etc;
46
47 /*! \endif
48 */
49
50
51
52 /*! \brief Helper class for the statement handle hstmt.
53  *
54  *
55  *
56  */
57  
58 public class SqlResult
59 {
60    
61     private struct Binding
62     {
63         bit allocated;
64         bit bound;
65
66         SQLPOINTER buffer;
67         SQLINTEGER bufLen;
68         SQLINTEGER* StrLen_or_IndPtr; /* this integer goes into the dll so it must be malloc'd */
69     }
70    
71     private struct ParamBinding
72     {
73        
74         SQLINTEGER  valueType;
75         SQLINTEGER  paramType;
76        
77         Binding binding;
78     }
79    
80     private struct ColumnBinding
81     {
82         SQLINTEGER targetType = UNBOUND;
83
84         Binding binding;
85     }
86    
87     public enum : int {
88         D_LONG =   SQL_C_SBIGINT,
89         D_INT =    SQL_C_SLONG,
90         D_SHORT =  SQL_C_SSHORT,
91         D_DOUBLE = SQL_C_DOUBLE,
92         D_FLOAT =  SQL_C_FLOAT,
93         D_CHAR =   SQL_C_CHAR,
94         D_WCHAR =  SQL_C_WCHAR,
95        
96         UNBOUND = 0
97     }
98
99     private static SQLINTEGER defaultTarget(int sqlType) {
100        
101         //writefln("defaultTarget(sql type %d)", sqlType);
102        
103         switch(sqlType) {
104            
105             case SQL_BIGINT:            return SQL_C_SBIGINT; /* odbc assigns char as default for this */
106             case SQL_TYPE_DATE:         return SQL_C_CHAR;
107             case SQL_TYPE_TIME:         return SQL_C_CHAR;
108             case SQL_TYPE_TIMESTAMP:    return SQL_C_CHAR;
109             default:                    return SQL_C_DEFAULT;
110         }
111     }
112    
113
114     private static bit checkTarget(int dType, int sqlType, int targetType)
115     {
116         if(dType == targetType)
117             return true;
118        
119         if(targetType == SQL_C_DEFAULT) {
120            
121             /* for columns bound with SQL_C_DEFAULT, check for sql types with compatible c type */
122            
123             switch(dType) {
124                
125                 case D_INT:     return sqlType == SQL_INTEGER;
126                 case D_SHORT:   return sqlType == SQL_SMALLINT;
127                 case D_DOUBLE:  return sqlType == SQL_DOUBLE || sqlType == SQL_FLOAT;
128                 case D_FLOAT:   return sqlType == SQL_REAL;
129                 case D_CHAR:    return sqlType == SQL_CHAR || sqlType == SQL_VARCHAR || sqlType == SQL_LONGVARCHAR || sqlType == SQL_NUMERIC || sqlType == SQL_BIGINT;
130                
131                 case D_LONG:   
132                 case D_WCHAR:   
133                 default:        return false;
134             }
135            
136         }
137         return false;
138     }
139    
140        
141     package OdbcConnection dbc;
142     private SqlResult next = null;
143     private SqlResult previous = null;
144    
145     package HSTMT hstmt;
146    
147    
148    
149     private ColumnSpec*[] cspecs;
150     private ColumnBinding*[] cbindings;
151     private ParamBinding*[] pbindings;
152    
153     private uint[char[]] cIndex;
154    
155     private uint fetchCount = 0;
156    
157     private bit prepared = false;
158     private bit executed = false;
159     private bit executedDirectly = false;
160    
161    
162     /*! \brief Diagnostics message from the last odbc call that returned SQL_SUCCESS_WITH_INFO
163     */
164     public OdbcMessage lastMessage;
165    
166     /*! \brief Return code from the last API call
167      *
168      * If this is set to SQL_SUCCESS_WITH_INFO, the
169      * info message is available in lastMessage.
170      *
171      *
172     */
173     public RETCODE lastRetcode;
174    
175
176     /*! \brief Read/write: Automatic column binding
177      *
178      * This value is used by the class internally
179      * on the first fetch call after a new query.
180      *
181      * On the first fetch, if bindAll is true,
182      * all columns will be bound.
183      * Columns not defined by setBinding will be bound to a default target type.
184      *
185      * If bindAll is false, only those columns defined with the setBinding functions
186      * will be bound. All other columns will remain unbound.
187      *
188      *
189      * Changes to this value won't be in effect between subsequent fetch'es after the first fetch
190      *
191      * Closing the result or executing a new query resets this value to true. The bindAll behaviour
192      * must be set between the query execution and the first call to fetch.
193      *
194      *
195      * Default of bindAll is true.
196      *
197     */
198
199     public bit bindAll = true;
200    
201    
202    
203     private this(OdbcConnection dbc)
204     {
205         this.dbc = dbc;
206         lastMessage = new OdbcMessage;
207         dbc.checkRetcode(SQLAllocHandle(SQL_HANDLE_STMT, dbc.hdbc, &hstmt)
208             , "SQLAllocHandle");
209        
210         next = dbc.reslist;
211         if(next) next.previous = this;
212         dbc.reslist = this;
213     }
214    
215     private this(OdbcConnection dbc, char[] sql)
216     {
217         this(dbc);
218         execDirect(sql);
219         executedDirectly = true;
220     }
221        
222    
223     ~this()
224     {
225        
226         //writefln("result destructor; next: %d", next);
227        
228         if(previous) {
229             previous.next = next;
230             if(next) next.previous = previous;
231         } else {
232             if(next) next.previous = null;
233             dbc.reslist = next;
234         }
235        
236         try {
237            
238             if(executedDirectly || executed || cbindings.length > 0 || pbindings.length > 0)
239                 close();
240            
241             checkRetcode(SQLFreeHandle(SQL_HANDLE_STMT, hstmt), "SQLFreeHandle");
242        
243         } catch(SqlException e) {}
244
245         //writefln("/result destructor");
246     }
247    
248
249     /*! \brief The parent connections environment.
250      *
251      * shortcut for result.connection().environment()
252      */
253    
254     public OdbcEnvironment environment()
255     {
256         return dbc.environment();
257     }
258    
259     /*! \brief The parent connection.
260      *
261      */
262    
263     public OdbcConnection connection()
264     {
265         return dbc;
266     }
267    
268
269     /*! \brief The underlying hstmt for operations not provided by this class.
270      *
271      */
272    
273     public HSTMT handle()
274     {
275         return hstmt;
276     }
277    
278    
279     /*! \brief Prepares a SQL statement for later execution.
280      */
281    
282     public void prepare(char[] query) {
283    
284         if(executed || executedDirectly)
285             close();
286        
287         checkRetcode(SQLPrepare(
288             hstmt,
289             cast(SQLCHAR*) toStringz(query),
290             query.length
291             ), "SQLPrepare");
292     }
293    
294    
295     /*! \brief Sets an input parameter for a prepared statement
296      *
297      * Parameter index numbers start at 0 for the leftmost parameter.
298      */
299     public void setInput(int num, long param, int paramType = SQL_BIGINT) {
300         setInputBuffer(num, D_LONG, &param, param.sizeof, paramType);
301     }
302    
303     /*! \brief Sets an input parameter for a prepared statement
304      *
305      * Parameter index numbers start at 0 for the leftmost parameter.
306      */
307     public void setInput(int num, int param, int paramType = SQL_INTEGER) {
308         setInputBuffer(num, D_INT, &param, param.sizeof, paramType);
309     }
310    
311     /*! \brief Sets an input parameter for a prepared statement
312      *
313      * Parameter index numbers start at 0 for the leftmost parameter.
314      */
315     public void setInput(int num, short param, int paramType = SQL_SMALLINT) {
316         setInputBuffer(num, D_SHORT, &param, param.sizeof, paramType);
317     }
318    
319     /*! \brief Sets an input parameter for a prepared statement
320      *
321      * Parameter index numbers start at 0 for the leftmost parameter.
322      */
323     public void setInput(int num, double param, int paramType = SQL_DOUBLE) {
324         setInputBuffer(num, D_DOUBLE, &param, param.sizeof, paramType);
325     }
326    
327     /*! \brief Sets an input parameter for a prepared statement
328      *
329      * Parameter index numbers start at 0 for the leftmost parameter.
330      */
331     public void setInput(int num, float param, int paramType = SQL_REAL) {
332         setInputBuffer(num, D_FLOAT, &param, param.sizeof, paramType);
333     }
334    
335     /*! \brief Sets an input parameter for a prepared statement
336      *
337      * Parameter index numbers start at 0 for the leftmost parameter.
338      *
339      */
340     public void setInput(int num, char[] param, int paramType = SQL_VARCHAR, int bufLen = 0) {
341         setInputBuffer(num, D_CHAR, cast(void*)param, param.length, paramType, bufLen);
342     }
343
344     /*! \brief Sets an input parameter for a prepared statement
345      *
346      * Parameter index numbers start at 0 for the leftmost parameter.
347      *
348      * This function is called setInputw, otherwise the compiler complains about
349      * ambiguous overload with setInput(... char[] ...)
350      *
351      */
352     public void setInputw(int num, wchar[] param, int paramType = SQL_WVARCHAR, int bufLen = 0) {
353         setInputBuffer(num, D_WCHAR, cast(void*)param, wchar.sizeof * param.length, paramType, bufLen);
354     }
355    
356    
357     /*! \if never
358     */
359    
360     private void setInputBuffer(int num, int valueType, void* param, uint paramLen, SQLINTEGER paramType, uint minBufLen = 0)
361     in {
362         assert(minBufLen == 0 || minBufLen >= paramLen);
363        
364     }
365     body {
366         /* sets the input parameter buffer for a column to the value submitted by the application.
367          * allocs, reallocs and binds the parameter as necessary.
368          */
369        
370         if(minBufLen == 0) minBufLen = paramLen;
371        
372         if(pbindings.length < num + 1) {
373             pbindings.length = num + 1;
374         }
375
376         if(pbindings[num]) {
377        
378             if(minBufLen > pbindings[num].binding.bufLen) {
379            
380                 pbindings[num].binding.bound = false;
381                 reallocBinding(&pbindings[num].binding, minBufLen);
382            
383             } else if(pbindings[num].binding.bound && (
384                             pbindings[num].valueType != valueType
385                             || pbindings[num].paramType != paramType
386                             )) {
387                
388                 pbindings[num].binding.bound = false;
389             }
390            
391         } else {
392            
393             pbindings[num] = new ParamBinding;
394             allocBinding(&pbindings[num].binding, minBufLen);
395         }
396        
397         if(!pbindings[num].binding.bound) {
398             bindParameter(SQL_PARAM_INPUT, num, valueType, paramType);
399         }
400        
401         pbindings[num].binding.buffer[0..paramLen] = param[0..paramLen];
402         *pbindings[num].binding.StrLen_or_IndPtr = paramLen;
403        
404     }
405    
406     private void bindParameter(SQLSMALLINT iotype, int num, SQLSMALLINT valueType, SQLSMALLINT paramType)
407     in {
408         assert(num < pbindings.length);
409         assert(pbindings[num]);
410         assert(pbindings[num].binding.allocated);
411     }
412     body {
413        
414         checkRetcode(SQLBindParameter(
415             hstmt,
416             num + 1,
417             iotype,
418             valueType,
419             paramType,
420             pbindings[num].binding.bufLen,
421             2,
422             pbindings[num].binding.buffer,
423             pbindings[num].binding.bufLen,
424             pbindings[num].binding.StrLen_or_IndPtr
425             ), "SQLBindParameter");
426
427     }
428    
429     /*! \endif
430     */
431
432    
433     /*! \brief Executes a prepared statement for this result.
434      *
435      * \warning
436      * If the result was bound to a previous query, all resources related to that query will be freed
437      * For instance, a buffer retrieved with rawBuffer will be freed.
438      *
439      */
440
441      public void execute()
442      {
443          checkRetcode(SQLExecute(hstmt), "SQLExecute");
444          executed = true;
445      }
446    
447
448     /*! \brief Executes a query for this result.
449      *
450      * \warning
451      * If the result was bound to a previous query, all resources related to that query will be freed
452      * For instance, a buffer retrieved with rawBuffer will be freed.
453      *
454      */
455    
456
457     public void execDirect(char[] sql)
458     {   
459        
460         if(executedDirectly || executed || prepared) close();
461        
462         checkRetcode(SQLExecDirect(hstmt, toStringz(sql), SQL_NTS), "SQLEcexDirect");
463         executedDirectly = true;
464        
465         short ccount = 0;
466         RETCODE rc = checkRetcode(SQLNumResultCols(hstmt, &ccount), "SQLNumResultCols");
467        
468         if(ccount == 0) {
469        
470         } else {
471            
472             cspecs.length = ccount;
473             cbindings.length = ccount;
474             getColumnSpecs();
475            
476         }
477        
478     }
479    
480     /*
481      *
482      * Column attributes
483      *
484      *
485      */
486    
487     /*! \brief Column count - 0 for not row source type results
488      *
489      * Number of columns in the result.
490      * If the the query didn't produce a row source,
491      * columnCount returns 0.
492      *
493      * Column index starts at 0 with the leftmost column.
494      *
495      */
496    
497     public uint columnCount()
498     {
499         return cspecs.length;
500     }
501      
502    
503     /*! \brief Array of column names, sorted by column index
504      *
505      * An array of strings holding the column names.
506      * 0 is the leftmost column, length-1 the rightmost.
507      *
508      *
509      */
510    
511     public char[][] columns()
512     {
513         assert(cspecs.length);
514        
515         char[][] columns;
516         columns.length = cspecs.length;
517        
518         int i = 0;
519         foreach(ColumnSpec* ca; cspecs) {
520             columns[i++] = ca.name;
521         }
522        
523         return columns;
524     }
525    
526     /* column specification. returns a pointer. */
527    
528     /*! \brief Column attributes specification
529      *
530      * This returns a struct holding information aboute a single column:
531      * - .name - name of the column (char[])
532      * - .type - column type (char[])
533      * - .csize - size of the column data (ushort)
534      * - .digits - (ushort)
535      * - .nullable - (ushort)
536      * - .numericType - (ushort)
537      *
538      */
539    
540     public ColumnSpec* columnSpec(char[] column)
541     {
542         assert(column in cIndex);
543         return columnSpec(cIndex[column]);
544     }
545    
546     /*! \brief Column attributes specification
547         
548     */
549     public ColumnSpec* columnSpec(uint col)
550     {
551         assert(col < cspecs.length);
552         /*
553         ColumnSpec* cs = new ColumnSpec;
554         
555         void[] v1 = (cast(void*)cs)[0..(*cs).sizeof];
556         void[] v2 = (cast(void*)cspecs[col])[0..(*cspecs[col]).sizeof];
557         
558         v1[0..length] = v2[0..length];
559         */
560        
561         return cspecs[col];
562        
563     }
564    
565     /*! \brief Sets the binding for a column
566      *
567      * 
568      * Valid parameters:
569      <pre>
570          SqlResult.D_LONG
571          SqlResult.D_INT
572          SqlResult.D_SHORT
573         
574          SqlResult.D_DOUBLE
575          SqlResult.D_FLOAT
576         
577          SqlResult.D_WCHAR
578          SqlResult.D_CHAR
579         
580          SqlResult.UNBOUND
581      </pre>
582      *
583      * This function sets the binding target type for a column or for all columns,
584      * but it doesn't actually bind the columns.
585      * The columns are bound on the first call to fetch().
586      *
587      *
588      * Every D type is mapped to a predefined target C type. Some target types can be extracted
589      * with the value functions.
590      *
591     
592         D_LONG =   SQL_C_SBIGINT   result.vlong(col)
593         D_INT =    SQL_C_SLONG     result.vint(col)
594         D_SHORT =  SQL_C_SSHORT    result.vshort(col)
595         D_DOUBLE = SQL_C_DOUBLE    result.vdouble(col)
596         D_FLOAT =  SQL_C_FLOAT     result.vfloat(col)
597         D_CHAR =   SQL_C_CHAR      result.string(col) result.stringz(col)
598         D_WCHAR =  SQL_C_WCHAR     result.wstring(col)
599     
600      *
601      *
602      * Default target types for column bindings are the default target types from the ODBC specs
603      * with the following exceptions:
604      <pre>
605         SQL_BIGINT:         D_LONG (SQL_C_SBIGINT) - odbc default is SQL_C_CHAR
606         SQL_TYPE_DATE:      D_CHAR (SQL_C_CHAR)
607         SQL_TYPE_TIME:      D_CHAR (SQL_C_CHAR)
608         SQL_TYPE_TIMESTAMP: D_CHAR (SQL_C_CHAR)
609      </pre>
610     
611      * SqlResult.UNBOUND causes the column not to be bound.
612      * \note
613      * If bindAll is true, all columns set to UNBOUND will be set to their default types on the first fetch.
614      * After the first fetch, a column set to UNBOUND stays unbound even if bindAll is true.
615      *
616      * If the column is already bound it will be unbound.
617      * \warning
618      * Allocated buffer for the column will be freed.
619      *
620      *
621      */
622    
623     public void setBinding(uint col, int dType)
624     {
625         assert(col < cbindings.length);
626        
627         cbindings[col].targetType = dType; /* mapped c types */
628         if(fetchCount > 0)
629             bindColumn(col); /* rebind column if this is called after the first fetch */
630     }
631      
632     /*! \brief Sets the binding for a column identified by column name
633     */
634     public void setBinding(char[] column, int dType)
635     {
636         assert(column in cIndex);
637         setBinding(cIndex[column], dType);
638     }
639      
640     /*! \brief Sets the binding for all columns
641     */
642     public void setBinding(int dType)
643     {
644
645         foreach(ColumnBinding* cb; cbindings)
646             cb.targetType = dType; /* mapped c types */
647        
648         if(fetchCount != 0) {
649
650             if(dType == UNBOUND) {
651        
652                 checkRetcode(SQLFreeStmt(
653                     hstmt,
654                     SQL_UNBIND
655                     ), "SQLFreeStmt");
656                    
657                 foreach(ColumnBinding* cb; cbindings) {
658                     cb.binding.bound = false;
659                     if(cb.binding.allocated)
660                         freeBinding(&cb.binding);
661                 }
662
663                    
664             } else {
665                 bindColumns();
666             }
667         }
668     }
669      
670    
671     /*! \brief Sets the target c type for a column
672      *
673      *
674      * This function can be called on any individual column to set
675      * it's target c type.
676      *
677      *
678      * Valid arguments are the SQL_C_* constants.
679      *
680      *
681      */
682      
683     public void setTargetC(uint col, SQLINTEGER targetType)
684     {
685         assert(col < cbindings.length);
686        
687         cbindings[col].targetType = targetType;
688        
689         if(fetchCount > 0)
690             bindColumn(col); /* rebind column if this is called after the first fetch */
691     }
692    
693    
694    
695     /*! \brief Fetches the next row into the buffer.
696      *
697      * Returns true if there was a new row to fetch, false otherwise.
698      *
699      *
700      *
701      */
702    
703     public bit fetch()
704     {
705         if(fetchCount == 0) {
706             if(bindAll) {
707                 for(int col = 0; col < cbindings.length; col++) {
708                     if(cbindings[col].targetType == UNBOUND)
709                         cbindings[col].targetType = defaultTarget(cspecs[col].numericType);
710                 }
711             }
712             bindColumns();
713         }
714         fetchCount++;
715
716         RETCODE rc = checkRetcode(SQLFetch(hstmt), "SQLFetch");
717        
718         if(rc == SQL_NO_DATA_FOUND)
719             return false;
720        
721         return true;
722        
723     }
724    
725    
726    
727     /*
728     *
729     * private functions and templates for access to column values
730     *
731     *
732     */
733    
734     private bit _checkTarget(int col, int dType)
735     {
736         return checkTarget(dType, cspecs[col].numericType, cbindings[col].targetType ? cbindings[col].targetType : defaultTarget(cspecs[col].numericType));
737     }
738     private void _checkTargetErr(int col, int dType)
739     {
740         SQLINTEGER targetType = cbindings[col].targetType ? cbindings[col].targetType : defaultTarget(cspecs[col].numericType);
741        
742         if(!checkTarget(dType, cspecs[col].numericType, targetType)) {
743            
744             char[] errmsg = "Incompatible value function for column " ~ .toString(col) ~ ". ";
745             char[] oktypes;
746            
747             int type = cspecs[col].numericType;
748            
749             if(checkTarget(D_LONG, type, targetType))
750                 oktypes ~= " result.vlong.";
751                
752             if(checkTarget(D_INT, type, targetType))
753                 oktypes ~= " result.vint.";
754                
755             if(checkTarget(D_SHORT, type, targetType))
756                 oktypes ~= " result.vshort.";
757                
758             if(checkTarget(D_DOUBLE, type, targetType))
759                 oktypes ~= " result.vdouble.";
760                
761             if(checkTarget(D_FLOAT, type, targetType))
762                 oktypes ~= " result.vfloat.";
763                
764             if(checkTarget(D_CHAR, type, targetType))
765                 oktypes ~= " result.string.";
766                
767             if(checkTarget(D_WCHAR, type, targetType))
768                 oktypes ~= " result.wstring.";
769                
770             if(oktypes.length == 0)
771                 errmsg ~= "Target type not supported. Use rawBuffer to retrieve this column.";
772             else
773                 errmsg ~= "Compatible value function is" ~ oktypes;
774            
775             throw new Exception(errmsg);
776         }
777     }
778
779
780     private template value(int D_TYPE, RET_TYPE)
781     {
782         RET_TYPE val(uint col)
783         in {
784             assert(col < cbindings.length);
785             assert(cbindings[col].binding.bound);
786            
787             _checkTargetErr(col, D_TYPE);
788         }
789         body {
790             return *(cast(RET_TYPE*) (cbindings[col].binding.buffer));
791         }
792         RET_TYPE val(char[] column)
793         in {
794             assert(column in cIndex);
795             assert(cbindings[cIndex[column]].binding.bound);
796             _checkTargetErr(cIndex[column], D_TYPE);
797         }
798         body {
799             return *(cast(RET_TYPE*) (cbindings[cIndex[column]].binding.buffer));
800         }
801     }
802    
803     private template value_char(int D_TYPE, RET_TYPE)
804     {
805         RET_TYPE[] val(uint col)
806         in {
807             assert(col < cbindings.length);
808             assert(cbindings[col].binding.bound);
809             _checkTargetErr(col, D_TYPE);
810         }
811         body {
812            
813             if(*cbindings[col].binding.StrLen_or_IndPtr == SQL_NULL_DATA || *cbindings[col].binding.StrLen_or_IndPtr == 0)
814                 return "";
815            
816             void[] retval;
817             retval.length = *cbindings[col].binding.StrLen_or_IndPtr;
818             retval[0..length] = ((cast(void*)cbindings[col].binding.buffer)[0..*cbindings[col].binding.StrLen_or_IndPtr]);
819             return cast(RET_TYPE[]) retval;
820         }
821         RET_TYPE[] val(char[] column)
822         in {
823             assert(column in cIndex);
824            
825         }
826         body {
827             return val(cIndex[column]);
828         }
829     }
830    
831     /*
832      *
833      * getData for unbound columns
834      *
835      *
836      */
837
838     private template getData(RET_TYPE, int SQL_C_TYPE)
839     {
840         RET_TYPE get(char[] column)
841         in {
842             assert(column in cIndex);
843         }
844         body {
845             return get(cIndex[column]);
846         }
847        
848         RET_TYPE get(int col)
849         in {
850             assert(fetchCount > 0);
851             assert(col < cbindings.length);
852             assert(!cbindings[col].binding.bound);
853         }
854         body {
855            
856             RET_TYPE targetValue;
857             SQLINTEGER StrLen_or_Ind;
858            
859             checkRetcode(SQLGetData(
860                 hstmt,
861                 col + 1,
862                 SQL_C_TYPE,
863                 &targetValue,
864                 0,
865                 &StrLen_or_Ind
866                 ), "SQLGetData");
867            
868             return targetValue;
869         }
870     }
871        
872    
873     private template getData_char(RET_TYPE, int SQL_C_TYPE)
874     {
875         RET_TYPE[] get(char[] column)
876         in {
877             assert(column in cIndex);
878         }
879         body {
880             return get(cIndex[column]);
881         }
882        
883         RET_TYPE[] get(int col)
884         in {
885             assert(fetchCount > 0);
886             assert(col < cbindings.length);
887             assert(!cbindings[col].binding.bound);
888         }
889         body {
890            
891             void[] targetValue;
892            
893             targetValue.length = RET_TYPE.sizeof * (cspecs[col].csize + 1);
894            
895             SQLINTEGER StrLen_or_Ind;
896            
897             void[] buffer = targetValue[0..length];
898            
899             while(checkRetcode(SQLGetData(
900                         hstmt,
901                         col + 1,
902                         SQL_C_TYPE,
903                         buffer,
904                         buffer.length,
905                         &StrLen_or_Ind
906                         ), "SQLGetData") == SQL_SUCCESS_WITH_INFO) {
907            
908                
909                 writefln(lastMessage.toString());
910                 assert(0); /* waiting for this to happen... */
911                
912                 uint tmp = targetValue.length;
913                 targetValue.length = StrLen_or_Ind;
914                 buffer = targetValue[tmp..length];
915                            
916             }
917            
918             RET_TYPE[] retval;
919             retval.length = StrLen_or_Ind / RET_TYPE.sizeof;
920            
921             (cast(void[])retval)[0..length] = targetValue[0..StrLen_or_Ind];
922            
923             return retval;
924         }
925     }
926    
927     private template values(int D_TYPE, RET_TYPE)
928     {
929      
930         public RET_TYPE[int] cvals()
931         {
932             assert(cbindings.length > 0);
933            
934             RET_TYPE[int] val;
935            
936             for(int col = 0; col < cbindings.length; col++){
937                 /* ColumnBinding* cb = cbindings[col]; */
938                 if(_checkTarget(col, D_TYPE)) {
939                    
940                     val[col] = value!(D_TYPE, RET_TYPE).val(col);
941                 }
942             }
943            
944             return val;
945         }
946         public RET_TYPE[char[]] fvals()
947         {
948             assert(cbindings.length > 0);
949            
950             RET_TYPE[char[]] val;
951             for(int col = 0; col < cbindings.length; col++){
952                 ColumnBinding* cb = cbindings[col];
953                 if(_checkTarget(col, D_TYPE)) {
954                    
955                     val[cspecs[col].name] = value!(D_TYPE, RET_TYPE).val(col);
956                 }
957             }
958            
959             return val;
960         }
961     }
962     private template values_char(int D_TYPE, RET_TYPE)
963     {
964      
965         public RET_TYPE[][int] cvals()
966         {
967             assert(cbindings.length > 0);
968            
969             RET_TYPE[][int] val;
970            
971             for(int col = 0; col < cbindings.length; col++){
972                 ColumnBinding* cb = cbindings[col];
973                 if(_checkTarget(col, D_TYPE)) {
974                    
975                     val[col] = value_char!(D_TYPE, RET_TYPE).val(col);
976                 }
977             }
978            
979             return val;
980         }
981         public RET_TYPE[][char[]] fvals()
982         {
983             assert(cbindings.length > 0);
984            
985             RET_TYPE[][char[]] val;
986             for(int col = 0; col < cbindings.length; col++){
987                 /* ColumnBinding* cb = cbindings[col]; */
988                 if(_checkTarget(col, D_TYPE)) {
989                    
990                     val[cspecs[col].name] = value_char!(D_TYPE, RET_TYPE).val(col);
991                 }
992             }
993            
994             return val;
995         }
996     }
997
998
999     /*
1000      *
1001      *
1002      * public access to column values
1003      *
1004      *
1005      */
1006    
1007    
1008    
1009     /*! \brief Returns true if the column contains a NULL value.
1010      */
1011
1012      public bit isNull(uint col)
1013     {
1014         assert(fetchCount > 0);
1015         assert(col < cbindings.length);
1016         assert(cbindings[col].binding.bound);
1017        
1018         return *cbindings[col].binding.StrLen_or_IndPtr == SQL_NULL_DATA;
1019     }
1020
1021     /*! \brief Returns true if the column contains a NULL value.
1022      */
1023         public bit isNull(char[] column)
1024     {
1025         assert(fetchCount > 0);
1026         assert(column in cIndex);
1027         assert(cbindings[cIndex[column]].binding.bound);
1028        
1029         return *cbindings[cIndex[column]].binding.StrLen_or_IndPtr == SQL_NULL_DATA;
1030     }
1031
1032
1033     /*
1034      *
1035      * Retrieving values from bound columns
1036      *
1037      *
1038      */
1039
1040      
1041
1042                    
1043    
1044     /*! \brief char[] string(int col); char[] string(char[] column);
1045      *
1046      * Retrieves a column value as a string.
1047      *
1048      * The value functions attempt to retrieve the buffer for a column
1049      * into each function's return type.
1050      *
1051      * In contract:
1052      *
1053      * If the c target type is incompatible, an Exception is thrown.
1054      *
1055      * See d type to sql c type mapping under setBinding()
1056      *
1057      *
1058      */
1059      
1060     public alias value_char!(D_CHAR, char).val string;
1061    
1062
1063     /*! \brief wchar[] wstring(int col); wchar[] wstring(char[] column);
1064      */
1065     public alias value_char!(D_WCHAR, wchar).val wstring;
1066
1067
1068     /*! \brief long vlong(int col); long vlong(char[] column);
1069      */
1070     public alias value!(D_LONG, long).val vlong;
1071
1072    
1073     /*! \brief int vint(int col); int vint(char[] column);
1074      */
1075     public alias value!(D_INT, int).val vint;
1076    
1077     /*! \brief short vshort(int col); short vshort(char[] column);
1078      */
1079     public alias value!(D_SHORT, short).val vshort;
1080
1081     /*! \brief double vdouble(int col); double vdouble(char[] column);
1082      */
1083     public alias value!(D_DOUBLE, double).val vdouble;
1084
1085     /*! \brief float vfloat(int col); float vfloat(char[] column);
1086      */
1087     public alias value!(D_FLOAT, float).val vfloat;
1088
1089    
1090     /*! \brief Returns a null terminated c string from the buffer
1091      */
1092     public char* stringz(int col)
1093     {
1094         return .toStringz(string(col));
1095     }
1096    
1097     /*! \brief Returns a null terminated c string from the buffer
1098      */
1099     public char* stringz(char[] column)
1100     {
1101         return .toStringz(string(column));
1102     }
1103    
1104
1105
1106     /*! \brief Returns a pointer to the buffer bound to a column.
1107      *
1108      *
1109      * SQLPOINTER TargetValuePtr points to the raw buffer. The buffer is not copied.
1110      * SQLINTEGER* StrLen_or_IndPtr points to the string length or indication buffer
1111      *
1112      * The buffer pointers remain valid after a new fetch, containing the new values.
1113      *
1114      * \warning
1115      * The buffer and the integer are malloc'd and will be free'd by the result object's destructor
1116      * or after a call to setBinding for this column.
1117      *
1118      *
1119      */
1120
1121      public void rawBuffer(in int col, out SQLPOINTER TargetValuePtr, out SQLINTEGER* StrLen_or_IndPtr)
1122     {
1123         assert(fetchCount > 0);
1124         assert(col < cbindings.length);
1125        
1126         TargetValuePtr = cbindings[col].binding.buffer;
1127         StrLen_or_IndPtr = cbindings[col].binding.StrLen_or_IndPtr;
1128     }
1129      
1130      
1131      
1132      
1133      
1134     /*! \brief char[][int] strings() - Returns an indexed associative array from the current row.
1135      *
1136      * Returns an associative array of all compatible columns of the current row.
1137      * The index is the column index. Missing indexes are of incompatible type.
1138      *
1139      */
1140      public alias values_char!(D_CHAR, char).cvals strings;
1141
1142     /*! \brief char[][char[]] stringsN() - Returns a named associative array from the current row.
1143      *
1144      * Returns an associative array of all compatible columns of the current row.
1145      * The index is the column name. Missing column names are of incompatible type.
1146      *
1147      */
1148      public alias values_char!(D_CHAR, char).fvals stringsN;
1149
1150      
1151      
1152     /*! \brief wchar[][int] wstrings(); wchar[][char[]] wstringsN()
1153      */
1154      public alias values_char!(D_WCHAR, wchar).cvals wstrings;
1155      public alias values_char!(D_WCHAR, wchar).fvals wstringsN;
1156
1157      
1158     /*! \brief long[int] vlongs(); long[char[]] vlongsN()
1159      */
1160      public alias values!(D_LONG, long).cvals vlongs;
1161      public alias values!(D_LONG, long).fvals vlongsN;
1162
1163
1164      
1165      /*! \brief int[int] vints(); int[char[]] vintsN()
1166      */
1167      public alias values!(D_INT, int).cvals vints;
1168      public alias values!(D_INT, int).fvals vintsN;
1169
1170
1171      
1172      /*! \brief short[int] vshorts(), short[char[]] vshortsN()
1173      */
1174      public alias values!(D_SHORT, short).cvals vshorts;
1175      public alias values!(D_SHORT, short).fvals vshortsN;
1176
1177      
1178      
1179      /*! \brief double[int] vdoubles(); double[char[]] vdoublesN()
1180      */
1181      public alias values!(D_DOUBLE, double).cvals vdoubles;
1182      public alias values!(D_DOUBLE, double).fvals vdoublesN;
1183      
1184      
1185      
1186      /*! \brief float[int] vfloats(), float[char[]] vfloatsN()
1187      */
1188      public alias values!(D_DOUBLE, float).cvals vfloats;
1189      public alias values!(D_DOUBLE, float).fvals vfloatsN;
1190
1191      
1192
1193      
1194     /*! \brief wstringData(int col); wstringData(char[] column)
1195      * Retrieves a wstring data value from an unbound column
1196     */
1197     public alias getData_char!(wchar, SQL_C_WCHAR).get wstringData;
1198        
1199     /*! \brief stringData(int col); stringData(char[] column)
1200     Retrieves a string data value from an unbound column
1201     */
1202     public alias getData_char!(char, SQL_C_CHAR).get stringData;
1203        
1204
1205     /*! \brief longData(int col); longData(char[] column)
1206     Retrieves a long data value from an unbound column
1207     */
1208     public alias getData!(long, SQL_C_LONG).get longData;
1209    
1210     /*! \brief intData(int col); intData(char[] column)
1211     Retrieves a int data value from an unbound column
1212     */
1213     public alias getData!(int, SQL_C_SHORT).get intData;
1214        
1215     /*! \brief shortData(int col); shortData(char[] column)
1216     Retrieves a short data value from an unbound column
1217     */
1218     public alias getData!(short, SQL_C_SHORT).get shortData;
1219        
1220    
1221    
1222     /*! \brief Closes the result. The result can be reexecuted with a new sql statement
1223     */
1224     public void close()
1225     {
1226        
1227         foreach(ColumnBinding* cb; cbindings) {
1228             if(cb.binding.allocated)
1229                 freeBinding(&cb.binding);
1230         }
1231        
1232         foreach(ParamBinding* pb; pbindings) {
1233             if(pb && pb.binding.allocated)
1234                 freeBinding(&pb.binding);
1235         }
1236        
1237         checkRetcode(SQLFreeStmt(
1238             hstmt,
1239             SQL_UNBIND
1240             ), "SQLFreeStmt");
1241        
1242         checkRetcode(SQLFreeStmt(
1243             hstmt,
1244             SQL_RESET_PARAMS
1245             ), "SQLFreeStmt");
1246        
1247        
1248         foreach(char[] key; cIndex.keys)
1249             delete cIndex[key];
1250        
1251         cbindings.length = 0;
1252         pbindings.length = 0;
1253         cspecs.length = 0;
1254         fetchCount = 0;
1255         bindAll = true;
1256        
1257         checkRetcode(SQLFreeStmt(
1258             hstmt,
1259             SQL_CLOSE
1260             ), "SQLFreeStmt");
1261
1262         prepared = false;
1263         executed = false;
1264         executedDirectly = false;
1265        
1266     }
1267    
1268     /*! \brief Deletes this object.
1269     */
1270     public void free()
1271     {
1272         delete this;
1273     }
1274    
1275    
1276    
1277     private void getColumnSpecs()
1278     {
1279        
1280         char[256] buffer;
1281        
1282         for(int i = 0; i < cspecs.length; i++) {
1283            
1284             short retLen = 0;
1285             cspecs[i] = new ColumnSpec();
1286             ColumnSpec* cs = cspecs[i];
1287            
1288             RETCODE rc = checkRetcode(SQLDescribeCol(
1289                 hstmt,
1290                 i + 1,
1291                 buffer,
1292                 buffer.length,
1293                 &retLen,
1294                 &(cs.numericType),
1295                 &(cs.csize),
1296                 &(cs.digits),
1297                 &(cs.nullable)
1298                 ), "SQLDescribeCol");
1299            
1300             cs.name.length = retLen;
1301             cs.name[0 .. length] = buffer[0 .. retLen];
1302                
1303             checkRetcode(SQLColAttribute (
1304                 hstmt,
1305                 i + 1,
1306                 SQL_DESC_TYPE_NAME,
1307                 buffer,
1308                 buffer.length,
1309                 &retLen,
1310                 null
1311                 ), "SQLColAttribute"); 
1312                
1313             cs.type.length = retLen;
1314             cs.type[0 .. length] = buffer[0 .. retLen];
1315
1316             checkRetcode(SQLColAttribute (
1317                 hstmt,
1318                 i + 1,
1319                 SQL_DESC_OCTET_LENGTH,
1320                 null,
1321                 0,
1322                 null,
1323                 &(cs.octetLength)
1324                 ), "SQLColAttribute"); 
1325                
1326             cIndex[cs.name] = i;
1327                        
1328             cbindings[i] = new ColumnBinding;
1329            
1330             //writefln("-- %s - %d %d %d %d ---- ", cs.name, cs.type, cs.csize, cs.digits, cs.nullable);
1331            
1332            
1333         }
1334     }
1335    
1336    
1337     private void bindColumns()
1338     {
1339        
1340         for(uint col = 0; col < cbindings.length; col++) {
1341             if(cbindings[col].targetType != UNBOUND || cbindings[col].binding.bound)
1342                 bindColumn(col);
1343         }
1344     }
1345    
1346     private void bindColumn(uint col) {
1347
1348         ColumnBinding* cb = cbindings[col];
1349         ColumnSpec* cs = cspecs[col];
1350        
1351         assert(cb.targetType != UNBOUND || cb.binding.bound);
1352        
1353         uint ptrSize;
1354        
1355         if(cb.targetType == UNBOUND) {
1356             ptrSize = 0;
1357             cb.binding.buffer = null;
1358         } else {
1359        
1360             ptrSize = cb.targetType == SQL_C_WCHAR ?
1361                     wchar.sizeof * cs.csize + 1
1362                     : (
1363                     cb.targetType == SQL_C_CHAR ?
1364                         cs.csize + 1
1365                         : cs.octetLength
1366                     );
1367        
1368                    
1369             if((cs.numericType == 93))
1370                 /* sql server datetime columns return timestamp (93) format with milliseconds.
1371                 those need more space. */
1372                 ptrSize += 8; /* room for sql server milliseconds */
1373         }
1374        
1375         //writefln("col %d targettype %d size %d", col, targetType, ptrSize);
1376        
1377         assert(cb.targetType == UNBOUND || ptrSize > 0);
1378        
1379         if(cb.binding.allocated) {
1380             if(cb.binding.bufLen < ptrSize)
1381                 reallocBinding(&cb.binding, ptrSize);
1382         } else {
1383             allocBinding(&cb.binding, ptrSize);
1384         }
1385            
1386         checkRetcode(SQLBindCol(
1387            hstmt,
1388            col + 1,
1389            cb.targetType,
1390            cb.binding.buffer,
1391            cb.binding.bufLen,
1392            cb.binding.StrLen_or_IndPtr
1393            ), "SQLBindCol");
1394            
1395         //writefln("bound %d targettype %d octet %d ptrsize %d", col, targetType, cs.octetLength, ptrSize);
1396
1397         cb.binding.bound = cb.targetType != UNBOUND;
1398        
1399     }
1400    
1401    
1402     private void allocBinding(Binding* b, uint bufLen)
1403     in {
1404         assert(!b.bound);
1405         assert(!b.allocated);
1406         assert(bufLen);
1407     }
1408     body {
1409         void* p = std.c.stdlib.malloc(bufLen);
1410        
1411         if(p == null) {
1412             throw new Exception("Out of memory");
1413             /* don't know if the gc has memory for that exception...
1414                if not, gc handles the error */
1415         }
1416        
1417         b.buffer = cast(SQLPOINTER) p;
1418        
1419         p = std.c.stdlib.malloc(b.StrLen_or_IndPtr.sizeof);
1420
1421         if(p == null) {
1422             std.c.stdlib.free(b.buffer);
1423             throw new Exception("Out of memory");
1424         }
1425        
1426         b.StrLen_or_IndPtr = cast(SQLINTEGER*) p;
1427         b.bufLen = bufLen;
1428        
1429         b.allocated = true;
1430     }
1431
1432     private void reallocBinding(Binding* b, uint bufLen)
1433     in {
1434         assert(b.allocated);
1435         assert(bufLen > b.bufLen);
1436     }
1437     body {
1438        
1439         void* p = std.c.stdlib.realloc(b.buffer, bufLen);
1440        
1441         if(p == null)
1442             throw new Exception("Out of memory"); /* probably there won't be memory for that exception? */
1443        
1444         b.buffer = cast(SQLPOINTER) p;
1445         b.bufLen = bufLen;
1446        
1447     }
1448
1449     private void freeBinding(Binding* b)
1450     in {
1451         assert(b.allocated);
1452     }
1453     body {
1454         std.c.stdlib.free(b.buffer);
1455         std.c.stdlib.free(b.StrLen_or_IndPtr);
1456         b.allocated = false;
1457     }
1458    
1459     private RETCODE checkRetcode(RETCODE rc, char[] msg)
1460     {
1461         return lastRetcode = SqlException.checkRetcode(rc, msg, hstmt, SQL_HANDLE_STMT, lastMessage);
1462     }
1463
1464    
1465 }
Note: See TracBrowser for help on using the browser.