Changeset 15 for trunk/sdbo

Show
Ignore:
Timestamp:
09/20/04 19:49:36 (8 years ago)
Author:
toaster
Message:

documentation changes

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/sdbo/_odbc_headers/sqltypes.d

    r14 r15  
    1616} else { 
    1717     
    18     typedef void *HANDLE; 
    19  
    20     /* 
    21      * Windows style typedefs for UNIX machines 
    22      * (from iODBC) 
    23      */ 
    24       
    25     alias byte BYTE; 
    26     alias ushort WORD; 
    27     alias ulong DWORD; 
    28      
    29     alias DWORD* LPDWORD; 
    30     alias char* LPSTR; 
    31     alias char* LPCSTR; 
    32  
    33     //  #if !defined(BOOL) && !defined(_OBJC_OBJC_H_) 
    34     typedef int BOOL; 
    35     // #endif 
    36      
     18    private extern(C) { 
     19        typedef void* HANDLE; 
     20     
     21        /* 
     22         * Windows style typedefs for UNIX machines 
     23         * (from iODBC) 
     24         */ 
     25          
     26        alias ubyte BYTE; 
     27        alias ushort WORD; 
     28        alias ulong DWORD; 
     29         
     30        alias DWORD* LPDWORD; 
     31        alias char* LPSTR; 
     32        alias char* LPCSTR; 
     33     
     34        alias int BOOL; 
     35    } 
    3736} 
    3837 
  • trunk/sdbo/odbc.d

    r14 r15  
    4343} 
    4444 
     45public alias sdbo.odbc_env.OdbcEnvironment   OdbcEnvironment; 
     46public alias sdbo.odbc_dbc.OdbcConnection    OdbcConnection; 
     47public alias sdbo.odbc_stmt.SqlResult        SqlResult; 
     48public alias sdbo.odbc_etc.ColumnSpec        ColumnSpec; 
     49public alias sdbo.odbc_etc.SqlException      SqlException; 
     50public alias sdbo.odbc_etc.OdbcMessage       OdbcMessage; 
     51 
     52 
     53/*! \brief Shortcut for faster typing 
     54*/ 
     55public alias OdbcEnvironment DBEnv; 
     56 
     57/*! \brief Shortcut for faster typing 
     58*/ 
     59public alias OdbcConnection  DBCon; 
     60 
    4561/*! \endif 
    4662*/ 
     
    6682 and you just want to connect to a database and execute query statements, without caring about 
    6783 odbc handles, data types, and API calls. 
     84  
     85 On the other hand, if you need to access the raw odbc api you can still use the classes to hide away 
     86 bulk operations: 
     87  
     88 <pre> 
     89 SqlResult r = (new OdbcConnection).connect("someserver").execDirect("select something from somewhere"); 
     90 HSTMT hstmt = r.handle(); 
     91 </pre> 
    6892  
    6993  
  • trunk/sdbo/odbc_env.d

    r14 r15  
    130130     
    131131    /*! \brief Sets up an environment for odbc version 3 (SQL_OV_ODBC3) 
     132     * 
     133     * Most applications will need only one environment to create all connections. 
     134     * 
     135     * 
    132136    */ 
    133137    public this() 
  • trunk/sdbo/odbc_stmt.d

    r14 r15  
    4242} 
    4343 
     44private import sdbo.odbc_env; 
    4445private import sdbo.odbc_dbc; 
    4546private import sdbo.odbc_etc; 
     
    5960{ 
    6061     
    61     private struct ColumnBinding 
     62    private struct Binding 
    6263    { 
    6364        bit allocated; 
    6465        bit bound; 
    6566 
     67        SQLPOINTER buffer; 
     68        SQLINTEGER bufLen; 
     69        SQLINTEGER* StrLen_or_IndPtr; /* this integer goes into the dll so it must be malloc'd */ 
     70    } 
     71     
     72    private struct ParamBinding 
     73    { 
     74         
     75        SQLINTEGER  valueType; 
     76        SQLINTEGER  paramType; 
     77         
     78        Binding binding; 
     79    } 
     80     
     81    private struct ColumnBinding 
     82    { 
    6683        SQLINTEGER targetType = UNBOUND; 
    67          
    68         SQLPOINTER ptr; 
    69         SQLINTEGER* bufLen; /* this integer goes into the dll so it must be malloc'd */ 
     84 
     85        Binding binding; 
    7086    } 
    7187     
     
    88104        switch(sqlType) { 
    89105             
    90             case SQL_BIGINT:           return SQL_C_SBIGINT; 
    91             case SQL_TYPE_DATE:            return SQL_C_CHAR; 
    92             case SQL_TYPE_TIME:            return SQL_C_CHAR; 
    93             case SQL_TYPE_TIMESTAMP:   return SQL_C_CHAR; 
    94             default: return SQL_C_DEFAULT; 
     106            case SQL_BIGINT:            return SQL_C_SBIGINT; /* odbc assigns char as default for this */ 
     107            case SQL_TYPE_DATE:         return SQL_C_CHAR; 
     108            case SQL_TYPE_TIME:         return SQL_C_CHAR; 
     109            case SQL_TYPE_TIMESTAMP:    return SQL_C_CHAR; 
     110            default:                    return SQL_C_DEFAULT; 
    95111        } 
    96112    } 
     
    108124            switch(dType) { 
    109125                 
    110                 case D_LONG:    return sqlType == SQL_BIGINT; 
    111126                case D_INT:     return sqlType == SQL_INTEGER; 
    112127                case D_SHORT:   return sqlType == SQL_SMALLINT; 
    113128                case D_DOUBLE:  return sqlType == SQL_DOUBLE || sqlType == SQL_FLOAT; 
    114129                case D_FLOAT:   return sqlType == SQL_REAL; 
    115                 case D_CHAR:    return sqlType == SQL_CHAR || sqlType == SQL_VARCHAR || sqlType == SQL_LONGVARCHAR || sqlType == SQL_NUMERIC; 
     130                case D_CHAR:    return sqlType == SQL_CHAR || sqlType == SQL_VARCHAR || sqlType == SQL_LONGVARCHAR || sqlType == SQL_NUMERIC || sqlType == SQL_BIGINT; 
     131                 
     132                case D_LONG:     
    116133                case D_WCHAR:    
    117134                default:        return false; 
     
    133150    private ColumnSpec*[] cspecs; 
    134151    private ColumnBinding*[] cbindings; 
     152    private ParamBinding*[] pbindings; 
    135153     
    136154    private uint[char[]] cIndex; 
     
    138156    private uint fetchCount = 0; 
    139157     
     158    private bit prepared = false; 
    140159    private bit executed = false; 
     160    private bit executedDirectly = false; 
     161     
    141162     
    142163    /*! \brief Diagnostics message from the last odbc call that returned SQL_SUCCESS_WITH_INFO 
     
    179200    public bit bindAll = true; 
    180201     
     202     
     203     
    181204    private this(OdbcConnection dbc) 
    182205    { 
     
    195218        this(dbc); 
    196219        execDirect(sql); 
    197         executed = true; 
     220        executedDirectly = true; 
    198221    } 
    199222         
     
    212235        } 
    213236         
    214         if(executed) 
    215             close(); 
    216          
    217         checkRetcode(SQLFreeHandle(SQL_HANDLE_STMT, hstmt), "SQLFreeHandle"); 
    218          
     237        try { 
     238             
     239            if(executedDirectly || executed || cbindings.length > 0 || pbindings.length > 0) 
     240                close(); 
     241             
     242            checkRetcode(SQLFreeHandle(SQL_HANDLE_STMT, hstmt), "SQLFreeHandle"); 
     243         
     244        } catch(SqlException e) {} 
     245 
    219246        //writefln("/result destructor"); 
    220247    } 
    221248     
    222249 
    223     /*! \brief The parent connection for this result. 
     250    /*! \brief The parent connections environment. 
     251     * 
     252     * shortcut for result.connection().environment() 
     253     */ 
     254     
     255    public OdbcEnvironment environment() 
     256    { 
     257        return dbc.environment(); 
     258    } 
     259     
     260    /*! \brief The parent connection. 
    224261     * 
    225262     */ 
     
    240277    } 
    241278     
    242     /*! \brief Executes a query for this result. 
     279     
     280    /*! \brief Prepares a SQL statement for later execution. 
     281     */ 
     282     
     283    public void prepare(char[] query) { 
     284     
     285        if(executed || executedDirectly) 
     286            close(); 
     287         
     288        checkRetcode(SQLPrepare( 
     289            hstmt, 
     290            cast(SQLCHAR*) toStringz(query), 
     291            query.length 
     292            ), "SQLPrepare"); 
     293    } 
     294     
     295     
     296    /*! \brief Sets an input parameter for a prepared statement 
     297     * 
     298     * Parameter index numbers start at 0 for the leftmost parameter. 
     299     */ 
     300    public void setInput(int num, long param, int paramType = SQL_BIGINT) { 
     301        setInputBuffer(num, D_LONG, &param, param.sizeof, paramType); 
     302    } 
     303     
     304    /*! \brief Sets an input parameter for a prepared statement 
     305     * 
     306     * Parameter index numbers start at 0 for the leftmost parameter. 
     307     */ 
     308    public void setInput(int num, int param, int paramType = SQL_INTEGER) { 
     309        setInputBuffer(num, D_INT, &param, param.sizeof, paramType); 
     310    } 
     311     
     312    /*! \brief Sets an input parameter for a prepared statement 
     313     * 
     314     * Parameter index numbers start at 0 for the leftmost parameter. 
     315     */ 
     316    public void setInput(int num, short param, int paramType = SQL_SMALLINT) { 
     317        setInputBuffer(num, D_SHORT, &param, param.sizeof, paramType); 
     318    } 
     319     
     320    /*! \brief Sets an input parameter for a prepared statement 
     321     * 
     322     * Parameter index numbers start at 0 for the leftmost parameter. 
     323     */ 
     324    public void setInput(int num, double param, int paramType = SQL_DOUBLE) { 
     325        setInputBuffer(num, D_DOUBLE, &param, param.sizeof, paramType); 
     326    } 
     327     
     328    /*! \brief Sets an input parameter for a prepared statement 
     329     * 
     330     * Parameter index numbers start at 0 for the leftmost parameter. 
     331     */ 
     332    public void setInput(int num, float param, int paramType = SQL_REAL) { 
     333        setInputBuffer(num, D_FLOAT, &param, param.sizeof, paramType); 
     334    } 
     335     
     336    /*! \brief Sets an input parameter for a prepared statement 
     337     * 
     338     * Parameter index numbers start at 0 for the leftmost parameter. 
     339     * 
     340     */ 
     341    public void setInput(int num, char[] param, int paramType = SQL_VARCHAR, int bufLen = 0) { 
     342        setInputBuffer(num, D_CHAR, cast(void*)param, param.length, paramType, bufLen); 
     343    } 
     344 
     345    /*! \brief Sets an input parameter for a prepared statement 
     346     * 
     347     * Parameter index numbers start at 0 for the leftmost parameter. 
     348     * 
     349     * This function is called setInputw, otherwise the compiler complains about 
     350     * ambiguous overload with setInput(... char[] ...) 
     351     *  
     352     */ 
     353    public void setInputw(int num, wchar[] param, int paramType = SQL_WVARCHAR, int bufLen = 0) { 
     354        setInputBuffer(num, D_WCHAR, cast(void*)param, wchar.sizeof * param.length, paramType, bufLen); 
     355    } 
     356     
     357     
     358    /*! \if never  
     359    */ 
     360     
     361    private void setInputBuffer(int num, int valueType, void* param, uint paramLen, SQLINTEGER paramType, uint minBufLen = 0) 
     362    in { 
     363        assert(minBufLen == 0 || minBufLen >= paramLen); 
     364         
     365    } 
     366    body { 
     367        /* sets the input parameter buffer for a column to the value submitted by the application. 
     368         * allocs, reallocs and binds the parameter as necessary. 
     369         */ 
     370         
     371        if(minBufLen == 0) minBufLen = paramLen; 
     372         
     373        if(pbindings.length < num + 1) { 
     374            pbindings.length = num + 1; 
     375        } 
     376 
     377        if(pbindings[num]) { 
     378         
     379            if(minBufLen > pbindings[num].binding.bufLen) { 
     380             
     381                pbindings[num].binding.bound = false; 
     382                reallocBinding(&pbindings[num].binding, minBufLen); 
     383             
     384            } else if(pbindings[num].binding.bound && ( 
     385                            pbindings[num].valueType != valueType  
     386                            || pbindings[num].paramType != paramType 
     387                            )) { 
     388                 
     389                pbindings[num].binding.bound = false; 
     390            } 
     391             
     392        } else { 
     393             
     394            pbindings[num] = new ParamBinding; 
     395            allocBinding(&pbindings[num].binding, minBufLen); 
     396        } 
     397         
     398        if(!pbindings[num].binding.bound) { 
     399            bindParameter(SQL_PARAM_INPUT, num, valueType, paramType); 
     400        } 
     401         
     402        pbindings[num].binding.buffer[0..paramLen] = param[0..paramLen]; 
     403        *pbindings[num].binding.StrLen_or_IndPtr = paramLen; 
     404         
     405    } 
     406     
     407    private void bindParameter(SQLSMALLINT iotype, int num, SQLSMALLINT valueType, SQLSMALLINT paramType) 
     408    in { 
     409        assert(num < pbindings.length); 
     410        assert(pbindings[num]); 
     411        assert(pbindings[num].binding.allocated); 
     412    } 
     413    body { 
     414         
     415        checkRetcode(SQLBindParameter( 
     416            hstmt, 
     417            num + 1, 
     418            iotype, 
     419            valueType, 
     420            paramType, 
     421            pbindings[num].binding.bufLen, 
     422            2, 
     423            pbindings[num].binding.buffer, 
     424            pbindings[num].binding.bufLen, 
     425            pbindings[num].binding.StrLen_or_IndPtr 
     426            ), "SQLBindParameter"); 
     427 
     428    } 
     429     
     430    /*! \endif 
     431    */ 
     432 
     433     
     434    /*! \brief Executes a prepared statement for this result. 
    243435     * 
    244436     * \warning 
     
    247439     * 
    248440     */ 
     441 
     442     public void execute() 
     443     { 
     444         checkRetcode(SQLExecute(hstmt), "SQLExecute"); 
     445         executed = true; 
     446     } 
     447     
     448 
     449    /*! \brief Executes a query for this result. 
     450     * 
     451     * \warning 
     452     * If the result was bound to a previous query, all resources related to that query will be freed 
     453     * For instance, a buffer retrieved with rawBuffer will be freed. 
     454     * 
     455     */ 
    249456     
    250457 
     
    252459    {    
    253460         
    254         if(executed) close(); 
     461        if(executedDirectly || executed || prepared) close(); 
    255462         
    256463        checkRetcode(SQLExecDirect(hstmt, toStringz(sql), SQL_NTS), "SQLEcexDirect"); 
    257         executed = true; 
     464        executedDirectly = true; 
    258465         
    259466        short ccount = 0; 
     
    357564    } 
    358565     
    359     public static const int UNBOUND = 0; 
    360      
    361     /* map c data types to d data types */ 
    362      
    363566    /*! \brief Sets the binding for a column 
    364567     * 
    365568     *   
    366569     * Valid parameters: 
    367      * 
    368      * - SqlResult.D_LONG 
    369      * - SqlResult.D_INT 
    370      * - SqlResult.D_SHORT 
    371      * 
    372      * - SqlResult.D_DOUBLE 
    373      * - SqlResult.D_FLOAT 
    374      * 
    375      * - SqlResult.D_WCHAR 
    376      * - SqlResult.D_CHAR 
    377      * 
    378      * - SqlResult.UNBOUND 
     570     <pre> 
     571         SqlResult.D_LONG 
     572         SqlResult.D_INT 
     573         SqlResult.D_SHORT 
     574          
     575         SqlResult.D_DOUBLE 
     576         SqlResult.D_FLOAT 
     577          
     578         SqlResult.D_WCHAR 
     579         SqlResult.D_CHAR 
     580          
     581         SqlResult.UNBOUND 
     582     </pre> 
     583     * 
     584     * This function sets the binding target type for a column or for all columns, 
     585     * but it doesn't actually bind the columns. 
     586     * The columns are bound on the first call to fetch(). 
     587     * 
    379588     * 
    380589     * Every D type is mapped to a predefined target C type. Some target types can be extracted 
    381590     * with the value functions. 
    382591     * 
    383      <pre> 
     592      
    384593        D_LONG =   SQL_C_SBIGINT   result.vlong(col) 
    385594        D_INT =    SQL_C_SLONG     result.vint(col) 
     
    389598        D_CHAR =   SQL_C_CHAR      result.string(col) result.stringz(col) 
    390599        D_WCHAR =  SQL_C_WCHAR     result.wstring(col) 
    391      </pre> 
     600      
    392601     * 
    393602     * 
     
    395604     * with the following exceptions: 
    396605     <pre> 
    397         SQL_BIGINT:         D_LONG (SQL_C_SBIGINT) 
     606        SQL_BIGINT:         D_LONG (SQL_C_SBIGINT) - odbc default is SQL_C_CHAR 
    398607        SQL_TYPE_DATE:      D_CHAR (SQL_C_CHAR) 
    399608        SQL_TYPE_TIME:      D_CHAR (SQL_C_CHAR) 
     
    401610     </pre> 
    402611      
    403      * SqlResult.UNBOUND causes the column not to be bound. If the column is bound it will be unbound imediately. 
    404      * 
    405      * 
    406      * 
     612     * SqlResult.UNBOUND causes the column not to be bound. 
     613     * \note 
     614     * If bindAll is true, all columns set to UNBOUND will be set to their default types on the first fetch. 
     615     * After the first fetch, a column set to UNBOUND stays unbound even if bindAll is true. 
     616     *  
     617     * If the column is already bound it will be unbound. 
    407618     * \warning 
    408      * Any previously allocated buffer will be free'd 
     619     * Allocated buffer for the column will be freed. 
     620     * 
    409621     * 
    410622     */ 
     
    445657                     
    446658                foreach(ColumnBinding* cb; cbindings) { 
    447                     cb.bound = false; 
    448                     if(cb.allocated) 
    449                         freeColumn(cb); 
    450                      
     659                    cb.binding.bound = false; 
     660                    if(cb.binding.allocated) 
     661                        freeBinding(&cb.binding); 
    451662                } 
    452663 
     
    573784        in { 
    574785            assert(col < cbindings.length); 
    575             assert(cbindings[col].bound); 
     786            assert(cbindings[col].binding.bound); 
    576787             
    577788            _checkTargetErr(col, D_TYPE); 
    578789        } 
    579790        body { 
    580             return *(cast(RET_TYPE*) (cbindings[col].ptr)); 
     791            return *(cast(RET_TYPE*) (cbindings[col].binding.buffer)); 
    581792        } 
    582793        RET_TYPE val(char[] column) 
    583794        in { 
    584795            assert(column in cIndex); 
    585             assert(cbindings[cIndex[column]].bound); 
    586              
    587              
     796            assert(cbindings[cIndex[column]].binding.bound); 
    588797            _checkTargetErr(cIndex[column], D_TYPE); 
    589798        } 
    590799        body { 
    591             return *(cast(RET_TYPE*) (cbindings[cIndex[column]].ptr)); 
     800            return *(cast(RET_TYPE*) (cbindings[cIndex[column]].binding.buffer)); 
    592801        } 
    593802    } 
     
    598807        in { 
    599808            assert(col < cbindings.length); 
    600             assert(cbindings[col].bound); 
     809            assert(cbindings[col].binding.bound); 
    601810            _checkTargetErr(col, D_TYPE); 
    602811        } 
    603812        body { 
    604813             
    605             if(*cbindings[col].bufLen == SQL_NULL_DATA || *cbindings[col].bufLen == 0)  
     814            if(*cbindings[col].binding.StrLen_or_IndPtr == SQL_NULL_DATA || *cbindings[col].binding.StrLen_or_IndPtr == 0)  
    606815                return ""; 
    607816             
    608817            void[] retval; 
    609             retval.length = *cbindings[col].bufLen
    610             retval[0..length] = ((cast(void*)cbindings[col].ptr)[0..*cbindings[col].bufLen]); 
     818            retval.length = *cbindings[col].binding.StrLen_or_IndPtr
     819            retval[0..length] = ((cast(void*)cbindings[col].binding.buffer)[0..*cbindings[col].binding.StrLen_or_IndPtr]); 
    611820            return cast(RET_TYPE[]) retval; 
    612821        } 
     
    642851            assert(fetchCount > 0); 
    643852            assert(col < cbindings.length); 
    644             assert(!cbindings[col].bound); 
     853            assert(!cbindings[col].binding.bound); 
    645854        }  
    646855        body { 
     
    677886            assert(fetchCount > 0); 
    678887            assert(col < cbindings.length); 
    679             assert(!cbindings[col].bound); 
     888            assert(!cbindings[col].binding.bound); 
    680889        }  
    681890        body { 
     
    727936             
    728937            for(int col = 0; col < cbindings.length; col++){ 
    729                 ColumnBinding* cb = cbindings[col]; 
     938                /* ColumnBinding* cb = cbindings[col]; */ 
    730939                if(_checkTarget(col, D_TYPE)) { 
    731940                     
     
    777986            RET_TYPE[][char[]] val; 
    778987            for(int col = 0; col < cbindings.length; col++){ 
    779                 ColumnBinding* cb = cbindings[col]; 
     988                /* ColumnBinding* cb = cbindings[col]; */ 
    780989                if(_checkTarget(col, D_TYPE)) { 
    781990                     
     
    8061015        assert(fetchCount > 0); 
    8071016        assert(col < cbindings.length); 
    808         assert(cbindings[col].bound); 
    809          
    810         return *cbindings[col].bufLen == SQL_NULL_DATA; 
     1017        assert(cbindings[col].binding.bound); 
     1018         
     1019        return *cbindings[col].binding.StrLen_or_IndPtr == SQL_NULL_DATA; 
    8111020    } 
    8121021 
     
    8171026        assert(fetchCount > 0); 
    8181027        assert(column in cIndex); 
    819         assert(cbindings[cIndex[column]].bound); 
    820          
    821         return *cbindings[cIndex[column]].bufLen == SQL_NULL_DATA; 
     1028        assert(cbindings[cIndex[column]].binding.bound); 
     1029         
     1030        return *cbindings[cIndex[column]].binding.StrLen_or_IndPtr == SQL_NULL_DATA; 
    8221031    } 
    8231032 
     
    9051114     * 
    9061115     * \warning 
    907      * The buffer and the integer are malloc'd and will be free'd by the result object's destructor. 
    908      *  
     1116     * The buffer and the integer are malloc'd and will be free'd by the result object's destructor 
     1117     * or after a call to setBinding for this column. 
    9091118     *  
    9101119     * 
     
    9161125        assert(col < cbindings.length); 
    9171126         
    918         TargetValuePtr = cbindings[col].ptr; 
    919         StrLen_or_IndPtr = cbindings[col].bufLen
     1127        TargetValuePtr = cbindings[col].binding.buffer; 
     1128        StrLen_or_IndPtr = cbindings[col].binding.StrLen_or_IndPtr
    9201129    } 
    9211130      
     
    10181227         
    10191228        foreach(ColumnBinding* cb; cbindings) { 
    1020             if(cb.allocated) 
    1021                 freeColumn(cb); 
     1229            if(cb.binding.allocated) 
     1230                freeBinding(&cb.binding); 
     1231        } 
     1232         
     1233        foreach(ParamBinding* pb; pbindings) { 
     1234            if(pb && pb.binding.allocated) 
     1235                freeBinding(&pb.binding); 
    10221236        } 
    10231237         
     
    10271241            ), "SQLFreeStmt"); 
    10281242         
     1243        checkRetcode(SQLFreeStmt( 
     1244            hstmt, 
     1245            SQL_RESET_PARAMS 
     1246            ), "SQLFreeStmt"); 
     1247         
    10291248         
    10301249        foreach(char[] key; cIndex.keys) 
     
    10321251         
    10331252        cbindings.length = 0; 
     1253        pbindings.length = 0; 
    10341254        cspecs.length = 0; 
    10351255        fetchCount = 0; 
     
    10411261            ), "SQLFreeStmt"); 
    10421262 
     1263        prepared = false; 
    10431264        executed = false; 
     1265        executedDirectly = false; 
    10441266         
    10451267    } 
     
    11181340         
    11191341        for(uint col = 0; col < cbindings.length; col++) { 
    1120             if(cbindings[col].targetType != UNBOUND || cbindings[col].bound)  
     1342            if(cbindings[col].targetType != UNBOUND || cbindings[col].binding.bound)  
    11211343                bindColumn(col); 
    11221344        } 
     
    11281350        ColumnSpec* cs = cspecs[col]; 
    11291351         
    1130         assert(cb.targetType != UNBOUND || cb.bound); 
     1352        assert(cb.targetType != UNBOUND || cb.binding.bound); 
    11311353         
    11321354        uint ptrSize; 
     
    11341356        if(cb.targetType == UNBOUND) { 
    11351357            ptrSize = 0; 
    1136             cb.ptr = null; 
     1358            cb.binding.buffer = null; 
    11371359        } else { 
    11381360         
     
    11541376        //writefln("col %d targettype %d size %d", col, targetType, ptrSize); 
    11551377         
    1156         if(cb.allocated) 
    1157             freeColumn(cb); 
    1158          
    11591378        assert(cb.targetType == UNBOUND || ptrSize > 0); 
    11601379         
    1161         allocColumn(cb, ptrSize); 
     1380        if(cb.binding.allocated) { 
     1381            if(cb.binding.bufLen < ptrSize) 
     1382                reallocBinding(&cb.binding, ptrSize); 
     1383        } else { 
     1384            allocBinding(&cb.binding, ptrSize); 
     1385        } 
    11621386             
    11631387        checkRetcode(SQLBindCol( 
     
    11651389           col + 1, 
    11661390           cb.targetType, 
    1167            cb.ptr, 
    1168            ptrSize
    1169            cb.bufLen 
     1391           cb.binding.buffer, 
     1392           cb.binding.bufLen
     1393           cb.binding.StrLen_or_IndPtr 
    11701394           ), "SQLBindCol");  
    11711395             
    11721396        //writefln("bound %d targettype %d octet %d ptrsize %d", col, targetType, cs.octetLength, ptrSize); 
    11731397 
    1174         cb.bound = cb.targetType != UNBOUND; 
     1398        cb.binding.bound = cb.targetType != UNBOUND; 
    11751399         
    11761400    } 
    11771401     
    11781402     
    1179      
    1180  
    1181     private void allocColumn(ColumnBinding* cb, uint size) 
    1182     { 
    1183          
    1184         void* p = std.c.stdlib.malloc(size); 
     1403    private void allocBinding(Binding* b, uint bufLen) 
     1404    in { 
     1405        assert(!b.bound); 
     1406        assert(!b.allocated); 
     1407        assert(bufLen); 
     1408    } 
     1409    body { 
     1410        void* p = std.c.stdlib.malloc(bufLen); 
     1411         
     1412        if(p == null) { 
     1413            throw new Exception("Out of memory");  
     1414            /* don't know if the gc has memory for that exception...  
     1415               if not, gc handles the error */ 
     1416        } 
     1417         
     1418        b.buffer = cast(SQLPOINTER) p; 
     1419         
     1420        p = std.c.stdlib.malloc(b.StrLen_or_IndPtr.sizeof); 
     1421 
     1422        if(p == null) { 
     1423            std.c.stdlib.free(b.buffer); 
     1424            throw new Exception("Out of memory"); 
     1425        } 
     1426         
     1427        b.StrLen_or_IndPtr = cast(SQLINTEGER*) p; 
     1428        b.bufLen = bufLen; 
     1429         
     1430        b.allocated = true; 
     1431    } 
     1432 
     1433    private void reallocBinding(Binding* b, uint bufLen) 
     1434    in { 
     1435        assert(b.allocated); 
     1436        assert(bufLen > b.bufLen); 
     1437    } 
     1438    body { 
     1439         
     1440        void* p = std.c.stdlib.realloc(b.buffer, bufLen); 
    11851441         
    11861442        if(p == null) 
    1187             throw new Exception("Out of memory"); 
    1188          
    1189         cb.ptr = cast(SQLPOINTER) p; 
    1190          
    1191         cb.allocated = true; 
    1192          
    1193         p = std.c.stdlib.malloc(cb.bufLen.sizeof); 
    1194  
    1195         if(p == null) 
    1196             throw new Exception("Out of memory"); 
    1197          
    1198         cb.bufLen = cast(SQLINTEGER*) p; 
    1199          
    1200          
    1201     } 
    1202      
    1203     private void freeColumn(ColumnBinding* cb) 
    1204     { 
    1205         std.c.stdlib.free(cb.ptr); 
    1206         std.c.stdlib.free(cb.bufLen); 
    1207         cb.allocated = false; 
     1443            throw new Exception("Out of memory"); /* probably there won't be memory for that exception? */ 
     1444         
     1445        b.buffer = cast(SQLPOINTER) p; 
     1446        b.bufLen = bufLen; 
     1447         
     1448    } 
     1449 
     1450    private void freeBinding(Binding* b) 
     1451    in { 
     1452        assert(b.allocated); 
     1453    } 
     1454    body { 
     1455        std.c.stdlib.free(b.buffer); 
     1456        std.c.stdlib.free(b.StrLen_or_IndPtr); 
     1457        b.allocated = false; 
    12081458    } 
    12091459