Changeset 95

Show
Ignore:
Timestamp:
10/28/08 19:16:04 (4 years ago)
Author:
larsivi
Message:

Initial commit for the next phase of DDBI development (I am really sorry that it has taken this long).

  • First revision of the new base interfaces (in dbi/model)
  • Implemented interfaces (95%) for mySQL - it is heavily tested, and since this commit does a lot, there may be some glitches
  • Support for other databases will be added (sqlite is next in line), although this commit clear out all of the old implementations. They can still be found in the tags/branches area of the repository.
  • I want feedback! In particular regarding how to fetch values not as strings
  • Is this an ideal minimal API that more complex functionality can be built on top of?
  • This code is only tested on 32 bit linux
Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/dbi/ErrorCode.d

    r70 r95  
    8181            return "Not a valid ErrorCode"; 
    8282    } 
    83     // Bugfix for DMD 0.162 
     83    // Return workaround 
    8484    return "Not a valid ErrorCode"; 
    8585} 
  • trunk/dbi/mysql/MysqlDatabase.d

    r89 r95  
    1 /** 
     1/** 
    22 * Authors: The D DBI project 
    33 * Copyright: BSD license 
    44 */ 
     5 
    56module dbi.mysql.MysqlDatabase; 
    67 
    7 version (dbi_mysql) { 
    8  
    9 private import tango.stdc.stringz : toDString = fromStringz, toCString = toStringz; 
    10 private import tango.io.Console; 
    11 private static import tango.text.Util; 
    12 private static import tango.text.convert.Integer; 
    13 debug(UnitTest) import tango.io.Stdout; 
    14  
    15 private import dbi.Database, dbi.DBIException, dbi.Result, dbi.Row, dbi.Statement, dbi.Registry; 
    16 version(Windows) { 
    17     private import dbi.mysql.imp_win; 
    18 
    19 else { 
    20     private import dbi.mysql.imp; 
    21 
    22 private import dbi.mysql.MysqlError, dbi.mysql.MysqlResult; 
    23  
    24 static this() { 
    25     uint ver = mysql_get_client_version(); 
    26     if(ver < 50000) { 
    27         throw new Exception("Unsupported MySQL client version.  Please compile using at least version 5.0 of the MySQL client libray."); 
    28     } 
    29     else if(ver < 50100) { 
    30         if(MYSQL_VERSION != 50000) { 
    31             throw new Exception("You are linking against version 5.0 of the MySQL client library but you have a build switch turned on for a different version (such as MySQL_51)."); 
    32         } 
    33     } 
    34     else { 
    35         if(MYSQL_VERSION != 50100) { 
    36             throw new Exception("You are linking against version 5.1 (or higher) of the MySQL client library so you need to use the build switch '-version=MySQL_51'."); 
    37         } 
    38     } 
    39 
    40  
    41 /** 
    42  * An implementation of Database for use with MySQL databases. 
    43  * 
    44  * Bugs: 
    45  *  Column types aren't retrieved. 
    46  * 
    47  * See_Also: 
    48  *  Database is the interface that this provides an implementation of. 
    49  */ 
    50 class MysqlDatabase : Database { 
    51     public: 
    52     /** 
    53      * Create a new instance of MysqlDatabase, but don't connect. 
    54      */ 
    55     this () { 
    56         connection = mysql_init(null); 
    57     } 
    58  
    59     /** 
    60      * Create a new instance of MysqlDatabase and connect to a server. 
    61      * 
    62      * See_Also: 
    63      *  connect 
    64      */ 
    65     this (char[] params, char[] username = null, char[] password = null) { 
    66         this(); 
    67         connect(params, username, password); 
    68     } 
    69  
    70     /** 
    71      * Connect to a database on a MySQL server. 
    72      * 
    73      * Params: 
    74      *  params = A string in the form "keyword1=value1;keyword2=value2;etc." 
    75      *  username = The _username to _connect with. 
    76      *  password = The _password to _connect with. 
    77      * 
    78      * Keywords: 
    79      *  dbname = The name of the database to use. 
    80      * 
    81      *  host = The host name of the database to _connect to. 
    82      * 
    83      *  port = The port number to _connect to. 
    84      * 
    85      *  sock = The socket to _connect to. 
    86      * 
    87      * Throws: 
    88      *  DBIException if there was an error connecting. 
    89      * 
    90      *  DBIException if port is provided but is not an integer. 
    91      * 
    92      * Examples: 
    93      *  --- 
    94      *  MysqlDatabase db = new MysqlDatabase(); 
    95      *  db.connect("host=localhost;dbname=test", "username", "password"); 
    96      *  --- 
    97      */ 
    98     override void connect (char[] params, char[] username = null, char[] password = null) { 
    99         char[] host = "localhost"; 
    100         char[] dbname = "test"; 
    101         char[] sock = null; 
    102         uint port = 0; 
    103  
    104         void parseKeywords () { 
    105             char[][char[]] keywords = getKeywords(params); 
    106             if ("host" in keywords) { 
    107                 host = keywords["host"]; 
    108             } 
    109             if ("dbname" in keywords) { 
    110                 dbname = keywords["dbname"]; 
    111             } 
    112             if ("sock" in keywords) { 
    113                 sock = keywords["sock"]; 
    114             } 
    115             if ("port" in keywords) { 
    116                 port = cast(uint)tango.text.convert.Integer.parse(keywords["port"]); 
    117             } 
    118         } 
    119         if (tango.text.Util.contains(params, '=')) { 
    120             parseKeywords(); 
    121         } else { 
    122             dbname = params; 
     8private import dbi.model.Database, 
     9               dbi.model.Result; 
     10 
     11private import dbi.Exception; 
     12 
     13private import dbi.mysql.MysqlError, 
     14               dbi.mysql.MysqlResult, 
     15               dbi.mysql.MysqlStatement; 
     16 
     17private import dbi.mysql.c.mysql; 
     18 
     19private import tango.util.log.Log, 
     20               tango.util.log.Config; 
     21private import tango.core.Variant; 
     22private import Integer = tango.text.convert.Integer; 
     23private import tango.text.convert.Format; 
     24 
     25private import tango.stdc.stringz; 
     26 
     27class MysqlDatabase : Database 
     28
     29 
     30private: 
     31 
     32    Logger log; 
     33 
     34package: 
     35    MYSQL* connection = null; 
     36 
     37public: 
     38 
     39    this () 
     40    { 
     41        connection = mysql_init(null); 
     42        log = Log.lookup(this.classinfo.toString); 
     43    } 
     44 
     45    this (char[] name, char[] user = null,  
     46          char[] password = null, 
     47          char[][char[]] params = null) 
     48    { 
     49        this(); 
     50        connect(name, user, password, params); 
     51    } 
     52 
     53    void connect (char[] name, char[] user = null,  
     54                  char[] password = null, 
     55                  char[][char[]] params = null) 
     56    { 
     57        char[] host = "localhost"; 
     58        char[] dbname = "test"; 
     59        char[] sock = null; 
     60        uint port = 0; 
     61 
     62        if ( connection is null ) 
     63            throw new Exception ("This database connection has been closed."); 
     64 
     65        if ("host" in params)  
     66            host = params["host"]; 
     67        if ("dbname" in params)  
     68            dbname = params["dbname"]; 
     69        if ("sock" in params)  
     70            sock = params["sock"]; 
     71        if ("port" in params) 
     72            port = cast(uint)Integer.parse(params["port"]); 
     73 
     74        // TODO: check docs - check for null? 
     75        mysql_real_connect(connection, toStringz(host), toStringz(user),  
     76                           toStringz(password), toStringz(name), port,  
     77                           toStringz(sock), 0); 
     78        if (uint error = mysql_errno(connection)) { 
     79            throw new DBIException("Unable to connect to the MySQL database.",  
     80                                   error, specificToGeneral(error)); 
    12381        } 
    124  
    125         mysql_real_connect(connection, toCString(host), toCString(username), toCString(password), toCString(dbname), port, toCString(sock), 0); 
    126         if (uint error = mysql_errno(connection)) { 
    127                 Cout("connect(): "); 
    128                 Cout(toDString(mysql_error(connection))); 
    129                 Cout("\n").flush;            
    130             throw new DBIException("Unable to connect to the MySQL database.", error, specificToGeneral(error)); 
    131         } 
    132     } 
    133  
    134     /** 
    135      * Close the current connection to the database. 
    136      * 
    137      * Throws: 
    138      *  DBIException if there was an error disconnecting. 
    139      */ 
    140     override void close () { 
    141         if (connection !is null) { 
     82    } 
     83 
     84    void close() 
     85    {        
     86        if (connection !is null) { 
    14287            mysql_close(connection); 
    14388            if (uint error = mysql_errno(connection)) { 
    144                         Cout("close(): "); 
    145                         Cout(toDString(mysql_error(connection))); 
    146                         Cout("\n").flush;            
    14789                throw new DBIException("Unable to close the MySQL database.", error, specificToGeneral(error)); 
    14890            } 
    14991            connection = null; 
    15092        } 
    151     } 
    152  
    153     /** 
    154      * Execute a SQL statement that returns no results. 
    155      * 
    156      * Params: 
    157      *  sql = The SQL statement to _execute. 
    158      * 
    159      * Throws: 
    160      *  DBIException if the SQL code couldn't be executed. 
    161      */ 
    162     override void execute (char[] sql) { 
    163         int error = mysql_real_query(connection, toCString(sql), sql.length); 
     93    } 
     94 
     95    void execute(char[] sql) 
     96    { 
     97        int error = mysql_real_query(connection, sql.ptr, sql.length); 
    16498        if (error) { 
    165                 Cout("execute(): "); 
    166                 Cout(toDString(mysql_error(connection))); 
    167                 Cout("\n").flush;            
    168                 throw new DBIException("Unable to execute a command on the MySQL database.", sql, error, specificToGeneral(error)); 
    169         } 
    170     } 
    171  
    172     /** 
    173      * Query the database. 
    174      * 
    175      * Bugs: 
    176      *  This does not currently check for errors. 
    177      * 
    178      * Params: 
    179      *  sql = The SQL statement to execute. 
    180      * 
    181      * Returns: 
    182      *  A Result object with the queried information. 
    183      */ 
    184     override MysqlResult query (char[] sql) { 
    185         mysql_real_query(connection, toCString(sql), sql.length); 
    186         MYSQL_RES* results = mysql_store_result(connection); 
    187         if (results is null) { 
    188                 Cout("query(): "); 
    189                 Cout(toDString(mysql_error(connection))); 
    190                 Cout("\n").flush; 
    191             throw new DBIException("Unable to query the MySQL database.", sql); 
    192         } 
    193         assert (results !is null); 
    194         return new MysqlResult(results); 
    195     } 
    196  
    197     /** 
    198      * Get the error code. 
    199      * 
    200      * Deprecated: 
    201      *  This functionality now exists in DBIException.  This will be 
    202      *  removed in version 0.3.0. 
    203      * 
    204      * Returns: 
    205      *  The database specific error code. 
    206      */ 
    207     deprecated override int getErrorCode () { 
    208             Cout("GetErrorCode: "); 
    209                 Cout(toDString(mysql_error(connection))); 
    210             Cout("\n").flush; 
    211         return cast(int)mysql_errno(connection); 
    212     } 
    213  
    214     /** 
    215      * Get the error message. 
    216      * 
    217      * Deprecated: 
    218      *  This functionality now exists in DBIException.  This will be 
    219      *  removed in version 0.3.0. 
    220      * 
    221      * Returns: 
    222      *  The database specific error message. 
    223      */ 
    224     deprecated override char[] getErrorMessage () { 
    225         return toDString(mysql_error(connection)); 
    226     } 
    227  
    228     /** 
    229      * Get the integer id of the last row to be inserted. 
    230      * 
    231      * Returns: 
    232      *  The id of the last row inserted into the database. 
    233      */ 
    234         override long getLastInsertID() { 
    235                 return mysql_insert_id(connection); 
     99           throw new DBIException("Unable to execute a command on the MySQL database.", sql, error, specificToGeneral(error)); 
     100        } 
     101    } 
     102 
     103    MysqlResult query(char[] sql) 
     104    { 
     105        execute(sql); 
     106        auto results = mysql_store_result(connection); 
     107 
     108        if (results is null) { 
     109            throw new DBIException("Unable to query the MySQL database using: " ~ sql); 
    236110        } 
    237          
    238     static this() 
    239     { 
    240         mysqlSqlGen = new MysqlSqlGenerator; 
    241     } 
    242     private static MysqlSqlGenerator mysqlSqlGen; 
    243          
    244     override SqlGenerator getSqlGenerator() 
    245     { 
    246         return mysqlSqlGen; 
    247     } 
    248  
    249     package: 
    250     MYSQL* connection; 
     111 
     112        return new MysqlResult(results); 
     113    } 
     114 
     115    MysqlStatement prepare(char[] sql) 
     116    { 
     117        // TODO: prepared query syntax standardized? Will a dbms agnostic 
     118        // frontend need to adjust it? 
     119        auto stmt = mysql_stmt_init(connection); 
     120        auto results = mysql_stmt_prepare(stmt, sql.ptr, sql.length); 
     121        if(results != 0) { 
     122            auto err = mysql_stmt_error(stmt); 
     123            auto errno = mysql_stmt_errno(stmt); 
     124            throw new DBIException("Unable to prepare statement: " ~ sql, errno,  
     125                                   specificToGeneral(errno), fromStringz(err)); 
     126        } 
     127        return new MysqlStatement(stmt); 
     128    } 
     129 
     130    MysqlTables tables(char[] filter = null) 
     131    { 
     132        auto results = mysql_list_tables(connection, toStringz(filter));  
     133        if(!results) { 
     134            log.error(fromStringz(mysql_error(connection))); 
     135            throw new DBIException("Table request returned null from MySQL"); 
     136        } 
     137     
     138        return new MysqlTables(this, new MysqlResult(results)); 
     139    } 
     140 
     141    bool hasTable(char[] name) 
     142    { 
     143        auto results = mysql_list_tables(connection, toStringz(name)); 
     144        if(!results) { 
     145            log.error(fromStringz(mysql_error(connection))); 
     146            throw new DBIException("Has Table request returned null from MySQL"); 
     147        } 
     148        bool exists = mysql_num_rows(results) > 0; 
     149        mysql_free_result(results); 
     150        return exists; 
     151    } 
     152 
     153    ulong lastInsertID() 
     154    { 
     155        return mysql_insert_id(connection); 
     156    } 
     157 
     158    void beginTransact() 
     159    { 
     160        const char[] sql = "START TRANSACTION;"; 
     161        mysql_real_query(connection, sql.ptr, sql.length); 
     162    } 
     163 
     164    void rollback() 
     165    { 
     166        mysql_rollback(connection); 
     167    } 
     168 
     169    void commit() 
     170    { 
     171        mysql_commit(connection); 
     172    } 
    251173} 
    252174 
    253 class MysqlSqlGenerator : SqlGenerator 
     175class MysqlTables : Tables 
    254176{ 
    255     override char getIdentifierQuoteCharacter() 
    256     { 
    257         return '`';  
    258     } 
     177private: 
     178 
     179    MysqlRows tablerows; 
     180    MysqlDatabase dbase; 
     181 
     182public: 
     183 
     184    this (MysqlDatabase dbase, MysqlResult results) 
     185    { 
     186        this.tablerows = new MysqlRows(results); 
     187        this.dbase = dbase; 
     188    } 
     189 
     190    int opApply (int delegate(inout Table) dg) 
     191    { 
     192        if (!tablerows.valid) 
     193            throw new DBIException("The underlying result set is no longer valid"); 
     194 
     195        int  result; 
     196        MysqlTable table = new MysqlTable(dbase); 
     197        foreach (Row tablerow; tablerows) {     
     198            table.set(tablerow.stringAt(0)); 
     199            Table t = table; 
     200            if ((result = dg(t)) != 0) 
     201                 break; 
     202        }  
     203        return result; 
     204    } 
     205 
     206    size_t tables() { return tablerows.rowCount; } 
    259207} 
    260208 
    261 private class MysqlRegister : Registerable { 
    262      
    263     public char[] getPrefix() { 
    264         return "mysql"; 
    265     } 
    266      
    267     public Database getInstance(char[] url) { 
    268         //parse the URL here 
    269         return new MysqlDatabase(); 
    270     } 
     209class MysqlTable : Table 
     210
     211private: 
     212 
     213    char[] _name = null; 
     214    MysqlDatabase dbase; 
     215    ulong _rowCount; 
     216    ColumnInfo[] _metadata = null; 
     217 
     218public: 
     219 
     220    this(MysqlDatabase dbase)  
     221    { 
     222        this.dbase = dbase; 
     223    } 
     224 
     225    this(MysqlDatabase dbase, char[] name) 
     226    { 
     227        this(dbase); 
     228        _name = name; 
     229    } 
     230 
     231    void set(char[] name) 
     232    in { 
     233        assert (name !is null); 
     234    } 
     235    body { 
     236        _name = name; 
     237    } 
     238 
     239    char[] name() {  
     240        return _name;  
     241    } 
     242 
     243    ColumnInfo[] metadata() 
     244    { 
     245        char[96] fieldquerybuf; 
     246        auto fieldquery = Format.sprint(fieldquerybuf, "SELECT * FROM {} LIMIT 1;", name); 
     247        fieldquerybuf[fieldquery.length] = 0; 
     248 
     249        mysql_real_query(dbase.connection, fieldquery.ptr, fieldquery.length); 
     250        auto results = mysql_store_result(dbase.connection); 
     251        auto fields = mysql_fetch_fields(results); 
     252 
     253        _metadata = new ColumnInfo[mysql_num_fields(results)]; 
     254        for (ulong i = 0; i < mysql_num_fields(results); i++) { 
     255            fromMysqlField(_metadata[i], fields[i]); 
     256        } 
     257 
     258        mysql_free_result(results); 
     259 
     260        return _metadata; 
     261    } 
     262 
     263    ColumnInfo metadata(size_t idx) 
     264    in { 
     265        if (_metadata !is null) 
     266            assert (idx > 0 && idx < _metadata.length); 
     267    } 
     268    body { 
     269        if (_metadata is null) 
     270            metadata(); 
     271        return _metadata[idx]; 
     272    } 
     273 
     274    ulong fieldCount()  
     275    {  
     276        if (_metadata is null) 
     277            metadata(); 
     278        return _metadata.length; 
     279    } 
     280 
     281    ulong rowCount() {  
     282        return _rowCount;  
     283    } 
     284 
     285    MysqlRows rows() 
     286    { 
     287        char[96] rowquerybuf; 
     288        auto rowquery = Format.sprint(rowquerybuf, "SELECT * FROM {};", name); 
     289        rowquerybuf[rowquery.length] = 0; 
     290 
     291        mysql_real_query(dbase.connection, rowquery.ptr, rowquery.length);  
     292        auto results = mysql_store_result(dbase.connection); 
     293 
     294        _rowCount = mysql_num_rows(results); 
     295        if (results is null) { 
     296            throw new DBIException("Unable to query the MySQL database for rows."); 
     297        } 
     298 
     299        return (new MysqlResult(results)).rows; 
     300    } 
     301 
     302    char[] toString() { return name; } 
    271303} 
    272304 
    273 static this() { 
    274     Cout("Attempting to register MysqlDatabase in Registry").newline; 
    275     registerDatabase(new MysqlRegister()); 
    276 } 
    277  
    278 debug(UnitTest) { 
    279 unittest { 
    280  
    281     void s1 (char[] s) { 
    282         tango.io.Stdout.Stdout(s).newline(); 
    283     } 
    284  
    285     void s2 (char[] s) { 
    286         tango.io.Stdout.Stdout("   ..." ~ s).newline(); 
    287     } 
    288  
    289     s1("dbi.mysql.MysqlDatabase:"); 
    290     MysqlDatabase db = new MysqlDatabase(); 
    291 /+  s2("connect"); 
    292     db.connect("dbname=test", "test", "test"); 
    293  
    294     s2("query"); 
    295     Result res = db.query("SELECT * FROM test"); 
    296     assert (res !is null); 
    297  
    298     s2("fetchRow"); 
    299     Row row = res.fetchRow(); 
    300     assert (row !is null); 
    301     assert (row.getFieldIndex("id") == 0); 
    302     assert (row.getFieldIndex("name") == 1); 
    303     assert (row.getFieldIndex("dateofbirth") == 2); 
    304     assert (row.get("id") == "1"); 
    305     assert (row.get("name") == "John Doe"); 
    306     assert (row.get("dateofbirth") == "1970-01-01"); 
    307     /** Todo: MySQL type retrieval is not functioning */ 
    308     //assert (row.getFieldType(1) == FIELD_TYPE_STRING); 
    309     //assert (row.getFieldDecl(1) == "char(40)"); 
    310     res.finish(); 
    311  
    312     s2("prepare"); 
    313     Statement stmt = db.prepare("SELECT * FROM test WHERE id = ?"); 
    314     stmt.bind(1, "1"); 
    315     res = stmt.query(); 
    316     row = res.fetchRow(); 
    317     res.finish(); 
    318     assert (row[0] == "1"); 
    319  
    320     s2("fetchOne"); 
    321     row = db.queryFetchOne("SELECT * FROM test"); 
    322     assert (row[0] == "1"); 
    323  
    324     s2("execute(INSERT)"); 
    325     db.execute("INSERT INTO test VALUES (2, 'Jane Doe', '2000-12-31')"); 
    326  
    327     s2("execute(DELETE via prepare statement)"); 
    328     stmt = db.prepare("DELETE FROM test WHERE id=?"); 
    329     stmt.bind(1, "2"); 
    330     stmt.execute(); 
    331  
    332     s2("close"); 
    333     db.close();+/ 
    334     auto sqlgen = db.getSqlGenerator; 
    335     auto res = sqlgen.makeInsertSql("user", ["name", "date"]); 
    336     assert(res == "INSERT INTO `user` (`name`,`date`) VALUES(?,?)", res); 
    337 } 
    338 } 
    339  
    340 } 
  • trunk/dbi/mysql/MysqlError.d

    r70 r95  
    88 
    99private import dbi.ErrorCode; 
     10 
     11package: 
    1012 
    1113/** 
     
    2123 *  Written against the MySQL 5.1 documentation (revision 2737) 
    2224 */ 
    23 package ErrorCode specificToGeneral (uint error) { 
     25 
     26ErrorCode specificToGeneral (uint error) { 
    2427    if (error > 999  && error < 2000) { 
    2528        return ErrorCode.ServerError; 
     
    141144            return ErrorCode.Unknown; 
    142145    } 
    143     // Bugfix for DMD 0.162 
    144    return ErrorCode.Unknown; 
     146    // Return workaround 
     147    return ErrorCode.Unknown; 
    145148} 
    146149 
  • trunk/dbi/mysql/MysqlResult.d

    r89 r95  
    33 * Copyright: BSD license 
    44 */ 
     5 
    56module dbi.mysql.MysqlResult; 
    67 
    7 version (dbi_mysql) { 
    8 private import tango.stdc.stringz : asString = fromStringz; 
    9 private import dbi.DBIException, dbi.Result, dbi.Row; 
    10 version(Windows) { 
    11     private import dbi.mysql.imp_win; 
    12 
    13 else { 
    14     private import dbi.mysql.imp; 
    15 
    16  
    17 /** 
    18  * Manage a result set from a MySQL database query. 
    19  * 
    20  * See_Also: 
    21  *  Result is the interface of which this provides an implementation. 
    22  */ 
    23 class MysqlResult : Result { 
    24     public: 
    25     this (MYSQL_RES* results) { 
    26         this.results = results; 
    27  
    28         fields = mysql_fetch_fields(results); 
    29         fieldCount = mysql_num_fields(results); 
    30     } 
    31  
    32     /** 
    33      * Get the next row from a result set. 
    34      * 
    35      * Returns: 
    36      *  A Row object with the queried information or null for an empty set. 
    37      */ 
    38     override Row fetchRow () { 
    39         MYSQL_ROW row = mysql_fetch_row(results); 
    40         uint* lengths = mysql_fetch_lengths(results); 
    41         if (row is null) { 
    42             return null; 
    43         } 
    44         assert (lengths !is null); 
    45         Row r = new Row(); 
    46         for (uint index = 0; index < fieldCount; index++) { 
    47             r.addField(asString(fields[index].name), row[index][0 .. lengths[index]], "", fields[index].type); 
    48         } 
    49         return r; 
    50     } 
    51  
    52     /** 
    53      * Free all database resources used by a result set. 
    54      */ 
    55     override void finish () { 
    56         if (results !is null) { 
    57             mysql_free_result(results); 
    58             results = null; 
    59         } 
    60     } 
    61  
    62     private: 
    63     MYSQL_RES* results; 
    64     const MYSQL_FIELD* fields; 
    65     const uint fieldCount; 
    66 
    67  
    68 
     8private import dbi.model.Result, 
     9               dbi.model.Types; 
     10 
     11private import dbi.mysql.c.mysql; 
     12 
     13private import dbi.mysql.MysqlDatabase; 
     14 
     15private import tango.core.Variant; 
     16private import tango.util.log.Log; 
     17 
     18debug private import tango.io.Stdout; 
     19 
     20class MysqlResult : FullResult 
     21
     22private: 
     23    MysqlRows _rows; 
     24    Alloc _alloc; 
     25    ColumnInfo[] _metadata; 
     26 
     27package: 
     28    MYSQL_RES* result = null; 
     29 
     30public: 
     31    this(MYSQL_RES* res) 
     32    in { 
     33        assert (res !is null); 
     34    } 
     35    body { 
     36        result = res; 
     37        _rows = null; 
     38    } 
     39 
     40    void set(MYSQL_RES* res) 
     41    { 
     42        if (result !is null) 
     43            close; 
     44 
     45        result = res; 
     46    } 
     47 
     48    MysqlRows rows() 
     49    { 
     50        if (_rows is null) 
     51            _rows = new MysqlRows(this); 
     52 
     53        return _rows; 
     54    } 
     55 
     56    ColumnInfo[] metadata() 
     57    { 
     58        auto fields = mysql_fetch_fields(result); 
     59 
     60        _metadata = new ColumnInfo[fieldCount]; 
     61        for (ulong i = 0; i < fieldCount; i++) { 
     62            fromMysqlField(_metadata[i], fields[i]); 
     63        } 
     64 
     65        return _metadata; 
     66    } 
     67 
     68    ColumnInfo metadata(size_t idx) 
     69    in { 
     70        if (_metadata !is null) 
     71            assert (idx > 0 && idx < _metadata.length); 
     72    } 
     73    body { 
     74        if (_metadata is null) 
     75            metadata(); 
     76        return _metadata[idx]; 
     77    } 
     78 
     79    ulong rowCount() { return mysql_num_rows(result); } 
     80    ulong fieldCount() { return mysql_num_fields(result); } 
     81 
     82    Alloc allocator() 
     83    { 
     84        return _alloc; 
     85    } 
     86 
     87    void allocator(Alloc alloc) 
     88    { 
     89        _alloc = alloc; 
     90    } 
     91 
     92    void close() 
     93    { 
     94        if (result is null) 
     95            throw new Exception ("This result set was already closed."); 
     96 
     97        mysql_free_result(result); 
     98        result = null; 
     99    } 
     100 
     101    bool valid() { return result !is null; } 
     102
     103 
     104class MysqlRows : Rows 
     105
     106private: 
     107 
     108    MysqlResult _rows; 
     109    ColumnInfo[] _metadata; 
     110    Alloc _alloc; 
     111 
     112    Logger log; 
     113 
     114public: 
     115 
     116    this (MysqlResult results) 
     117    { 
     118        _rows = results; 
     119        log = Log.lookup(this.classinfo.name); 
     120    } 
     121 
     122    ulong rowCount() 
     123    { 
     124        return _rows.rowCount; 
     125    } 
     126 
     127    ulong fieldCount() 
     128    { 
     129        return _rows.fieldCount; 
     130    } 
     131 
     132    ColumnInfo[] metadata() 
     133    { 
     134        auto fields = mysql_fetch_fields(_rows.result); 
     135 
     136        _metadata = new ColumnInfo[_rows.fieldCount]; 
     137        for (ulong i = 0; i < _rows.fieldCount; i++) { 
     138            fromMysqlField(_metadata[i], fields[i]); 
     139        } 
     140 
     141        return _metadata; 
     142    } 
     143 
     144    ColumnInfo metadata(size_t idx) 
     145    in { 
     146        if (_metadata !is null) 
     147            assert (idx > 0 && idx < _metadata.length); 
     148    } 
     149    body { 
     150        if (_metadata is null) 
     151            metadata(); 
     152        return _metadata[idx]; 
     153    } 
     154 
     155    int opApply (int delegate(inout Row) dg) 
     156    { 
     157        int result; 
     158        MysqlRow host = new MysqlRow;  
     159        MYSQL_ROW row; 
     160        assert (_rows.valid); 
     161        while ((row = mysql_fetch_row(_rows.result)) !is null) { 
     162            host.set(row, mysql_fetch_lengths(_rows.result)); 
     163            Row r = host; 
     164            if ((result = dg(r)) != 0) 
     165                break; 
     166        } 
     167        return result; 
     168    } 
     169 
     170    Alloc allocator() 
     171    { 
     172        return _alloc; 
     173    } 
     174 
     175    void allocator(Alloc alloc) 
     176    { 
     177        _alloc = alloc; 
     178    } 
     179 
     180    Row next() 
     181    { 
     182        return new MysqlRow(_rows); 
     183    } 
     184 
     185    bool fetch(Alloc alloc, void* ...) 
     186    { 
     187        // TODO 
     188        return false; 
     189    } 
     190 
     191    Row opIndex(ulong idx) 
     192    { 
     193        // TODO 
     194        return null; 
     195    } 
     196 
     197    void seek(ulong offset) 
     198    { 
     199        mysql_data_seek(_rows.result, offset); 
     200    } 
     201 
     202    bool valid() { return _rows.valid; } 
     203 
     204
     205 
     206class MysqlRow : Row 
     207
     208private: 
     209    MysqlResult _results = null; 
     210    MYSQL_ROW _row; 
     211    uint* _lengths = null; 
     212    private Logger log; 
     213 
     214private: 
     215 
     216    void initRow() { 
     217        _row = mysql_fetch_row(_results.result); 
     218        _lengths = mysql_fetch_lengths(_results.result); 
     219    } 
     220 
     221 
     222public: 
     223 
     224    this ()  
     225    { 
     226        log = Log.getLogger(this.classinfo.toString); 
     227    } 
     228 
     229    this (MysqlResult results) 
     230    in { 
     231        assert (results !is null); 
     232    } 
     233    body { 
     234        _results = results; 
     235        initRow; 
     236    } 
     237 
     238    this (MYSQL_ROW row, uint* lengths) 
     239    { 
     240        _row = row; 
     241        _lengths = lengths; 
     242    } 
     243 
     244 
     245    MysqlRow set(MYSQL_ROW row, uint* lengths) 
     246    { 
     247        _row = row; 
     248        _lengths = lengths; 
     249        return this; 
     250    } 
     251 
     252    MysqlRow set(MysqlResult results) { 
     253        _results = results; 
     254        initRow;  
     255         
     256        return this; 
     257    } 
     258 
     259    ColumnInfo[] metadata() 
     260    { 
     261        return _results.metadata(); 
     262    } 
     263 
     264    ColumnInfo metadata(size_t idx) 
     265    { 
     266        return _results.metadata(idx); 
     267    } 
     268 
     269    ulong fieldCount() 
     270    { 
     271        return _results.fieldCount; 
     272    } 
     273 
     274    char[] stringAt(size_t idx) 
     275    { 
     276        return _row[idx][0 .. _lengths[idx]]; 
     277    } 
     278 
     279    char[] stringAt(char[] name) 
     280    { 
     281        foreach (i, column; metadata)  
     282            if (name is column.name) 
     283                return _row[i][0 .. _lengths[i]]; 
     284 
     285        return null; 
     286    } 
     287 
     288    void fetch(inout char[][] values) 
     289    in { 
     290        assert (values.length >= fieldCount); 
     291    } 
     292    body { 
     293        for (int i = 0; i < fieldCount; i++) { 
     294            values[i] = _row[i][0 .. _lengths[i]]; 
     295        } 
     296    } 
     297 
     298    void fetch(inout char[] value, size_t idx = 0) 
     299    in { 
     300        assert(idx < fieldCount); 
     301    } 
     302    body { 
     303        value = _row[idx][0 .. _lengths[idx]]; 
     304    } 
     305
     306 
     307package: 
     308 
     309DbiType fromMysqlType(enum_field_types type) 
     310
     311    with (enum_field_types) { 
     312        switch (type) { 
     313            case MYSQL_TYPE_DECIMAL: 
     314                return DbiType.Decimal; 
     315            case MYSQL_TYPE_TINY: 
     316                return DbiType.Byte; 
     317            case MYSQL_TYPE_SHORT: 
     318                return DbiType.Short; 
     319            case MYSQL_TYPE_LONG: 
     320            case MYSQL_TYPE_ENUM: 
     321                return DbiType.Int; 
     322            case MYSQL_TYPE_FLOAT: 
     323                return DbiType.Float; 
     324            case MYSQL_TYPE_DOUBLE: 
     325                return DbiType.Double; 
     326            case MYSQL_TYPE_NULL: 
     327                return DbiType.Null; 
     328            case MYSQL_TYPE_TIMESTAMP: 
     329                 return DbiType.DateTime; 
     330            case MYSQL_TYPE_LONGLONG: 
     331                return DbiType.Long; 
     332            case MYSQL_TYPE_INT24: 
     333                 return DbiType.Int; 
     334            case MYSQL_TYPE_DATE: 
     335            case MYSQL_TYPE_TIME: 
     336            case MYSQL_TYPE_DATETIME: 
     337            case MYSQL_TYPE_YEAR: 
     338            case MYSQL_TYPE_NEWDATE: 
     339                return DbiType.DateTime; 
     340            case MYSQL_TYPE_BIT: 
     341                assert(false); 
     342            case MYSQL_TYPE_NEWDECIMAL: 
     343                return DbiType.Decimal; 
     344            case MYSQL_TYPE_SET: 
     345                assert(false); 
     346            case MYSQL_TYPE_TINY_BLOB: 
     347            case MYSQL_TYPE_MEDIUM_BLOB: 
     348            case MYSQL_TYPE_LONG_BLOB: 
     349            case MYSQL_TYPE_BLOB: 
     350                return DbiType.Binary; 
     351            case MYSQL_TYPE_VARCHAR: 
     352            case MYSQL_TYPE_VAR_STRING: 
     353            case MYSQL_TYPE_STRING: 
     354                return DbiType.String; 
     355            case MYSQL_TYPE_GEOMETRY: 
     356                assert(false); 
     357            default: 
     358                return DbiType.None; 
     359        } 
     360    } 
     361
     362 
     363void fromMysqlField(inout ColumnInfo column, MYSQL_FIELD field) 
     364
     365    column.name = field.name[0..field.name_length]; 
     366    column.name.length = field.name_length; 
     367    column.type = fromMysqlType(field.type); 
     368    column.flags = field.flags; 
     369
  • trunk/dbi/mysql/c/mysql.d

    • Property svn:mergeinfo set
    r81 r95  
    1 /** 
     1/** 
    22 * Authors: The D DBI project 
    33 * Copyright: BSD license 
    44 */ 
    5 module dbi.mysql.imp
     5module dbi.mysql.c.mysql
    66 
    77version (dbi_mysql) { 
    88 
    9 extern (C): 
     9extern (System): 
    1010 
    1111version (Windows) { 
    1212    pragma (lib, "libmysql.lib"); 
    13 } else version (linux) { 
    14     pragma (lib, "libmysql.a"); 
    15 } else version (Posix) { 
    16     pragma (lib, "libmysql.a"); 
    17 } else version (darwin) { 
    18     pragma (lib, "libmysql.a"); 
    19 } else { 
    20     pragma (msg, "You will need to manually link in the MySQL library."); 
    21 
     13}  
    2214 
    2315version(MySQL_51) { 
  • trunk/dsss.conf

    r87 r95  
    11name=dbi 
    2 exclude=buildme.d 
    32 
    43[testddbi.d] 
     
    65 
    76[dbi] 
     7version (dbi_mysql) { 
     8    buildflags += -L-lmysqlclient 
     9} 
    810target=dbi 
    911